Merge remote-tracking branches 'asoc/topic/ak4613', 'asoc/topic/core', 'asoc/topic/dmic' and 'asoc/topic/intel' into asoc-next
This commit is contained in:
commit
535b218a7f
@ -737,16 +737,17 @@ bool acpi_dev_found(const char *hid)
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_dev_found);
|
||||
|
||||
struct acpi_dev_present_info {
|
||||
struct acpi_dev_match_info {
|
||||
const char *dev_name;
|
||||
struct acpi_device_id hid[2];
|
||||
const char *uid;
|
||||
s64 hrv;
|
||||
};
|
||||
|
||||
static int acpi_dev_present_cb(struct device *dev, void *data)
|
||||
static int acpi_dev_match_cb(struct device *dev, void *data)
|
||||
{
|
||||
struct acpi_device *adev = to_acpi_device(dev);
|
||||
struct acpi_dev_present_info *match = data;
|
||||
struct acpi_dev_match_info *match = data;
|
||||
unsigned long long hrv;
|
||||
acpi_status status;
|
||||
|
||||
@ -757,6 +758,8 @@ static int acpi_dev_present_cb(struct device *dev, void *data)
|
||||
strcmp(adev->pnp.unique_id, match->uid)))
|
||||
return 0;
|
||||
|
||||
match->dev_name = acpi_dev_name(adev);
|
||||
|
||||
if (match->hrv == -1)
|
||||
return 1;
|
||||
|
||||
@ -789,20 +792,44 @@ static int acpi_dev_present_cb(struct device *dev, void *data)
|
||||
*/
|
||||
bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
|
||||
{
|
||||
struct acpi_dev_present_info match = {};
|
||||
struct acpi_dev_match_info match = {};
|
||||
struct device *dev;
|
||||
|
||||
strlcpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
|
||||
match.uid = uid;
|
||||
match.hrv = hrv;
|
||||
|
||||
dev = bus_find_device(&acpi_bus_type, NULL, &match,
|
||||
acpi_dev_present_cb);
|
||||
|
||||
dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
|
||||
return !!dev;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_dev_present);
|
||||
|
||||
/**
|
||||
* acpi_dev_get_first_match_name - Return name of first match of ACPI device
|
||||
* @hid: Hardware ID of the device.
|
||||
* @uid: Unique ID of the device, pass NULL to not check _UID
|
||||
* @hrv: Hardware Revision of the device, pass -1 to not check _HRV
|
||||
*
|
||||
* Return device name if a matching device was present
|
||||
* at the moment of invocation, or NULL otherwise.
|
||||
*
|
||||
* See additional information in acpi_dev_present() as well.
|
||||
*/
|
||||
const char *
|
||||
acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
|
||||
{
|
||||
struct acpi_dev_match_info match = {};
|
||||
struct device *dev;
|
||||
|
||||
strlcpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
|
||||
match.uid = uid;
|
||||
match.hrv = hrv;
|
||||
|
||||
dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
|
||||
return dev ? match.dev_name : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_dev_get_first_match_name);
|
||||
|
||||
/*
|
||||
* acpi_backlight= handling, this is done here rather then in video_detect.c
|
||||
* because __setup cannot be used in modules.
|
||||
|
@ -9,6 +9,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
@ -380,9 +381,16 @@ static void mrfld_irq_init_hw(struct mrfld_gpio *priv)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *mrfld_gpio_get_pinctrl_dev_name(void)
|
||||
{
|
||||
const char *dev_name = acpi_dev_get_first_match_name("INTC1002", NULL, -1);
|
||||
return dev_name ? dev_name : "pinctrl-merrifield";
|
||||
}
|
||||
|
||||
static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
const struct mrfld_gpio_pinrange *range;
|
||||
const char *pinctrl_dev_name;
|
||||
struct mrfld_gpio *priv;
|
||||
u32 gpio_base, irq_base;
|
||||
void __iomem *base;
|
||||
@ -439,10 +447,11 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
||||
return retval;
|
||||
}
|
||||
|
||||
pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name();
|
||||
for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
|
||||
range = &mrfld_gpio_ranges[i];
|
||||
retval = gpiochip_add_pin_range(&priv->chip,
|
||||
"pinctrl-merrifield",
|
||||
pinctrl_dev_name,
|
||||
range->gpio_base,
|
||||
range->pin_base,
|
||||
range->npins);
|
||||
|
@ -91,6 +91,9 @@ acpi_evaluate_dsm_typed(acpi_handle handle, const guid_t *guid, u64 rev,
|
||||
bool acpi_dev_found(const char *hid);
|
||||
bool acpi_dev_present(const char *hid, const char *uid, s64 hrv);
|
||||
|
||||
const char *
|
||||
acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#include <linux/proc_fs.h>
|
||||
|
@ -640,6 +640,12 @@ static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline const char *
|
||||
acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool is_acpi_node(struct fwnode_handle *fwnode)
|
||||
{
|
||||
return false;
|
||||
|
@ -27,17 +27,13 @@ struct snd_soc_acpi_package_context {
|
||||
bool data_valid;
|
||||
};
|
||||
|
||||
/* codec name is used in DAIs is i2c-<HID>:00 with HID being 8 chars */
|
||||
#define SND_ACPI_I2C_ID_LEN (4 + ACPI_ID_LEN + 3 + 1)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
/* translation fron HID to I2C name, needed for DAI codec_name */
|
||||
const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
|
||||
bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
|
||||
struct snd_soc_acpi_package_context *ctx);
|
||||
#else
|
||||
static inline const char *
|
||||
snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline bool
|
||||
snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
|
||||
struct snd_soc_acpi_package_context *ctx)
|
||||
|
@ -804,6 +804,9 @@ struct snd_soc_component_driver {
|
||||
int (*suspend)(struct snd_soc_component *);
|
||||
int (*resume)(struct snd_soc_component *);
|
||||
|
||||
unsigned int (*read)(struct snd_soc_component *, unsigned int);
|
||||
int (*write)(struct snd_soc_component *, unsigned int, unsigned int);
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_soc_pcm_runtime *);
|
||||
void (*pcm_free)(struct snd_pcm *);
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -95,6 +96,9 @@ struct ak4613_priv {
|
||||
struct mutex lock;
|
||||
const struct ak4613_interface *iface;
|
||||
struct snd_pcm_hw_constraint_list constraint;
|
||||
struct work_struct dummy_write_work;
|
||||
struct snd_soc_component *component;
|
||||
unsigned int rate;
|
||||
unsigned int sysclk;
|
||||
|
||||
unsigned int fmt;
|
||||
@ -392,6 +396,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->rate = rate;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
@ -467,11 +472,83 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ak4613_dummy_write(struct work_struct *work)
|
||||
{
|
||||
struct ak4613_priv *priv = container_of(work,
|
||||
struct ak4613_priv,
|
||||
dummy_write_work);
|
||||
struct snd_soc_component *component = priv->component;
|
||||
unsigned int mgmt1;
|
||||
unsigned int mgmt3;
|
||||
|
||||
/*
|
||||
* PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
|
||||
*
|
||||
* Note
|
||||
*
|
||||
* To avoid extra delay, we want to avoid preemption here,
|
||||
* but we can't. Because it uses I2C access which is using IRQ
|
||||
* and sleep. Thus, delay might be more than 5 LR clocks
|
||||
* see also
|
||||
* ak4613_dai_trigger()
|
||||
*/
|
||||
udelay(5000000 / priv->rate);
|
||||
|
||||
snd_soc_component_read(component, PW_MGMT1, &mgmt1);
|
||||
snd_soc_component_read(component, PW_MGMT3, &mgmt3);
|
||||
|
||||
snd_soc_component_write(component, PW_MGMT1, mgmt1);
|
||||
snd_soc_component_write(component, PW_MGMT3, mgmt3);
|
||||
}
|
||||
|
||||
static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
|
||||
* from Power Down Release. Otherwise, Playback volume will be 0dB.
|
||||
* To avoid complex multiple delay/dummy_write method from
|
||||
* ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...),
|
||||
* call it once here.
|
||||
*
|
||||
* But, unfortunately, we can't "write" here because here is atomic
|
||||
* context (It uses I2C access for writing).
|
||||
* Thus, use schedule_work() to switching to normal context
|
||||
* immediately.
|
||||
*
|
||||
* Note
|
||||
*
|
||||
* Calling ak4613_dummy_write() function might be delayed.
|
||||
* In such case, ak4613 volume might be temporarily 0dB when
|
||||
* beggining of playback.
|
||||
* see also
|
||||
* ak4613_dummy_write()
|
||||
*/
|
||||
|
||||
if ((cmd != SNDRV_PCM_TRIGGER_START) &&
|
||||
(cmd != SNDRV_PCM_TRIGGER_RESUME))
|
||||
return 0;
|
||||
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return 0;
|
||||
|
||||
priv->component = &codec->component;
|
||||
schedule_work(&priv->dummy_write_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops ak4613_dai_ops = {
|
||||
.startup = ak4613_dai_startup,
|
||||
.shutdown = ak4613_dai_shutdown,
|
||||
.set_sysclk = ak4613_dai_set_sysclk,
|
||||
.set_fmt = ak4613_dai_set_fmt,
|
||||
.trigger = ak4613_dai_trigger,
|
||||
.hw_params = ak4613_dai_hw_params,
|
||||
};
|
||||
|
||||
@ -590,6 +667,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
|
||||
priv->iface = NULL;
|
||||
priv->cnt = 0;
|
||||
priv->sysclk = 0;
|
||||
INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
|
@ -113,7 +113,7 @@ static int dmic_dev_probe(struct platform_device *pdev)
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans);
|
||||
if (err && (err != -ENOENT))
|
||||
if (err && (err != -EINVAL))
|
||||
return err;
|
||||
|
||||
if (!err) {
|
||||
|
@ -77,7 +77,6 @@ config SND_SST_ATOM_HIFI2_PLATFORM_PCI
|
||||
depends on X86 && PCI
|
||||
select SND_SST_IPC_PCI
|
||||
select SND_SOC_COMPRESS
|
||||
select SND_SOC_INTEL_COMMON
|
||||
help
|
||||
If you have a Intel Medfield or Merrifield/Edison platform, then
|
||||
enable this option by saying Y or m. Distros will typically not
|
||||
@ -98,6 +97,9 @@ config SND_SST_ATOM_HIFI2_PLATFORM
|
||||
codec, then enable this option by saying Y or m. This is a
|
||||
recommended option
|
||||
|
||||
config SND_SOC_INTEL_SKYLAKE_SSP_CLK
|
||||
tristate
|
||||
|
||||
config SND_SOC_INTEL_SKYLAKE
|
||||
tristate "SKL/BXT/KBL/GLK/CNL... Platforms"
|
||||
depends on PCI && ACPI
|
||||
|
@ -139,6 +139,7 @@ config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
|
||||
config SND_SOC_INTEL_BYT_CHT_ES8316_MACH
|
||||
tristate "Baytrail & Cherrytrail with ES8316 codec"
|
||||
depends on X86_INTEL_LPSS && I2C && ACPI
|
||||
select SND_SOC_ACPI
|
||||
select SND_SOC_ES8316
|
||||
help
|
||||
This adds support for ASoC machine driver for Intel(R) Baytrail &
|
||||
@ -234,6 +235,7 @@ config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH
|
||||
select SND_SOC_MAX98927
|
||||
select SND_SOC_DMIC
|
||||
select SND_SOC_HDAC_HDMI
|
||||
select SND_SOC_INTEL_SKYLAKE_SSP_CLK
|
||||
help
|
||||
This adds support for ASoC Onboard Codec I2S machine driver. This will
|
||||
create an alsa sound card for RT5663 + MAX98927.
|
||||
|
@ -219,7 +219,7 @@ static struct snd_soc_card bytcht_da7213_card = {
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
};
|
||||
|
||||
static char codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
|
||||
static char codec_name[SND_ACPI_I2C_ID_LEN];
|
||||
|
||||
static int bytcht_da7213_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -243,7 +243,7 @@ static int bytcht_da7213_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* fixup codec name based on HID */
|
||||
i2c_name = snd_soc_acpi_find_name_from_hid(mach->id);
|
||||
i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1);
|
||||
if (i2c_name) {
|
||||
snprintf(codec_name, sizeof(codec_name),
|
||||
"%s%s", "i2c-", i2c_name);
|
||||
|
@ -232,15 +232,39 @@ static struct snd_soc_card byt_cht_es8316_card = {
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static char codec_name[SND_ACPI_I2C_ID_LEN];
|
||||
|
||||
static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct byt_cht_es8316_private *priv;
|
||||
struct snd_soc_acpi_mach *mach;
|
||||
const char *i2c_name = NULL;
|
||||
int dai_index = 0;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mach = (&pdev->dev)->platform_data;
|
||||
/* fix index of codec dai */
|
||||
for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
|
||||
if (!strcmp(byt_cht_es8316_dais[i].codec_name,
|
||||
"i2c-ESSX8316:00")) {
|
||||
dai_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* fixup codec name based on HID */
|
||||
i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1);
|
||||
if (i2c_name) {
|
||||
snprintf(codec_name, sizeof(codec_name),
|
||||
"%s%s", "i2c-", i2c_name);
|
||||
byt_cht_es8316_dais[dai_index].codec_name = codec_name;
|
||||
}
|
||||
|
||||
/* register the soc card */
|
||||
byt_cht_es8316_card.dev = &pdev->dev;
|
||||
snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
|
||||
|
@ -713,7 +713,7 @@ static struct snd_soc_card byt_rt5640_card = {
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
|
||||
static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN];
|
||||
static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */
|
||||
static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */
|
||||
|
||||
@ -762,7 +762,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* fixup codec name based on HID */
|
||||
i2c_name = snd_soc_acpi_find_name_from_hid(mach->id);
|
||||
i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1);
|
||||
if (i2c_name) {
|
||||
snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
|
||||
"%s%s", "i2c-", i2c_name);
|
||||
|
@ -509,7 +509,7 @@ static struct snd_soc_card byt_rt5651_card = {
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static char byt_rt5651_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
|
||||
static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN];
|
||||
|
||||
static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -539,7 +539,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* fixup codec name based on HID */
|
||||
i2c_name = snd_soc_acpi_find_name_from_hid(mach->id);
|
||||
i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1);
|
||||
if (i2c_name) {
|
||||
snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name),
|
||||
"%s%s", "i2c-", i2c_name);
|
||||
|
@ -49,7 +49,7 @@ struct cht_acpi_card {
|
||||
struct cht_mc_private {
|
||||
struct snd_soc_jack jack;
|
||||
struct cht_acpi_card *acpi_card;
|
||||
char codec_name[16];
|
||||
char codec_name[SND_ACPI_I2C_ID_LEN];
|
||||
struct clk *mclk;
|
||||
};
|
||||
|
||||
@ -506,7 +506,7 @@ static struct cht_acpi_card snd_soc_cards[] = {
|
||||
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
|
||||
};
|
||||
|
||||
static char cht_rt5645_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
|
||||
static char cht_rt5645_codec_name[SND_ACPI_I2C_ID_LEN];
|
||||
static char cht_rt5645_codec_aif_name[12]; /* = "rt5645-aif[1|2]" */
|
||||
static char cht_rt5645_cpu_dai_name[10]; /* = "ssp[0|2]-port" */
|
||||
|
||||
@ -573,7 +573,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* fixup codec name based on HID */
|
||||
i2c_name = snd_soc_acpi_find_name_from_hid(mach->id);
|
||||
i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1);
|
||||
if (i2c_name) {
|
||||
snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name),
|
||||
"%s%s", "i2c-", i2c_name);
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
struct cht_mc_private {
|
||||
struct snd_soc_jack headset;
|
||||
char codec_name[16];
|
||||
char codec_name[SND_ACPI_I2C_ID_LEN];
|
||||
struct clk *mclk;
|
||||
};
|
||||
|
||||
@ -396,7 +396,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
|
||||
|
||||
/* fixup codec name based on HID */
|
||||
if (mach) {
|
||||
i2c_name = snd_soc_acpi_find_name_from_hid(mach->id);
|
||||
i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1);
|
||||
if (i2c_name) {
|
||||
snprintf(drv->codec_name, sizeof(drv->codec_name),
|
||||
"i2c-%s", i2c_name);
|
||||
|
@ -28,6 +28,9 @@
|
||||
#include "../../codecs/rt5663.h"
|
||||
#include "../../codecs/hdac_hdmi.h"
|
||||
#include "../skylake/skl.h"
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
#define KBL_REALTEK_CODEC_DAI "rt5663-aif"
|
||||
#define KBL_MAXIM_CODEC_DAI "max98927-aif1"
|
||||
@ -48,6 +51,8 @@ struct kbl_hdmi_pcm {
|
||||
struct kbl_rt5663_private {
|
||||
struct snd_soc_jack kabylake_headset;
|
||||
struct list_head hdmi_pcm_list;
|
||||
struct clk *mclk;
|
||||
struct clk *sclk;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -69,6 +74,61 @@ static const struct snd_kcontrol_new kabylake_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Right Spk"),
|
||||
};
|
||||
|
||||
static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct kbl_rt5663_private *priv = snd_soc_card_get_drvdata(card);
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* MCLK/SCLK need to be ON early for a successful synchronization of
|
||||
* codec internal clock. And the clocks are turned off during
|
||||
* POST_PMD after the stream is stopped.
|
||||
*/
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
/* Enable MCLK */
|
||||
ret = clk_set_rate(priv->mclk, 24000000);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "Can't set rate for mclk, err: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->mclk);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "Can't enable mclk, err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable SCLK */
|
||||
ret = clk_set_rate(priv->sclk, 3072000);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "Can't set rate for sclk, err: %d\n",
|
||||
ret);
|
||||
clk_disable_unprepare(priv->mclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->sclk);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "Can't enable sclk, err: %d\n", ret);
|
||||
clk_disable_unprepare(priv->mclk);
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
clk_disable_unprepare(priv->mclk);
|
||||
clk_disable_unprepare(priv->sclk);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget kabylake_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
@ -78,11 +138,14 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("HDMI1", NULL),
|
||||
SND_SOC_DAPM_SPK("HDMI2", NULL),
|
||||
SND_SOC_DAPM_SPK("HDMI3", NULL),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
|
||||
platform_clock_control, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route kabylake_map[] = {
|
||||
/* HP jack connectors - unknown if we have jack detection */
|
||||
{ "Headphone Jack", NULL, "Platform Clock" },
|
||||
{ "Headphone Jack", NULL, "HPOL" },
|
||||
{ "Headphone Jack", NULL, "HPOR" },
|
||||
|
||||
@ -91,6 +154,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = {
|
||||
{ "Right Spk", NULL, "Right BE_OUT" },
|
||||
|
||||
/* other jacks */
|
||||
{ "Headset Mic", NULL, "Platform Clock" },
|
||||
{ "IN1P", NULL, "Headset Mic" },
|
||||
{ "IN1N", NULL, "Headset Mic" },
|
||||
{ "DMic", NULL, "SoC DMIC" },
|
||||
@ -901,6 +965,7 @@ static int kabylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct kbl_rt5663_private *ctx;
|
||||
struct skl_machine_pdata *pdata;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
|
||||
if (!ctx)
|
||||
@ -919,6 +984,34 @@ static int kabylake_audio_probe(struct platform_device *pdev)
|
||||
dmic_constraints = pdata->dmic_num == 2 ?
|
||||
&constraints_dmic_2ch : &constraints_dmic_channels;
|
||||
|
||||
ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk");
|
||||
if (IS_ERR(ctx->mclk)) {
|
||||
ret = PTR_ERR(ctx->mclk);
|
||||
if (ret == -ENOENT) {
|
||||
dev_info(&pdev->dev,
|
||||
"Failed to get ssp1_sclk, defer probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
dev_err(&pdev->dev, "Failed to get ssp1_mclk with err:%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->sclk = devm_clk_get(&pdev->dev, "ssp1_sclk");
|
||||
if (IS_ERR(ctx->sclk)) {
|
||||
ret = PTR_ERR(ctx->sclk);
|
||||
if (ret == -ENOENT) {
|
||||
dev_info(&pdev->dev,
|
||||
"Failed to get ssp1_sclk, defer probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
dev_err(&pdev->dev, "Failed to get ssp1_sclk with err:%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card);
|
||||
}
|
||||
|
||||
|
@ -14,3 +14,8 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \
|
||||
skl-sst-utils.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
|
||||
|
||||
#Skylake Clock device support
|
||||
snd-soc-skl-ssp-clk-objs := skl-ssp-clk.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_SSP_CLK) += snd-soc-skl-ssp-clk.o
|
||||
|
@ -27,6 +27,12 @@
|
||||
#define SKL_SHIFT(x) (ffs(x) - 1)
|
||||
#define SKL_MCLK_DIV_RATIO_MASK GENMASK(11, 0)
|
||||
|
||||
#define is_legacy_blob(x) (x.signature != 0xEE)
|
||||
#define ext_to_legacy_blob(i2s_config_blob_ext) \
|
||||
((struct skl_i2s_config_blob_legacy *) i2s_config_blob_ext)
|
||||
|
||||
#define get_clk_src(mclk, mask) \
|
||||
((mclk.mdivctrl & mask) >> SKL_SHIFT(mask))
|
||||
struct skl_i2s_config {
|
||||
u32 ssc0;
|
||||
u32 ssc1;
|
||||
@ -45,6 +51,24 @@ struct skl_i2s_config_mclk {
|
||||
u32 mdivr;
|
||||
};
|
||||
|
||||
struct skl_i2s_config_mclk_ext {
|
||||
u32 mdivctrl;
|
||||
u32 mdivr_count;
|
||||
u32 mdivr[0];
|
||||
} __packed;
|
||||
|
||||
struct skl_i2s_config_blob_signature {
|
||||
u32 minor_ver : 8;
|
||||
u32 major_ver : 8;
|
||||
u32 resvdz : 8;
|
||||
u32 signature : 8;
|
||||
} __packed;
|
||||
|
||||
struct skl_i2s_config_blob_header {
|
||||
struct skl_i2s_config_blob_signature sig;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct skl_i2s_config_blob_legacy - Structure defines I2S Gateway
|
||||
* configuration legacy blob
|
||||
@ -61,4 +85,11 @@ struct skl_i2s_config_blob_legacy {
|
||||
struct skl_i2s_config_mclk mclk;
|
||||
};
|
||||
|
||||
struct skl_i2s_config_blob_ext {
|
||||
u32 gtw_attr;
|
||||
struct skl_i2s_config_blob_header hdr;
|
||||
u32 tdm_ts_group[SKL_I2S_MAX_TIME_SLOTS];
|
||||
struct skl_i2s_config i2s_cfg;
|
||||
struct skl_i2s_config_mclk_ext mclk;
|
||||
} __packed;
|
||||
#endif /* __SOUND_SOC_SKL_I2S_H */
|
||||
|
@ -675,6 +675,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
|
||||
kfree(dma_ctrl);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control);
|
||||
|
||||
static void skl_setup_out_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
|
@ -28,6 +28,7 @@ static guid_t osc_guid =
|
||||
GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
|
||||
0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
|
||||
|
||||
|
||||
struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
|
||||
{
|
||||
acpi_handle handle;
|
||||
@ -287,6 +288,7 @@ void skl_nhlt_remove_sysfs(struct skl *skl)
|
||||
static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
|
||||
struct nhlt_fmt *fmt, u8 id)
|
||||
{
|
||||
struct skl_i2s_config_blob_ext *i2s_config_ext;
|
||||
struct skl_i2s_config_blob_legacy *i2s_config;
|
||||
struct skl_clk_parent_src *parent;
|
||||
struct skl_ssp_clk *sclk, *sclkfs;
|
||||
@ -347,12 +349,18 @@ static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
|
||||
|
||||
/* Fill rate and parent for sclk/sclkfs */
|
||||
if (!present) {
|
||||
/* MCLK Divider Source Select */
|
||||
i2s_config = (struct skl_i2s_config_blob_legacy *)
|
||||
i2s_config_ext = (struct skl_i2s_config_blob_ext *)
|
||||
fmt->fmt_config[0].config.caps;
|
||||
clk_src = ((i2s_config->mclk.mdivctrl)
|
||||
& SKL_MNDSS_DIV_CLK_SRC_MASK) >>
|
||||
SKL_SHIFT(SKL_MNDSS_DIV_CLK_SRC_MASK);
|
||||
|
||||
/* MCLK Divider Source Select */
|
||||
if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
|
||||
i2s_config = ext_to_legacy_blob(i2s_config_ext);
|
||||
clk_src = get_clk_src(i2s_config->mclk,
|
||||
SKL_MNDSS_DIV_CLK_SRC_MASK);
|
||||
} else {
|
||||
clk_src = get_clk_src(i2s_config_ext->mclk,
|
||||
SKL_MNDSS_DIV_CLK_SRC_MASK);
|
||||
}
|
||||
|
||||
parent = skl_get_parent_clk(clk_src);
|
||||
|
||||
@ -378,6 +386,7 @@ static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
|
||||
static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk,
|
||||
struct nhlt_fmt *fmt, u8 id)
|
||||
{
|
||||
struct skl_i2s_config_blob_ext *i2s_config_ext;
|
||||
struct skl_i2s_config_blob_legacy *i2s_config;
|
||||
struct nhlt_specific_cfg *fmt_cfg;
|
||||
struct skl_clk_parent_src *parent;
|
||||
@ -385,13 +394,21 @@ static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk,
|
||||
u8 clk_src;
|
||||
|
||||
fmt_cfg = &fmt->fmt_config[0].config;
|
||||
i2s_config = (struct skl_i2s_config_blob_legacy *)fmt_cfg->caps;
|
||||
i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps;
|
||||
|
||||
/* MCLK Divider Source Select */
|
||||
clk_src = ((i2s_config->mclk.mdivctrl) & SKL_MCLK_DIV_CLK_SRC_MASK) >>
|
||||
SKL_SHIFT(SKL_MCLK_DIV_CLK_SRC_MASK);
|
||||
|
||||
clkdiv = i2s_config->mclk.mdivr & SKL_MCLK_DIV_RATIO_MASK;
|
||||
/* MCLK Divider Source Select and divider */
|
||||
if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
|
||||
i2s_config = ext_to_legacy_blob(i2s_config_ext);
|
||||
clk_src = get_clk_src(i2s_config->mclk,
|
||||
SKL_MCLK_DIV_CLK_SRC_MASK);
|
||||
clkdiv = i2s_config->mclk.mdivr &
|
||||
SKL_MCLK_DIV_RATIO_MASK;
|
||||
} else {
|
||||
clk_src = get_clk_src(i2s_config_ext->mclk,
|
||||
SKL_MCLK_DIV_CLK_SRC_MASK);
|
||||
clkdiv = i2s_config_ext->mclk.mdivr[0] &
|
||||
SKL_MCLK_DIV_RATIO_MASK;
|
||||
}
|
||||
|
||||
/* bypass divider */
|
||||
div_ratio = 1;
|
||||
|
429
sound/soc/intel/skylake/skl-ssp-clk.c
Normal file
429
sound/soc/intel/skylake/skl-ssp-clk.c
Normal file
@ -0,0 +1,429 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright(c) 2015-17 Intel Corporation
|
||||
|
||||
/*
|
||||
* skl-ssp-clk.c - ASoC skylake ssp clock driver
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include "skl.h"
|
||||
#include "skl-ssp-clk.h"
|
||||
#include "skl-topology.h"
|
||||
|
||||
#define to_skl_clk(_hw) container_of(_hw, struct skl_clk, hw)
|
||||
|
||||
struct skl_clk_parent {
|
||||
struct clk_hw *hw;
|
||||
struct clk_lookup *lookup;
|
||||
};
|
||||
|
||||
struct skl_clk {
|
||||
struct clk_hw hw;
|
||||
struct clk_lookup *lookup;
|
||||
unsigned long rate;
|
||||
struct skl_clk_pdata *pdata;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
struct skl_clk_data {
|
||||
struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
|
||||
struct skl_clk *clk[SKL_MAX_CLK_CNT];
|
||||
u8 avail_clk_cnt;
|
||||
};
|
||||
|
||||
static int skl_get_clk_type(u32 index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0 ... (SKL_SCLK_OFS - 1):
|
||||
return SKL_MCLK;
|
||||
|
||||
case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
|
||||
return SKL_SCLK;
|
||||
|
||||
case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
|
||||
return SKL_SCLK_FS;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int skl_get_vbus_id(u32 index, u8 clk_type)
|
||||
{
|
||||
switch (clk_type) {
|
||||
case SKL_MCLK:
|
||||
return index;
|
||||
|
||||
case SKL_SCLK:
|
||||
return index - SKL_SCLK_OFS;
|
||||
|
||||
case SKL_SCLK_FS:
|
||||
return index - SKL_SCLKFS_OFS;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
|
||||
{
|
||||
struct nhlt_fmt_cfg *fmt_cfg;
|
||||
union skl_clk_ctrl_ipc *ipc;
|
||||
struct wav_fmt *wfmt;
|
||||
|
||||
if (!rcfg)
|
||||
return;
|
||||
|
||||
ipc = &rcfg->dma_ctl_ipc;
|
||||
if (clk_type == SKL_SCLK_FS) {
|
||||
fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
|
||||
wfmt = &fmt_cfg->fmt_ext.fmt;
|
||||
|
||||
/* Remove TLV Header size */
|
||||
ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
|
||||
sizeof(struct skl_tlv_hdr);
|
||||
ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
|
||||
ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
|
||||
ipc->sclk_fs.valid_bit_depth =
|
||||
fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
|
||||
ipc->sclk_fs.number_of_channels = wfmt->channels;
|
||||
} else {
|
||||
ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
|
||||
/* Remove TLV Header size */
|
||||
ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
|
||||
sizeof(struct skl_tlv_hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sends dma control IPC to turn the clock ON/OFF */
|
||||
static int skl_send_clk_dma_control(struct skl *skl,
|
||||
struct skl_clk_rate_cfg_table *rcfg,
|
||||
u32 vbus_id, u8 clk_type,
|
||||
bool enable)
|
||||
{
|
||||
struct nhlt_specific_cfg *sp_cfg;
|
||||
u32 i2s_config_size, node_id = 0;
|
||||
struct nhlt_fmt_cfg *fmt_cfg;
|
||||
union skl_clk_ctrl_ipc *ipc;
|
||||
void *i2s_config = NULL;
|
||||
u8 *data, size;
|
||||
int ret;
|
||||
|
||||
if (!rcfg)
|
||||
return -EIO;
|
||||
|
||||
ipc = &rcfg->dma_ctl_ipc;
|
||||
fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
|
||||
sp_cfg = &fmt_cfg->config;
|
||||
|
||||
if (clk_type == SKL_SCLK_FS) {
|
||||
ipc->sclk_fs.hdr.type =
|
||||
enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
|
||||
data = (u8 *)&ipc->sclk_fs;
|
||||
size = sizeof(struct skl_dmactrl_sclkfs_cfg);
|
||||
} else {
|
||||
/* 1 to enable mclk, 0 to enable sclk */
|
||||
if (clk_type == SKL_SCLK)
|
||||
ipc->mclk.mclk = 0;
|
||||
else
|
||||
ipc->mclk.mclk = 1;
|
||||
|
||||
ipc->mclk.keep_running = enable;
|
||||
ipc->mclk.warm_up_over = enable;
|
||||
ipc->mclk.clk_stop_over = !enable;
|
||||
data = (u8 *)&ipc->mclk;
|
||||
size = sizeof(struct skl_dmactrl_mclk_cfg);
|
||||
}
|
||||
|
||||
i2s_config_size = sp_cfg->size + size;
|
||||
i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
|
||||
if (!i2s_config)
|
||||
return -ENOMEM;
|
||||
|
||||
/* copy blob */
|
||||
memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
|
||||
|
||||
/* copy additional dma controls information */
|
||||
memcpy(i2s_config + sp_cfg->size, data, size);
|
||||
|
||||
node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
|
||||
ret = skl_dsp_set_dma_control(skl->skl_sst, (u32 *)i2s_config,
|
||||
i2s_config_size, node_id);
|
||||
kfree(i2s_config);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
|
||||
struct skl_clk_rate_cfg_table *rcfg,
|
||||
unsigned long rate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
|
||||
if (rcfg[i].rate == rate)
|
||||
return &rcfg[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int skl_clk_change_status(struct skl_clk *clkdev,
|
||||
bool enable)
|
||||
{
|
||||
struct skl_clk_rate_cfg_table *rcfg;
|
||||
int vbus_id, clk_type;
|
||||
|
||||
clk_type = skl_get_clk_type(clkdev->id);
|
||||
if (clk_type < 0)
|
||||
return clk_type;
|
||||
|
||||
vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
|
||||
if (vbus_id < 0)
|
||||
return vbus_id;
|
||||
|
||||
rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
|
||||
clkdev->rate);
|
||||
if (!rcfg)
|
||||
return -EINVAL;
|
||||
|
||||
return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
|
||||
vbus_id, clk_type, enable);
|
||||
}
|
||||
|
||||
static int skl_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct skl_clk *clkdev = to_skl_clk(hw);
|
||||
|
||||
return skl_clk_change_status(clkdev, true);
|
||||
}
|
||||
|
||||
static void skl_clk_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct skl_clk *clkdev = to_skl_clk(hw);
|
||||
|
||||
skl_clk_change_status(clkdev, false);
|
||||
}
|
||||
|
||||
static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct skl_clk *clkdev = to_skl_clk(hw);
|
||||
struct skl_clk_rate_cfg_table *rcfg;
|
||||
int clk_type;
|
||||
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
|
||||
rate);
|
||||
if (!rcfg)
|
||||
return -EINVAL;
|
||||
|
||||
clk_type = skl_get_clk_type(clkdev->id);
|
||||
if (clk_type < 0)
|
||||
return clk_type;
|
||||
|
||||
skl_fill_clk_ipc(rcfg, clk_type);
|
||||
clkdev->rate = rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct skl_clk *clkdev = to_skl_clk(hw);
|
||||
|
||||
if (clkdev->rate)
|
||||
return clkdev->rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not supported by clk driver. Implemented to satisfy clk fw */
|
||||
long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare/unprepare are used instead of enable/disable as IPC will be sent
|
||||
* in non-atomic context.
|
||||
*/
|
||||
static const struct clk_ops skl_clk_ops = {
|
||||
.prepare = skl_clk_prepare,
|
||||
.unprepare = skl_clk_unprepare,
|
||||
.set_rate = skl_clk_set_rate,
|
||||
.round_rate = skl_clk_round_rate,
|
||||
.recalc_rate = skl_clk_recalc_rate,
|
||||
};
|
||||
|
||||
static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
|
||||
unsigned int id)
|
||||
{
|
||||
while (id--) {
|
||||
clkdev_drop(pclk[id].lookup);
|
||||
clk_hw_unregister_fixed_rate(pclk[id].hw);
|
||||
}
|
||||
}
|
||||
|
||||
static void unregister_src_clk(struct skl_clk_data *dclk)
|
||||
{
|
||||
u8 cnt = dclk->avail_clk_cnt;
|
||||
|
||||
while (cnt--)
|
||||
clkdev_drop(dclk->clk[cnt]->lookup);
|
||||
}
|
||||
|
||||
static int skl_register_parent_clks(struct device *dev,
|
||||
struct skl_clk_parent *parent,
|
||||
struct skl_clk_parent_src *pclk)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
|
||||
|
||||
/* Register Parent clock */
|
||||
parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
|
||||
pclk[i].parent_name, 0, pclk[i].rate);
|
||||
if (IS_ERR(parent[i].hw)) {
|
||||
ret = PTR_ERR(parent[i].hw);
|
||||
goto err;
|
||||
}
|
||||
|
||||
parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
|
||||
NULL);
|
||||
if (!parent[i].lookup) {
|
||||
clk_hw_unregister_fixed_rate(parent[i].hw);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
unregister_parent_src_clk(parent, i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Assign fmt_config to clk_data */
|
||||
static struct skl_clk *register_skl_clk(struct device *dev,
|
||||
struct skl_ssp_clk *clk,
|
||||
struct skl_clk_pdata *clk_pdata, int id)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct skl_clk *clkdev;
|
||||
int ret;
|
||||
|
||||
clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
|
||||
if (!clkdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = clk->name;
|
||||
init.ops = &skl_clk_ops;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
init.parent_names = &clk->parent_name;
|
||||
init.num_parents = 1;
|
||||
clkdev->hw.init = &init;
|
||||
clkdev->pdata = clk_pdata;
|
||||
|
||||
clkdev->id = id;
|
||||
ret = devm_clk_hw_register(dev, &clkdev->hw);
|
||||
if (ret) {
|
||||
clkdev = ERR_PTR(ret);
|
||||
return clkdev;
|
||||
}
|
||||
|
||||
clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
|
||||
if (!clkdev->lookup)
|
||||
clkdev = ERR_PTR(-ENOMEM);
|
||||
|
||||
return clkdev;
|
||||
}
|
||||
|
||||
static int skl_clk_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *parent_dev = dev->parent;
|
||||
struct skl_clk_parent_src *parent_clks;
|
||||
struct skl_clk_pdata *clk_pdata;
|
||||
struct skl_clk_data *data;
|
||||
struct skl_ssp_clk *clks;
|
||||
int ret, i;
|
||||
|
||||
clk_pdata = dev_get_platdata(&pdev->dev);
|
||||
parent_clks = clk_pdata->parent_clks;
|
||||
clks = clk_pdata->ssp_clks;
|
||||
if (!parent_clks || !clks)
|
||||
return -EIO;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Register Parent clock */
|
||||
ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < clk_pdata->num_clks; i++) {
|
||||
/*
|
||||
* Only register valid clocks
|
||||
* i.e. for which nhlt entry is present.
|
||||
*/
|
||||
if (clks[i].rate_cfg[0].rate == 0)
|
||||
continue;
|
||||
|
||||
data->clk[i] = register_skl_clk(dev, &clks[i], clk_pdata, i);
|
||||
if (IS_ERR(data->clk[i])) {
|
||||
ret = PTR_ERR(data->clk[i]);
|
||||
goto err_unreg_skl_clk;
|
||||
}
|
||||
|
||||
data->avail_clk_cnt++;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unreg_skl_clk:
|
||||
unregister_src_clk(data);
|
||||
unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_clk_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct skl_clk_data *data;
|
||||
|
||||
data = platform_get_drvdata(pdev);
|
||||
unregister_src_clk(data);
|
||||
unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver skl_clk_driver = {
|
||||
.driver = {
|
||||
.name = "skl-ssp-clk",
|
||||
},
|
||||
.probe = skl_clk_dev_probe,
|
||||
.remove = skl_clk_dev_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(skl_clk_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Skylake clock driver");
|
||||
MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
|
||||
MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:skl-ssp-clk");
|
@ -54,8 +54,46 @@ struct skl_clk_parent_src {
|
||||
const char *parent_name;
|
||||
};
|
||||
|
||||
struct skl_tlv_hdr {
|
||||
u32 type;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct skl_dmactrl_mclk_cfg {
|
||||
struct skl_tlv_hdr hdr;
|
||||
/* DMA Clk TLV params */
|
||||
u32 clk_warm_up:16;
|
||||
u32 mclk:1;
|
||||
u32 warm_up_over:1;
|
||||
u32 rsvd0:14;
|
||||
u32 clk_stop_delay:16;
|
||||
u32 keep_running:1;
|
||||
u32 clk_stop_over:1;
|
||||
u32 rsvd1:14;
|
||||
};
|
||||
|
||||
struct skl_dmactrl_sclkfs_cfg {
|
||||
struct skl_tlv_hdr hdr;
|
||||
/* DMA SClk&FS TLV params */
|
||||
u32 sampling_frequency;
|
||||
u32 bit_depth;
|
||||
u32 channel_map;
|
||||
u32 channel_config;
|
||||
u32 interleaving_style;
|
||||
u32 number_of_channels : 8;
|
||||
u32 valid_bit_depth : 8;
|
||||
u32 sample_type : 8;
|
||||
u32 reserved : 8;
|
||||
};
|
||||
|
||||
union skl_clk_ctrl_ipc {
|
||||
struct skl_dmactrl_mclk_cfg mclk;
|
||||
struct skl_dmactrl_sclkfs_cfg sclk_fs;
|
||||
};
|
||||
|
||||
struct skl_clk_rate_cfg_table {
|
||||
unsigned long rate;
|
||||
union skl_clk_ctrl_ipc dma_ctl_ipc;
|
||||
void *config;
|
||||
};
|
||||
|
||||
|
@ -190,7 +190,6 @@ skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
|
||||
u8 res_idx = mconfig->res_idx;
|
||||
struct skl_module_res *res = &mconfig->module->resources[res_idx];
|
||||
|
||||
res = &mconfig->module->resources[res_idx];
|
||||
skl->resource.mcps -= res->cps;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,10 @@
|
||||
/* D0I3C Register fields */
|
||||
#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */
|
||||
#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */
|
||||
#define SKL_MAX_DMACTRL_CFG 18
|
||||
#define DMA_CLK_CONTROLS 1
|
||||
#define DMA_TRANSMITION_START 2
|
||||
#define DMA_TRANSMITION_STOP 3
|
||||
|
||||
struct skl_dsp_resource {
|
||||
u32 max_mcps;
|
||||
@ -147,6 +151,8 @@ int skl_nhlt_create_sysfs(struct skl *skl);
|
||||
void skl_nhlt_remove_sysfs(struct skl *skl);
|
||||
void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks);
|
||||
struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id);
|
||||
int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
|
||||
u32 caps_size, u32 node_id);
|
||||
|
||||
struct skl_module_cfg;
|
||||
|
||||
|
@ -16,39 +16,6 @@
|
||||
|
||||
#include <sound/soc-acpi.h>
|
||||
|
||||
static acpi_status snd_soc_acpi_find_name(acpi_handle handle, u32 level,
|
||||
void *context, void **ret)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
const char *name = NULL;
|
||||
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
return AE_OK;
|
||||
|
||||
if (adev->status.present && adev->status.functional) {
|
||||
name = acpi_dev_name(adev);
|
||||
*(const char **)ret = name;
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
|
||||
{
|
||||
const char *name = NULL;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_get_devices(hid, snd_soc_acpi_find_name, NULL,
|
||||
(void **)&name);
|
||||
|
||||
if (ACPI_FAILURE(status) || name[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
return name;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_acpi_find_name_from_hid);
|
||||
|
||||
struct snd_soc_acpi_mach *
|
||||
snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines)
|
||||
{
|
||||
|
@ -590,14 +590,23 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
|
||||
{
|
||||
struct snd_soc_rtdcom_list *rtdcom;
|
||||
|
||||
if (!driver_name)
|
||||
return NULL;
|
||||
|
||||
for_each_rtdcom(rtd, rtdcom) {
|
||||
if ((rtdcom->component->driver->name == driver_name) ||
|
||||
strcmp(rtdcom->component->driver->name, driver_name) == 0)
|
||||
const char *component_name = rtdcom->component->driver->name;
|
||||
|
||||
if (!component_name)
|
||||
continue;
|
||||
|
||||
if ((component_name == driver_name) ||
|
||||
strcmp(component_name, driver_name) == 0)
|
||||
return rtdcom->component;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup);
|
||||
|
||||
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
|
||||
const char *dai_link, int stream)
|
||||
|
@ -34,6 +34,10 @@ int snd_soc_component_read(struct snd_soc_component *component,
|
||||
ret = regmap_read(component->regmap, reg, val);
|
||||
else if (component->read)
|
||||
ret = component->read(component, reg, val);
|
||||
else if (component->driver->read) {
|
||||
*val = component->driver->read(component, reg);
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
ret = -EIO;
|
||||
|
||||
@ -70,6 +74,8 @@ int snd_soc_component_write(struct snd_soc_component *component,
|
||||
return regmap_write(component->regmap, reg, val);
|
||||
else if (component->write)
|
||||
return component->write(component, reg, val);
|
||||
else if (component->driver->write)
|
||||
return component->driver->write(component, reg, val);
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user