From 6bd3c6f75e0f9baddbf1196a7e3fceabb50c7e3c Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 31 Aug 2015 17:07:12 +0200 Subject: ASoC: fsl-asoc-card: put ASRC OF node in case of unknown device In case of unknown DT compatible device the ASRC OF node possibly acquired earlier by of_parse_phandle() has to be put before returning from probe method. Signed-off-by: Maciej Szmigiero Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5aeb6ed..96f55ae 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -488,7 +488,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); - return -EINVAL; + ret = -EINVAL; + goto asrc_fail; } /* Common settings for corresponding Freescale CPU DAI driver */ -- cgit v1.1 From 887e83e32d99e43798e9ac9a274ad4debebf03f7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:18:50 +0800 Subject: ASoC: mediatek: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Acked-by: Koro Chen Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173-max98090.c | 11 +---------- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 11 +---------- 2 files changed, 2 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c index 4d44b58..72d66cb 100644 --- a/sound/soc/mediatek/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173-max98090.c @@ -169,21 +169,13 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) } card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); return ret; } -static int mt8173_max98090_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static const struct of_device_id mt8173_max98090_dt_match[] = { { .compatible = "mediatek,mt8173-max98090", }, { } @@ -200,7 +192,6 @@ static struct platform_driver mt8173_max98090_driver = { #endif }, .probe = mt8173_max98090_dev_probe, - .remove = mt8173_max98090_dev_remove, }; module_platform_driver(mt8173_max98090_driver); diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 0940553..bff96e4 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -234,21 +234,13 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; platform_set_drvdata(pdev, card); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); return ret; } -static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { { .compatible = "mediatek,mt8173-rt5650-rt5676", }, { } @@ -265,7 +257,6 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = { #endif }, .probe = mt8173_rt5650_rt5676_dev_probe, - .remove = mt8173_rt5650_rt5676_dev_remove, }; module_platform_driver(mt8173_rt5650_rt5676_driver); -- cgit v1.1 From d76f41982f2fc88492efd96c7c3178044f32e125 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Mon, 31 Aug 2015 08:24:23 -0700 Subject: ASoC: Document snd-soc-dummy-dai purpose Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- sound/soc/soc-utils.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 362c69a..53dd085 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -101,6 +101,15 @@ static struct snd_soc_codec_driver dummy_codec; SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_U32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) +/* + * The dummy CODEC is only meant to be used in situations where there is no + * actual hardware. + * + * If there is actual hardware even if it does not have a control bus + * the hardware will still have constraints like supported samplerates, etc. + * which should be modelled. And the data flow graph also should be modelled + * using DAPM. + */ static struct snd_soc_dai_driver dummy_dai = { .name = "snd-soc-dummy-dai", .playback = { -- cgit v1.1 From 51d2eeef1d958ef6834b24f548194f5acea0f499 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 1 Sep 2015 11:14:05 +0530 Subject: ASoC: wm0010: fix memory leak We were aborting if the kzalloc of img_swap fails but without freeing the already allocated out. Similarly we were aborting if spi_sync fails without releasing out and img_swap. Signed-off-by: Sudip Mukherjee Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 6560a66..9d370a4 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -672,8 +672,10 @@ static int wm0010_boot(struct snd_soc_codec *codec) } img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!img_swap) + if (!img_swap) { + kfree(out); goto abort; + } /* We need to re-order for 0010 */ byte_swap_64((u64 *)&pll_rec, img_swap, len); @@ -690,6 +692,8 @@ static int wm0010_boot(struct snd_soc_codec *codec) ret = spi_sync(spi, &m); if (ret != 0) { dev_err(codec->dev, "First PLL write failed: %d\n", ret); + kfree(img_swap); + kfree(out); goto abort; } @@ -697,6 +701,8 @@ static int wm0010_boot(struct snd_soc_codec *codec) ret = spi_sync(spi, &m); if (ret != 0) { dev_err(codec->dev, "Second PLL write failed: %d\n", ret); + kfree(img_swap); + kfree(out); goto abort; } -- cgit v1.1 From 721b51fcf91898299d96f4b72cb9434cda29dce6 Mon Sep 17 00:00:00 2001 From: John Lin Date: Wed, 9 Sep 2015 16:47:48 +0800 Subject: ASoC: rt5645: Add struct dmi_system_id "Google Ultima" for chrome platform Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 610eacd..e620da1 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3236,6 +3236,13 @@ static struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Celes"), }, }, + { + .ident = "Google Ultima", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"), + }, + }, { } }; -- cgit v1.1 From f1ec5ec7a94ba8138f9cbdc1e9e3b03aee29c4df Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 Sep 2015 12:10:31 +0100 Subject: ASoC: intel: Fix SSP port configuration after RTD3 resume. Currently the SSP port settings are being clobbered as part of the DSP RTD3 restore logic. make sure we save the correct params and restore them at resume. The FW sadly does not save SSP settings as part of the PM context. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/intel/haswell/sst-haswell-ipc.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index f95f271..5bc9855 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -302,6 +302,10 @@ struct sst_hsw { struct sst_hsw_ipc_dx_reply dx; void *dx_context; dma_addr_t dx_context_paddr; + enum sst_hsw_device_id dx_dev; + enum sst_hsw_device_mclk dx_mclk; + enum sst_hsw_device_mode dx_mode; + u32 dx_clock_divider; /* boot */ wait_queue_head_t boot_wait; @@ -1400,10 +1404,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, trace_ipc_request("set device config", dev); - config.ssp_interface = dev; - config.clock_frequency = mclk; - config.mode = mode; - config.clock_divider = clock_divider; + hsw->dx_dev = config.ssp_interface = dev; + hsw->dx_mclk = config.clock_frequency = mclk; + hsw->dx_mode = config.mode = mode; + hsw->dx_clock_divider = config.clock_divider = clock_divider; if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER) config.channels = 4; else @@ -1704,10 +1708,10 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) return -EIO; } - /* Set ADSP SSP port settings */ - ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0, - SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, - SST_HSW_DEVICE_CLOCK_MASTER, 9); + /* Set ADSP SSP port settings - sadly the FW does not store SSP port + settings as part of the PM context. */ + ret = sst_hsw_device_set_config(hsw, hsw->dx_dev, hsw->dx_mclk, + hsw->dx_mode, hsw->dx_clock_divider); if (ret < 0) dev_err(dev, "error: SSP re-initialization failed\n"); -- cgit v1.1 From 3a0e27d84bb9abac5e39dc71706768a88c72cb71 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Thu, 10 Sep 2015 09:45:55 +0200 Subject: ASoC: sti: check return of of_property_read Add check on of_property_read to return error when DT required property is not defined. Signed-off-by: Arnaud Pouliquen Signed-off-by: Mark Brown --- sound/soc/sti/uniperif_player.c | 14 ++++++++++---- sound/soc/sti/uniperif_reader.c | 6 +++++- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index f6eefe1..843f037 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -989,8 +989,8 @@ static int uni_player_parse_dt(struct platform_device *pdev, if (!info) return -ENOMEM; - of_property_read_u32(pnode, "version", &player->ver); - if (player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { + if (of_property_read_u32(pnode, "version", &player->ver) || + player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { dev_err(dev, "Unknown uniperipheral version "); return -EINVAL; } @@ -998,10 +998,16 @@ static int uni_player_parse_dt(struct platform_device *pdev, if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) info->underflow_enabled = 1; - of_property_read_u32(pnode, "uniperiph-id", &info->id); + if (of_property_read_u32(pnode, "uniperiph-id", &info->id)) { + dev_err(dev, "uniperipheral id not defined"); + return -EINVAL; + } /* Read the device mode property */ - of_property_read_string(pnode, "mode", &mode); + if (of_property_read_string(pnode, "mode", &mode)) { + dev_err(dev, "uniperipheral mode not defined"); + return -EINVAL; + } if (strcasecmp(mode, "hdmi") == 0) info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI; diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index c502626..f791239 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -316,7 +316,11 @@ static int uni_reader_parse_dt(struct platform_device *pdev, if (!info) return -ENOMEM; - of_property_read_u32(node, "version", &reader->ver); + if (of_property_read_u32(node, "version", &reader->ver) || + reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { + dev_err(&pdev->dev, "Unknown uniperipheral version "); + return -EINVAL; + } /* Save the info structure */ reader->info = info; -- cgit v1.1 From 75881df3fd7708f234c1e2573ade812eb5701708 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 10 Sep 2015 18:01:44 +0530 Subject: ASoC: dapm: fix memory leak Incase of an unknown event we were directly returning but we missed freeing params. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f4bf21a..ff8bda4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3501,7 +3501,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, default: WARN(1, "Unknown event %d\n", event); - return -EINVAL; + ret = -EINVAL; } out: -- cgit v1.1 From f17b329b73a0393dc9d5fc5b4457189f92e5bbef Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 10 Sep 2015 13:40:16 +0800 Subject: ASoC: rt5645: Remove incorrect settings The patch removes the incorrect settings to avoid the pop sound in the first playback with headphone after boot. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 4972bf3..595ace9 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2829,10 +2829,6 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; } - - snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200); - snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d); - snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001); } else { /* jack out */ rt5645->jack_type = 0; @@ -2880,8 +2876,6 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec, rt5645->en_button_func = true; regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ); - regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1, - RT5645_HP_CB_MASK, RT5645_HP_CB_PU); regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1, RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL); } -- cgit v1.1 From 3758ff5f3dab57cd768d54279962a2f6bbc17188 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Wed, 9 Sep 2015 19:29:10 +0800 Subject: ASoC: wm8960: correct the min gain value of some PGA The min gain is the corresponding gain value when the register value is 0 instead of 1, just correct it. Signed-off-by: Zidan Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 94c5c46..9c73049 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -205,11 +205,11 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, return wm8960_set_deemph(codec); } -static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); -static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); -static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1); +static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); static const struct snd_kcontrol_new wm8960_snd_controls[] = { SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, -- cgit v1.1 From 7e90f9b2b56669260e5f6f97974735d187f79b7d Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Wed, 9 Sep 2015 19:29:11 +0800 Subject: ASoC: wm8960: correct gain value for input PGA and add microphone PGA The input PGAs have a gain range from -17.25dB to +30dB in 0.75dB steps. The boost stage can provide additional gain. For line inputs, -12dB to +6dB gain is available on the boost mixer. For micphone inputs, it can provide up to +29dB additional gain from the microphone PGA. Signed-off-by: Zidan Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 9c73049..70816f0 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -206,27 +206,37 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, } static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); -static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1); +static const unsigned int micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0), + 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0), +}; static const struct snd_kcontrol_new wm8960_snd_controls[] = { SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, - 0, 63, 0, adc_tlv), + 0, 63, 0, inpga_tlv), SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 6, 1, 0), SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 7, 1, 0), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", - WM8960_INBMIX1, 4, 7, 0, boost_tlv), + WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", - WM8960_INBMIX1, 1, 7, 0, boost_tlv), + WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", - WM8960_INBMIX2, 4, 7, 0, boost_tlv), + WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", - WM8960_INBMIX2, 1, 7, 0, boost_tlv), + WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", + WM8960_RINPATH, 4, 4, 0, micboost_tlv), +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume", + WM8960_LINPATH, 4, 4, 0, micboost_tlv), SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 0, 255, 0, dac_tlv), -- cgit v1.1 From 723831927e8813b5b336d383174f686ad708bf10 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Sep 2015 16:06:48 +0300 Subject: ASoC: davinci-mcasp: Revise the FIFO threshold calculation The FIFO threshold for McASP should be <=[tx/rx]numevt so the initial value for the refining should meet this requirement as well. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index add6bb9..1260b31 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -663,7 +663,7 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, u8 rx_ser = 0; u8 slots = mcasp->tdm_slots; u8 max_active_serializers = (channels + slots - 1) / slots; - int active_serializers, numevt, n; + int active_serializers, numevt; u32 reg; /* Default configuration */ if (mcasp->version < MCASP_VERSION_3) @@ -745,9 +745,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, * The number of words for numevt need to be in steps of active * serializers. */ - n = numevt % active_serializers; - if (n) - numevt += (active_serializers - n); + numevt = (numevt / active_serializers) * active_serializers; + while (period_words % numevt && numevt > 0) numevt -= active_serializers; if (numevt <= 0) -- cgit v1.1 From e4fba9b5be12d577d2e2c19fdca6b0744c3f271e Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Mon, 14 Sep 2015 14:51:17 +0800 Subject: ASoC: mediatek: Increase periods_min in capture In capture, there is chance that hw_ptr reported at IRQ is a little smaller than period_size due to internal AFE buffer. In the case of ping-pong buffer: |xxxxxxxxxxxxxxxxxxxxxxxxxxxx--|-----------------------------| hw_ptr < period_size This available buffer will not be read since its size is smaller than avail_min (which is period_size by default), and read thread continues to sleep. If the next hw_ptr is just a little larger than buffer_size, overrun occurs. One more period can hold the possible unread buffer. Signed-off-by: Koro Chen Signed-off-by: Mark Brown --- sound/soc/mediatek/mtk-afe-pcm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sound') diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index d190fe0..f5baf3c 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -549,6 +549,23 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream, memif->substream = substream; snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); + + /* + * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be + * smaller than period_size due to AFE's internal buffer. + * This easily leads to overrun when avail_min is period_size. + * One more period can hold the possible unread buffer. + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIODS, + 3, + mtk_afe_hardware.periods_max); + if (ret < 0) { + dev_err(afe->dev, "hw_constraint_minmax failed\n"); + return ret; + } + } ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) -- cgit v1.1 From 2342cafe2269fe7cccda0aea88e30e8f4bcc075f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:16:01 +0800 Subject: ASoC: au1x: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/au1x/db1000.c | 10 +--------- sound/soc/au1x/db1200.c | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c index 452f404..e97c327 100644 --- a/sound/soc/au1x/db1000.c +++ b/sound/soc/au1x/db1000.c @@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev) { struct snd_soc_card *card = &db1000_ac97; card->dev = &pdev->dev; - return snd_soc_register_card(card); -} - -static int db1000_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - snd_soc_unregister_card(card); - return 0; + return devm_snd_soc_register_card(&pdev->dev, card); } static struct platform_driver db1000_audio_driver = { @@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = { .pm = &snd_soc_pm_ops, }, .probe = db1000_audio_probe, - .remove = db1000_audio_remove, }; module_platform_driver(db1000_audio_driver); diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 58c3164..638ca0b 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -174,14 +174,7 @@ static int db1200_audio_probe(struct platform_device *pdev) card = db1200_cards[pid->driver_data]; card->dev = &pdev->dev; - return snd_soc_register_card(card); -} - -static int db1200_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - snd_soc_unregister_card(card); - return 0; + return devm_snd_soc_register_card(&pdev->dev, card); } static struct platform_driver db1200_audio_driver = { @@ -191,7 +184,6 @@ static struct platform_driver db1200_audio_driver = { }, .id_table = db1200_pids, .probe = db1200_audio_probe, - .remove = db1200_audio_remove, }; module_platform_driver(db1200_audio_driver); -- cgit v1.1 From 76b0d1f5e748630e01763d7fd6bec8436af7ec51 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:17:44 +0800 Subject: ASoC: blackfin: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-ad1836.c | 11 +---------- sound/soc/blackfin/bfin-eval-adau1373.c | 12 +----------- sound/soc/blackfin/bfin-eval-adau1701.c | 12 +----------- sound/soc/blackfin/bfin-eval-adav80x.c | 12 +----------- 4 files changed, 4 insertions(+), 43 deletions(-) (limited to 'sound') diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index 5bf1501..864df26 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -87,27 +87,18 @@ static int bf5xx_ad1836_driver_probe(struct platform_device *pdev) card->dev = &pdev->dev; platform_set_drvdata(pdev, card); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "Failed to register card\n"); return ret; } -static int bf5xx_ad1836_driver_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver bf5xx_ad1836_driver = { .driver = { .name = "bfin-snd-ad1836", .pm = &snd_soc_pm_ops, }, .probe = bf5xx_ad1836_driver_probe, - .remove = bf5xx_ad1836_driver_remove, }; module_platform_driver(bf5xx_ad1836_driver); diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c index 523baf58..72ac789 100644 --- a/sound/soc/blackfin/bfin-eval-adau1373.c +++ b/sound/soc/blackfin/bfin-eval-adau1373.c @@ -154,16 +154,7 @@ static int bfin_eval_adau1373_probe(struct platform_device *pdev) card->dev = &pdev->dev; - return snd_soc_register_card(&bfin_eval_adau1373); -} - -static int bfin_eval_adau1373_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373); } static struct platform_driver bfin_eval_adau1373_driver = { @@ -172,7 +163,6 @@ static struct platform_driver bfin_eval_adau1373_driver = { .pm = &snd_soc_pm_ops, }, .probe = bfin_eval_adau1373_probe, - .remove = bfin_eval_adau1373_remove, }; module_platform_driver(bfin_eval_adau1373_driver); diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c index f9e926d..5c67f72 100644 --- a/sound/soc/blackfin/bfin-eval-adau1701.c +++ b/sound/soc/blackfin/bfin-eval-adau1701.c @@ -94,16 +94,7 @@ static int bfin_eval_adau1701_probe(struct platform_device *pdev) card->dev = &pdev->dev; - return snd_soc_register_card(&bfin_eval_adau1701); -} - -static int bfin_eval_adau1701_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701); } static struct platform_driver bfin_eval_adau1701_driver = { @@ -112,7 +103,6 @@ static struct platform_driver bfin_eval_adau1701_driver = { .pm = &snd_soc_pm_ops, }, .probe = bfin_eval_adau1701_probe, - .remove = bfin_eval_adau1701_remove, }; module_platform_driver(bfin_eval_adau1701_driver); diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c index 27eee66..1037477 100644 --- a/sound/soc/blackfin/bfin-eval-adav80x.c +++ b/sound/soc/blackfin/bfin-eval-adav80x.c @@ -119,16 +119,7 @@ static int bfin_eval_adav80x_probe(struct platform_device *pdev) card->dev = &pdev->dev; - return snd_soc_register_card(&bfin_eval_adav80x); -} - -static int bfin_eval_adav80x_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x); } static const struct platform_device_id bfin_eval_adav80x_ids[] = { @@ -144,7 +135,6 @@ static struct platform_driver bfin_eval_adav80x_driver = { .pm = &snd_soc_pm_ops, }, .probe = bfin_eval_adav80x_probe, - .remove = bfin_eval_adav80x_remove, .id_table = bfin_eval_adav80x_ids, }; -- cgit v1.1 From 5b73de19fd65f11a8c1b33bb7aeb0275898f8786 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:20:01 +0800 Subject: ASoC: mxs-sgtl5000: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 6e6fce6..2b23ffb 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) card->dev = &pdev->dev; platform_set_drvdata(pdev, card); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -154,12 +154,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) static int mxs_sgtl5000_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - mxs_saif_put_mclk(0); - snd_soc_unregister_card(card); - return 0; } -- cgit v1.1 From 2fd7076a43710666b147e206866ecd946b0b33e5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 10:32:59 +0800 Subject: ASoC: pxa: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/pxa/brownstone.c | 9 +-------- sound/soc/pxa/corgi.c | 11 +---------- sound/soc/pxa/e740_wm9705.c | 5 +---- sound/soc/pxa/e750_wm9705.c | 5 +---- sound/soc/pxa/e800_wm9712.c | 5 +---- sound/soc/pxa/hx4700.c | 4 +--- sound/soc/pxa/imote2.c | 11 +---------- sound/soc/pxa/mioa701_wm9713.c | 11 +---------- sound/soc/pxa/palm27x.c | 9 +-------- sound/soc/pxa/poodle.c | 11 +---------- sound/soc/pxa/spitz.c | 5 +---- sound/soc/pxa/tosa.c | 5 +---- sound/soc/pxa/ttc-dkb.c | 12 +----------- 13 files changed, 13 insertions(+), 90 deletions(-) (limited to 'sound') diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 2b26318..6147e86 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -116,26 +116,19 @@ static int brownstone_probe(struct platform_device *pdev) int ret; brownstone.dev = &pdev->dev; - ret = snd_soc_register_card(&brownstone); + ret = devm_snd_soc_register_card(&pdev->dev, &brownstone); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int brownstone_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&brownstone); - return 0; -} - static struct platform_driver mmp_driver = { .driver = { .name = "brownstone-audio", .pm = &snd_soc_pm_ops, }, .probe = brownstone_probe, - .remove = brownstone_remove, }; module_platform_driver(mmp_driver); diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 3580d10..c97dc13 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -295,28 +295,19 @@ static int corgi_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int corgi_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver corgi_driver = { .driver = { .name = "corgi-audio", .pm = &snd_soc_pm_ops, }, .probe = corgi_probe, - .remove = corgi_remove, }; module_platform_driver(corgi_driver); diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index d72e124..1de8765 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -138,7 +138,7 @@ static int e740_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -149,10 +149,7 @@ static int e740_probe(struct platform_device *pdev) static int e740_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios)); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 48f2d7c..b7eb7cd 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -120,7 +120,7 @@ static int e750_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -131,10 +131,7 @@ static int e750_probe(struct platform_device *pdev) static int e750_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios)); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 45d4bd4..41bf714 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -119,7 +119,7 @@ static int e800_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -130,10 +130,7 @@ static int e800_probe(struct platform_device *pdev) static int e800_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios)); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index 9f8be7c..ecbf287 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -193,7 +193,7 @@ static int hx4700_audio_probe(struct platform_device *pdev) return ret; snd_soc_card_hx4700.dev = &pdev->dev; - ret = snd_soc_register_card(&snd_soc_card_hx4700); + ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700); if (ret) gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios)); @@ -203,8 +203,6 @@ static int hx4700_audio_probe(struct platform_device *pdev) static int hx4700_audio_remove(struct platform_device *pdev) { - snd_soc_unregister_card(&snd_soc_card_hx4700); - gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0); gpio_set_value(GPIO107_HX4700_SPK_nSD, 0); diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c index 29fabbf..9d0e407 100644 --- a/sound/soc/pxa/imote2.c +++ b/sound/soc/pxa/imote2.c @@ -72,28 +72,19 @@ static int imote2_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int imote2_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver imote2_driver = { .driver = { .name = "imote2-audio", .pm = &snd_soc_pm_ops, }, .probe = imote2_probe, - .remove = imote2_remove, }; module_platform_driver(imote2_driver); diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index a9615a57..29bc60e8 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -181,7 +181,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev) return -ENODEV; mioa701.dev = &pdev->dev; - rc = snd_soc_register_card(&mioa701); + rc = devm_snd_soc_register_card(&pdev->dev, &mioa701); if (!rc) dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will" "lead to overheating and possible destruction of your device." @@ -189,17 +189,8 @@ static int mioa701_wm9713_probe(struct platform_device *pdev) return rc; } -static int mioa701_wm9713_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver mioa701_wm9713_driver = { .probe = mioa701_wm9713_probe, - .remove = mioa701_wm9713_remove, .driver = { .name = "mioa701-wm9713", .pm = &snd_soc_pm_ops, diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index c20bbc0..4e74d95 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -140,22 +140,15 @@ static int palm27x_asoc_probe(struct platform_device *pdev) palm27x_asoc.dev = &pdev->dev; - ret = snd_soc_register_card(&palm27x_asoc); + ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int palm27x_asoc_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&palm27x_asoc); - return 0; -} - static struct platform_driver palm27x_wm9712_driver = { .probe = palm27x_asoc_probe, - .remove = palm27x_asoc_remove, .driver = { .name = "palm27x-asoc", .pm = &snd_soc_pm_ops, diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 80b457a..84d0e2e 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -267,28 +267,19 @@ static int poodle_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int poodle_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver poodle_driver = { .driver = { .name = "poodle-audio", .pm = &snd_soc_pm_ops, }, .probe = poodle_probe, - .remove = poodle_remove, }; module_platform_driver(poodle_driver); diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 461123a..b002226 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -305,7 +305,7 @@ static int spitz_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -322,9 +322,6 @@ err1: static int spitz_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); gpio_free(spitz_mic_gpio); return 0; } diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index f59f566..49518dd 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -233,7 +233,7 @@ static int tosa_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -244,10 +244,7 @@ static int tosa_probe(struct platform_device *pdev) static int tosa_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free(TOSA_GPIO_L_MUTE); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index 1753c7d..65c20f7 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -128,7 +128,7 @@ static int ttc_dkb_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -136,22 +136,12 @@ static int ttc_dkb_probe(struct platform_device *pdev) return ret; } -static int ttc_dkb_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static struct platform_driver ttc_dkb_driver = { .driver = { .name = "ttc-dkb-audio", .pm = &snd_soc_pm_ops, }, .probe = ttc_dkb_probe, - .remove = ttc_dkb_remove, }; module_platform_driver(ttc_dkb_driver); -- cgit v1.1 From 42617869bf095c650e67aad4001cab4224e7fa98 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 12:31:24 +0800 Subject: ASoC: SPEAr: Make SND_SPEAR_SOC select SND_SOC_GENERIC_DMAENGINE_PCM devm_snd_dmaengine_pcm_register() is guarded by CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/spear/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 0a53053..4fb9141 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,6 +1,6 @@ config SND_SPEAR_SOC tristate - select SND_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SPEAR_SPDIF_OUT tristate -- cgit v1.1 From 921e54680aefe52f28d9ce9485edb1bfef4b92a8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 14:50:15 +0800 Subject: ASoC: au1x: psc-i2s: Fix unused variable 'ret' warning Fix below build warning: sound/soc/au1x/psc-i2s.c: In function 'au1xpsc_i2s_drvprobe': sound/soc/au1x/psc-i2s.c:299:6: warning: unused variable 'ret' [-Wunused-variable] Reported-by: kbuild test robot Signed-off-by: Axel Lin Acked-by: Manuel Lauss Signed-off-by: Mark Brown --- sound/soc/au1x/psc-i2s.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index 38e853a..0bf9d62 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -296,7 +296,6 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev) { struct resource *iores, *dmares; unsigned long sel; - int ret; struct au1xpsc_audio_data *wd; wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data), -- cgit v1.1 From 1ff68f55901ffd25b40f23bc6296657beca8377f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 21:11:10 +0800 Subject: ASoC: sh: siu_dai: Convert to use resource managed APIs Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/sh/siu_dai.c | 85 ++++++++++++++------------------------------------ 1 file changed, 24 insertions(+), 61 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index abb0d95..76b2ab8 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -738,7 +738,7 @@ static int siu_probe(struct platform_device *pdev) struct siu_info *info; int ret; - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; siu_i2s_data = info; @@ -746,7 +746,7 @@ static int siu_probe(struct platform_device *pdev) ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); if (ret) - goto ereqfw; + return ret; /* * Loaded firmware is "const" - read only, but we have to modify it in @@ -757,89 +757,52 @@ static int siu_probe(struct platform_device *pdev) release_firmware(fw_entry); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto egetres; - } + if (!res) + return -ENODEV; - region = request_mem_region(res->start, resource_size(res), - pdev->name); + region = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name); if (!region) { dev_err(&pdev->dev, "SIU region already claimed\n"); - ret = -EBUSY; - goto ereqmemreg; + return -EBUSY; } - ret = -ENOMEM; - info->pram = ioremap(res->start, PRAM_SIZE); + info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE); if (!info->pram) - goto emappram; - info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE); + return -ENOMEM; + info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET, + XRAM_SIZE); if (!info->xram) - goto emapxram; - info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE); + return -ENOMEM; + info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET, + YRAM_SIZE); if (!info->yram) - goto emapyram; - info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) - - REG_OFFSET); + return -ENOMEM; + info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET, + resource_size(res) - REG_OFFSET); if (!info->reg) - goto emapreg; + return -ENOMEM; dev_set_drvdata(&pdev->dev, info); /* register using ARRAY version so we can keep dai name */ - ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component, - &siu_i2s_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component, + &siu_i2s_dai, 1); if (ret < 0) - goto edaiinit; + return ret; - ret = snd_soc_register_platform(&pdev->dev, &siu_platform); + ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform); if (ret < 0) - goto esocregp; + return ret; pm_runtime_enable(&pdev->dev); - return ret; - -esocregp: - snd_soc_unregister_component(&pdev->dev); -edaiinit: - iounmap(info->reg); -emapreg: - iounmap(info->yram); -emapyram: - iounmap(info->xram); -emapxram: - iounmap(info->pram); -emappram: - release_mem_region(res->start, resource_size(res)); -ereqmemreg: -egetres: -ereqfw: - kfree(info); - - return ret; + return 0; } static int siu_remove(struct platform_device *pdev) { - struct siu_info *info = dev_get_drvdata(&pdev->dev); - struct resource *res; - pm_runtime_disable(&pdev->dev); - - snd_soc_unregister_platform(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - - iounmap(info->reg); - iounmap(info->yram); - iounmap(info->xram); - iounmap(info->pram); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); - kfree(info); - return 0; } -- cgit v1.1 From 9ed747641b0fe122a827494b1d490bc2e2e45347 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 2 Sep 2015 12:01:27 +0800 Subject: ASoC: intel: broadwell: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/intel/boards/broadwell.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 8bafaf6..3f8a1e1 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -266,18 +266,11 @@ static int broadwell_audio_probe(struct platform_device *pdev) { broadwell_rt286.dev = &pdev->dev; - return snd_soc_register_card(&broadwell_rt286); -} - -static int broadwell_audio_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&broadwell_rt286); - return 0; + return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286); } static struct platform_driver broadwell_audio = { .probe = broadwell_audio_probe, - .remove = broadwell_audio_remove, .driver = { .name = "broadwell-audio", }, -- cgit v1.1 From aab7826f592b4af9a1407cf5b717978dc16cc19a Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:55:08 +0200 Subject: ASoC: atmel_wm8904: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/atmel/atmel_wm8904.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index aa354e1..1933bcd 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = { { .compatible = "atmel,asoc-wm8904", }, { } }; +MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids); #endif static struct platform_driver atmel_asoc_wm8904_driver = { -- cgit v1.1 From 5226f2340c67225d27f61330205f16314881cc5c Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:57:47 +0200 Subject: ASoC: fsl-asoc-card: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5aeb6ed..97bb4c5 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -592,6 +592,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-wm8960", }, {} }; +MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); static struct platform_driver fsl_asoc_card_driver = { .probe = fsl_asoc_card_probe, -- cgit v1.1 From c759241fe2f16e6be43675abaa715f0da9d7a254 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:58:23 +0200 Subject: ASoC: fsl_sai: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a18fd92..9366b5a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -801,6 +801,7 @@ static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,imx6sx-sai", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, fsl_sai_ids); static struct platform_driver fsl_sai_driver = { .probe = fsl_sai_probe, -- cgit v1.1 From 27fcf913f6462662e9ec1b74ce04961126852db9 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:58:57 +0200 Subject: ASoC: jz4740: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/jz4740/jz4740-i2s.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index b05fb1c..794a349 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -485,6 +485,7 @@ static const struct of_device_id jz4740_of_matches[] = { { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, jz4740_of_matches); #endif static int jz4740_i2s_dev_probe(struct platform_device *pdev) -- cgit v1.1 From 30953a804714fcfe49bec594239306bf5b476464 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:59:32 +0200 Subject: ASoC: kirkwood: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/kirkwood/armada-370-db.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c index de7563b..e0304d5 100644 --- a/sound/soc/kirkwood/armada-370-db.c +++ b/sound/soc/kirkwood/armada-370-db.c @@ -130,6 +130,7 @@ static const struct of_device_id a370db_dt_ids[] = { { .compatible = "marvell,a370db-audio" }, { }, }; +MODULE_DEVICE_TABLE(of, a370db_dt_ids); static struct platform_driver a370db_driver = { .driver = { -- cgit v1.1 From baafd373e9287f20ca0c9a6bb38eb6785a146ac2 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 13:00:03 +0200 Subject: ASoC: pxa: Fix module autoload for OF platform drivers These platform drivers have a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/pxa/pxa-ssp.c | 1 + sound/soc/pxa/pxa2xx-pcm.c | 1 + 2 files changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 3da485e..da03fad 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -809,6 +809,7 @@ static const struct of_device_id pxa_ssp_of_ids[] = { { .compatible = "mrvl,pxa-ssp-dai" }, {} }; +MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); #endif static int asoc_ssp_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 831ee37..29a3fdb 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -132,6 +132,7 @@ static const struct of_device_id snd_soc_pxa_audio_match[] = { { .compatible = "mrvl,pxa-pcm-audio" }, { } }; +MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match); #endif static struct platform_driver pxa_pcm_driver = { -- cgit v1.1 From cfa7a38c754a56fd80762d1251f4c344eb535230 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 13:00:40 +0200 Subject: ASoC: mop500: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/ux500/mop500.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 4e0c0e5..ba9fc09 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -152,6 +152,7 @@ static const struct of_device_id snd_soc_mop500_match[] = { { .compatible = "stericsson,snd-soc-mop500", }, {}, }; +MODULE_DEVICE_TABLE(of, snd_soc_mop500_match); static struct platform_driver snd_soc_mop500_driver = { .driver = { -- cgit v1.1 From c81740e00556312af32fb3cd5ec12174935e5f70 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 13:01:15 +0200 Subject: ASoC: ux500: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_dai.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index f5df08d..6ba8ae9 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -843,6 +843,7 @@ static const struct of_device_id ux500_msp_i2s_match[] = { { .compatible = "stericsson,ux500-msp-i2s", }, {}, }; +MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match); static struct platform_driver msp_i2s_driver = { .driver = { -- cgit v1.1 From 295c3405a8bbd69ee8c8eb6580f30b0b8739b33a Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Sep 2015 21:27:42 +0300 Subject: ASoC: davinci-mcasp: Set .symmetric_rates = 1 in snd_soc_dai_driver The TX and RX direction share the same bit clock and frame sync, so the samplerate must be the same to both directions. Signed-off-by: Jyri Sarha Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 1260b31..2c5a2c5 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1298,6 +1298,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { .ops = &davinci_mcasp_dai_ops, .symmetric_samplebits = 1, + .symmetric_rates = 1, }, { .name = "davinci-mcasp.1", -- cgit v1.1 From 2719a752b6e96b22ac6a0d5b9397c21c93abcd0f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:39:59 +0000 Subject: ASoC: ak4642: use *dev on ak4642_i2c_probe() Let's replace &i2c->dev to dev Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4642.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 4a90143..9af06d3 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -584,7 +584,8 @@ static const struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct device_node *np = i2c->dev.of_node; + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; const struct ak4642_drvdata *drvdata = NULL; struct regmap *regmap; struct ak4642_priv *priv; @@ -592,7 +593,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, if (np) { const struct of_device_id *of_id; - of_id = of_match_device(ak4642_of_match, &i2c->dev); + of_id = of_match_device(ak4642_of_match, dev); if (of_id) drvdata = of_id->data; } else { @@ -600,11 +601,11 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, } if (!drvdata) { - dev_err(&i2c->dev, "Unknown device type\n"); + dev_err(dev, "Unknown device type\n"); return -EINVAL; } - priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -616,7 +617,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, if (IS_ERR(regmap)) return PTR_ERR(regmap); - return snd_soc_register_codec(&i2c->dev, + return snd_soc_register_codec(dev, &soc_codec_dev_ak4642, &ak4642_dai, 1); } -- cgit v1.1 From 171a0138ab75fcbe1228c4af0442221efccfb197 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:40:19 +0000 Subject: ASoC: ak4642: enable to use MCKO as fixed rate output pin on DT ak4642 chip can output clock via MCKO pin as audio reference clock. This patch supports it on DT Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4642.c | 141 ++++++++++++++++++++++++++++++---------------- 1 file changed, 93 insertions(+), 48 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 9af06d3..b5c4981 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -23,6 +23,8 @@ * AK4648 is tested. */ +#include +#include #include #include #include @@ -128,11 +130,8 @@ #define I2S (3 << 0) /* MD_CTL2 */ -#define FS0 (1 << 0) -#define FS1 (1 << 1) -#define FS2 (1 << 2) -#define FS3 (1 << 5) -#define FS_MASK (FS0 | FS1 | FS2 | FS3) +#define FS(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) +#define PS(val) ((val & 0x3) << 6) /* MD_CTL3 */ #define BST1 (1 << 3) @@ -147,6 +146,7 @@ struct ak4642_drvdata { struct ak4642_priv { const struct ak4642_drvdata *drvdata; + struct clk *mcko; }; /* @@ -430,56 +430,55 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int ak4642_set_mcko(struct snd_soc_codec *codec, + u32 frequency) +{ + u32 fs_list[] = { + [0] = 8000, + [1] = 12000, + [2] = 16000, + [3] = 24000, + [4] = 7350, + [5] = 11025, + [6] = 14700, + [7] = 22050, + [10] = 32000, + [11] = 48000, + [14] = 29400, + [15] = 44100, + }; + u32 ps_list[] = { + [0] = 256, + [1] = 128, + [2] = 64, + [3] = 32 + }; + int ps, fs; + + for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) { + for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) { + if (frequency == ps_list[ps] * fs_list[fs]) { + snd_soc_write(codec, MD_CTL2, PS(ps) | FS(fs)); + return 0; + } + } + } + + return 0; +} + static int ak4642_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - u8 rate; + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + u32 rate = clk_get_rate(priv->mcko); - switch (params_rate(params)) { - case 7350: - rate = FS2; - break; - case 8000: - rate = 0; - break; - case 11025: - rate = FS2 | FS0; - break; - case 12000: - rate = FS0; - break; - case 14700: - rate = FS2 | FS1; - break; - case 16000: - rate = FS1; - break; - case 22050: - rate = FS2 | FS1 | FS0; - break; - case 24000: - rate = FS1 | FS0; - break; - case 29400: - rate = FS3 | FS2 | FS1; - break; - case 32000: - rate = FS3 | FS1; - break; - case 44100: - rate = FS3 | FS2 | FS1 | FS0; - break; - case 48000: - rate = FS3 | FS1 | FS0; - break; - default: - return -EINVAL; - } - snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate); + if (!rate) + rate = params_rate(params) * 256; - return 0; + return ak4642_set_mcko(codec, rate); } static int ak4642_set_bias_level(struct snd_soc_codec *codec, @@ -532,7 +531,18 @@ static int ak4642_resume(struct snd_soc_codec *codec) return 0; } +static int ak4642_probe(struct snd_soc_codec *codec) +{ + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (priv->mcko) + ak4642_set_mcko(codec, clk_get_rate(priv->mcko)); + + return 0; +} + static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { + .probe = ak4642_probe, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, .controls = ak4642_snd_controls, @@ -580,6 +590,35 @@ static const struct ak4642_drvdata ak4648_drvdata = { .extended_frequencies = 1, }; +#ifdef CONFIG_COMMON_CLK +static struct clk *ak4642_of_parse_mcko(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct clk *clk; + const char *clk_name = np->name; + const char *parent_clk_name = NULL; + u32 rate; + + if (of_property_read_u32(np, "clock-frequency", &rate)) + return NULL; + + if (of_property_read_bool(np, "clocks")) + parent_clk_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, + (parent_clk_name) ? 0 : CLK_IS_ROOT, + rate); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + return clk; +} +#else +#define ak4642_of_parse_mcko(d) 0 +#endif + static const struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -589,10 +628,15 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, const struct ak4642_drvdata *drvdata = NULL; struct regmap *regmap; struct ak4642_priv *priv; + struct clk *mcko = NULL; if (np) { const struct of_device_id *of_id; + mcko = ak4642_of_parse_mcko(dev); + if (IS_ERR(mcko)) + mcko = NULL; + of_id = of_match_device(ak4642_of_match, dev); if (of_id) drvdata = of_id->data; @@ -610,6 +654,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, return -ENOMEM; priv->drvdata = drvdata; + priv->mcko = mcko; i2c_set_clientdata(i2c, priv); -- cgit v1.1 From c25f20e1de6e22960f69cc93220e71ba8b5ee8b6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:48:52 +0000 Subject: ASoC: rsnd: tidyup rsnd_dma_to_xxx() position rsnd_dma_to_xxx() macro should exist in same place Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/rsnd.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 7a0e52b..6ae8068 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -214,6 +214,7 @@ struct rsnd_dma { }; #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma); void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma); @@ -225,8 +226,6 @@ int rsnd_dma_probe(struct platform_device *pdev, struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); -#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) - /* * R-Car sound mod */ -- cgit v1.1 From b9bfe9d5f7755e225989a253e427cc620c001662 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:49:12 +0000 Subject: ASoC: rsnd: rename rsnd_src_pcm_new() to rsnd_src_pcm_new_gen2() rsnd_src_pcm_new() is used only from Gen2. make it clear in function name, and remove unneeded Gen1 check. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 89a18e1..06555a3 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -918,11 +918,10 @@ static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_IFSVR, fsrate); } -static int rsnd_src_pcm_new(struct rsnd_mod *mod, +static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -932,12 +931,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, */ /* - * Gen1 is not supported - */ - if (rsnd_is_gen1(priv)) - return 0; - - /* * SRC sync convert needs clock master */ if (!rsnd_rdai_is_clk_master(rdai)) @@ -975,7 +968,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = { .start = rsnd_src_start_gen2, .stop = rsnd_src_stop_gen2, .hw_params = rsnd_src_hw_params, - .pcm_new = rsnd_src_pcm_new, + .pcm_new = rsnd_src_pcm_new_gen2, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) -- cgit v1.1 From 7486d80f7d853f50088124824bf62d9a4d14de75 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:49:29 +0000 Subject: ASoC: rsnd: remove unneeded sh_clk header sh_clk header is not needed, and it will create confusion. Let's remove it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index fefc881..b512be8 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -7,7 +7,6 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#include #include "rsnd.h" #define CLKA 0 -- cgit v1.1 From ac37a45b0b6c8400719bb837f1c321079b72db53 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:01:58 +0000 Subject: ASoC: rsnd: Add Gen3 initial support Renesas sound Gen3 is updated version of Gen2. We need to update driver for it, but basically it should works as Gen2 compatible. This is initial support for Gen3. Gen3 specific feature will be incrementally added in the future Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f3feed5..870f944 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = { static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, + { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */ {}, }; MODULE_DEVICE_TABLE(of, rsnd_of_match); -- cgit v1.1 From b76e218ae5e5e8d8025b75066e82ad0674e2e845 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:02:21 +0000 Subject: ASoC: rsnd: add rsnd_mod_get() macro and use it Renesas sound driver has SSI/SRC/DVC/CTU/MIX, and these are controlled as modules. And these module are member of each modules's private data. It used own method to get module pointer, but Let's use common method Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 6 ++--- sound/soc/sh/rcar/dvc.c | 6 ++--- sound/soc/sh/rcar/mix.c | 6 ++--- sound/soc/sh/rcar/rsnd.h | 1 + sound/soc/sh/rcar/src.c | 6 ++--- sound/soc/sh/rcar/ssi.c | 58 ++++++++++++++++++++++++++++-------------------- 6 files changed, 47 insertions(+), 36 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 05498bb..a3e7c71 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -66,7 +66,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) id = 0; - return &((struct rsnd_ctu *)(priv->ctu) + id)->mod; + return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id); } static void rsnd_of_parse_ctu(struct platform_device *pdev, @@ -150,7 +150,7 @@ int rsnd_ctu_probe(struct platform_device *pdev, ctu->info = &info->ctu_info[i]; - ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops, + ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, clk, RSND_MOD_CTU, i); if (ret) return ret; @@ -166,6 +166,6 @@ void rsnd_ctu_remove(struct platform_device *pdev, int i; for_each_rsnd_ctu(ctu, priv, i) { - rsnd_mod_quit(&ctu->mod); + rsnd_mod_quit(rsnd_mod_get(ctu)); } } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 5779638..8d8eee6 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -282,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) id = 0; - return &((struct rsnd_dvc *)(priv->dvc) + id)->mod; + return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id); } static void rsnd_of_parse_dvc(struct platform_device *pdev, @@ -361,7 +361,7 @@ int rsnd_dvc_probe(struct platform_device *pdev, dvc->info = &info->dvc_info[i]; - ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, + ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); if (ret) return ret; @@ -377,6 +377,6 @@ void rsnd_dvc_remove(struct platform_device *pdev, int i; for_each_rsnd_dvc(dvc, priv, i) { - rsnd_mod_quit(&dvc->mod); + rsnd_mod_quit(rsnd_mod_get(dvc)); } } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 0d5c102..8544403 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -99,7 +99,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) id = 0; - return &((struct rsnd_mix *)(priv->mix) + id)->mod; + return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id); } static void rsnd_of_parse_mix(struct platform_device *pdev, @@ -179,7 +179,7 @@ int rsnd_mix_probe(struct platform_device *pdev, mix->info = &info->mix_info[i]; - ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops, + ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, clk, RSND_MOD_MIX, i); if (ret) return ret; @@ -195,6 +195,6 @@ void rsnd_mix_remove(struct platform_device *pdev, int i; for_each_rsnd_mix(mix, priv, i) { - rsnd_mod_quit(&mix->mod); + rsnd_mod_quit(rsnd_mod_get(mix)); } } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 6ae8068..f35d9cc 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -331,6 +331,7 @@ struct rsnd_mod { #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) #define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) +#define rsnd_mod_get(ip) (&(ip)->mod) int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 06555a3..ca7a20f 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -976,7 +976,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) id = 0; - return &((struct rsnd_src *)(priv->src) + id)->mod; + return rsnd_mod_get((struct rsnd_src *)(priv->src) + id); } static void rsnd_of_parse_src(struct platform_device *pdev, @@ -1071,7 +1071,7 @@ int rsnd_src_probe(struct platform_device *pdev, src->info = &info->src_info[i]; - ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i); + ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i); if (ret) return ret; } @@ -1086,6 +1086,6 @@ void rsnd_src_remove(struct platform_device *pdev, int i; for_each_rsnd_src(src, priv, i) { - rsnd_mod_quit(&src->mod); + rsnd_mod_quit(rsnd_mod_get(src)); } } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d45b9a7..91712e8 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -128,6 +128,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, struct rsnd_priv *priv = rsnd_io_to_priv(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *mod = rsnd_mod_get(ssi); int i, j, ret; int adg_clk_div_table[] = { 1, 6, /* see adg.c */ @@ -152,14 +153,14 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, main_rate = rate / adg_clk_div_table[i] * 32 * 2 * ssi_clk_mul_table[j]; - ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (0 == ret) { ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(j); dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(&ssi->mod), - rsnd_mod_id(&ssi->mod), rate); + rsnd_mod_name(mod), + rsnd_mod_id(mod), rate); return 0; } @@ -172,8 +173,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) { + struct rsnd_mod *mod = rsnd_mod_get(ssi); + ssi->cr_clk = 0; - rsnd_adg_ssi_clk_stop(&ssi->mod); + rsnd_adg_ssi_clk_stop(mod); } static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, @@ -182,11 +185,12 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *mod = rsnd_mod_get(ssi); u32 cr_mode; u32 cr; if (0 == ssi->usrcnt) { - rsnd_mod_hw_start(&ssi->mod); + rsnd_mod_hw_start(mod); if (rsnd_rdai_is_clk_master(rdai)) { struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); @@ -198,7 +202,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, } } - if (rsnd_ssi_is_dma_mode(&ssi->mod)) { + if (rsnd_ssi_is_dma_mode(mod)) { cr_mode = UIEN | OIEN | /* over/under run */ DMEN; /* DMA : enable DMA */ } else { @@ -210,24 +214,25 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, cr_mode | EN; - rsnd_mod_write(&ssi->mod, SSICR, cr); + rsnd_mod_write(mod, SSICR, cr); /* enable WS continue */ if (rsnd_rdai_is_clk_master(rdai)) - rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + rsnd_mod_write(mod, SSIWSR, CONT); /* clear error status */ - rsnd_mod_write(&ssi->mod, SSISR, 0); + rsnd_mod_write(mod, SSISR, 0); ssi->usrcnt++; dev_dbg(dev, "%s[%d] hw started\n", - rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); + rsnd_mod_name(mod), rsnd_mod_id(mod)); } static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) { - struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); u32 cr; @@ -247,15 +252,15 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) cr = ssi->cr_own | ssi->cr_clk; - rsnd_mod_write(&ssi->mod, SSICR, cr | EN); - rsnd_ssi_status_check(&ssi->mod, DIRQ); + rsnd_mod_write(mod, SSICR, cr | EN); + rsnd_ssi_status_check(mod, DIRQ); /* * disable SSI, * and, wait idle state */ - rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ - rsnd_ssi_status_check(&ssi->mod, IIRQ); + rsnd_mod_write(mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(mod, IIRQ); if (rsnd_rdai_is_clk_master(rdai)) { struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); @@ -266,13 +271,13 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) rsnd_ssi_master_clk_stop(ssi); } - rsnd_mod_hw_stop(&ssi->mod); + rsnd_mod_hw_stop(mod); ssi->chan = 0; } dev_dbg(dev, "%s[%d] hw stopped\n", - rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); + rsnd_mod_name(mod), rsnd_mod_id(mod)); } /* @@ -371,7 +376,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, /* It will be removed on rsnd_ssi_hw_stop */ ssi->chan = chan; if (ssi_parent) - return rsnd_ssi_hw_params(&ssi_parent->mod, io, + return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io, substream, params); return 0; @@ -379,12 +384,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) { + struct rsnd_mod *mod = rsnd_mod_get(ssi); + /* under/over flow error */ if (status & (UIRQ | OIRQ)) { ssi->err++; /* clear error status */ - rsnd_mod_write(&ssi->mod, SSISR, 0); + rsnd_mod_write(mod, SSISR, 0); } } @@ -656,7 +663,7 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) id = 0; - return &((struct rsnd_ssi *)(priv->ssi) + id)->mod; + return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); } int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) @@ -668,10 +675,12 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { - if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) + struct rsnd_mod *mod = rsnd_mod_get(ssi); + + if (!rsnd_ssi_is_pin_sharing(mod)) return; - switch (rsnd_mod_id(&ssi->mod)) { + switch (rsnd_mod_id(mod)) { case 1: case 2: ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); @@ -794,7 +803,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, else if (rsnd_ssi_pio_available(ssi)) ops = &rsnd_ssi_pio_ops; - ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i); + ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, + RSND_MOD_SSI, i); if (ret) return ret; @@ -811,6 +821,6 @@ void rsnd_ssi_remove(struct platform_device *pdev, int i; for_each_rsnd_ssi(ssi, priv, i) { - rsnd_mod_quit(&ssi->mod); + rsnd_mod_quit(rsnd_mod_get(ssi)); } } -- cgit v1.1 From 1665a9e5e224a44798ea6ae16b507d48752eb4a1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:02:39 +0000 Subject: ASoC: rsnd: ADG uses mod base common method Renesas sound has ADG IP, but it is special device. (It is clock generater, and it doesn't need MSTP) Thus, ADG didn't use mod base common method on rsnd driver. But it can be confusable point. Let's use common method Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 67 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index b512be8..9ff1736 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -15,8 +15,13 @@ #define CLKI 3 #define CLKMAX 4 +static struct rsnd_mod_ops adg_ops = { + .name = "adg", +}; + struct rsnd_adg { struct clk *clk[CLKMAX]; + struct rsnd_mod mod; int rbga_rate_for_441khz_div_6; /* RBGA */ int rbgb_rate_for_48khz_div_6; /* RBGB */ @@ -59,6 +64,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int id = rsnd_mod_id(mod); int shift = (id % 2) ? 16 : 0; u32 mask, val; @@ -68,7 +76,7 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, val = val << shift; mask = 0xffff << shift; - rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val); + rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val); return 0; } @@ -77,6 +85,9 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, u32 timsel) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int is_play = rsnd_io_is_play(io); int id = rsnd_mod_id(mod); int shift = (id % 2) ? 16 : 0; @@ -94,24 +105,24 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, switch (id / 2) { case 0: - rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out); break; case 1: - rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out); break; case 2: - rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out); break; case 3: - rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out); break; case 4: - rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out); break; } @@ -125,6 +136,7 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); int idx, sel, div, step, ret; u32 val, en; @@ -180,7 +192,7 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, return ret; } - rsnd_mod_bset(mod, DIV_EN, en, en); + rsnd_mod_bset(adg_mod, DIV_EN, en, en); dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); @@ -201,6 +213,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, unsigned int dst_rate) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); int idx, sel, div, shift; u32 mask, val; @@ -237,13 +250,13 @@ find_rate: switch (id / 4) { case 0: - rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val); break; case 1: - rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val); break; case 2: - rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val); break; } @@ -258,6 +271,9 @@ find_rate: static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int id = rsnd_mod_id(mod); int shift = (id % 4) * 8; u32 mask = 0xFF << shift; @@ -273,13 +289,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) switch (id / 4) { case 0: - rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val); break; case 1: - rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val); break; case 2: - rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val); break; } } @@ -299,6 +315,7 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; int i; @@ -342,9 +359,9 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) found_clock: /* see rsnd_adg_ssi_clk_init() */ - rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr); - rsnd_mod_write(mod, BRRA, 0x00000002); /* 1/6 */ - rsnd_mod_write(mod, BRRB, 0x00000002); /* 1/6 */ + rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, adg->ckr); + rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ + rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ /* * This "mod" = "ssi" here. @@ -421,6 +438,14 @@ int rsnd_adg_probe(struct platform_device *pdev, return -ENOMEM; } + /* + * ADG is special module. + * Use ADG mod without rsnd_mod_init() to make debug easy + * for rsnd_write/rsnd_read + */ + adg->mod.ops = &adg_ops; + adg->mod.priv = priv; + adg->clk[CLKA] = devm_clk_get(dev, "clk_a"); adg->clk[CLKB] = devm_clk_get(dev, "clk_b"); adg->clk[CLKC] = devm_clk_get(dev, "clk_c"); -- cgit v1.1 From f1df12290722f998f5eb173ae30434d313aea54c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:03:08 +0000 Subject: ASoC: rsnd: add common mod confirm method Renesas sound has SSI/SRC/DVC/MIX/ADG modules, and these have original register mapping. Thus this driver is using regmap field, and each module is using it based on each module ID. Sometimes, each module needs other module to controlling. but current each function is using just "mod" as parameter name. This is confusable. For example, if SSI0 and SRC2 are connected, and if SRC module function has bug of module access, and if it needs to control connected SSI, SRC function will access to SSI2 (It should access to SSI0, but it uses SRC's ID 2). This is easy to happen in current driver style. To avoid this kind of confusable trouble, this patch adds module confirm macro for debug purpose. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 30 +++++++++++++++++++----------- sound/soc/sh/rcar/core.c | 11 +++++++++++ sound/soc/sh/rcar/rsnd.h | 11 +++++++++++ 3 files changed, 41 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 9ff1736..48bb38d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -81,19 +81,21 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, return 0; } -static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, +static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, u32 timsel) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int is_play = rsnd_io_is_play(io); - int id = rsnd_mod_id(mod); + int id = rsnd_mod_id(src_mod); int shift = (id % 2) ? 16 : 0; u32 mask, ws; u32 in, out; + rsnd_mod_confirm_src(src_mod); + ws = rsnd_adg_ssi_ws_timing_gen2(io); in = (is_play) ? timsel : ws; @@ -129,12 +131,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, return 0; } -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, unsigned int src_rate, unsigned int dst_rate) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); @@ -149,6 +151,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ }; + rsnd_mod_confirm_src(src_mod); + min = ~0; val = 0; en = 0; @@ -186,7 +190,7 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, return -EIO; } - ret = rsnd_adg_set_src_timsel_gen2(mod, io, val); + ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val); if (ret < 0) { dev_err(dev, "timsel error\n"); return ret; @@ -199,12 +203,14 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, return 0; } -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io) { u32 val = rsnd_adg_ssi_ws_timing_gen2(io); - return rsnd_adg_set_src_timsel_gen2(mod, io, val); + rsnd_mod_confirm_src(src_mod); + + return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); } int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, @@ -269,15 +275,17 @@ find_rate: return 0; } -static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) +static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int id = rsnd_mod_id(mod); + int id = rsnd_mod_id(ssi_mod); int shift = (id % 4) * 8; u32 mask = 0xFF << shift; + rsnd_mod_confirm_ssi(ssi_mod); + val = val << shift; /* diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 870f944..eec294d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -127,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match); #define rsnd_info_id(priv, io, name) \ ((io)->info->name - priv->info->name##_info) +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) +{ + if (mod->type != type) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "%s[%d] is not your expected module\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } +} + /* * rsnd_mod functions */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index f35d9cc..e4068d7 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -627,4 +627,15 @@ void rsnd_dvc_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); +#ifdef DEBUG +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); +#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI) +#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC) +#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC) +#else +#define rsnd_mod_confirm_ssi(mssi) +#define rsnd_mod_confirm_src(msrc) +#define rsnd_mod_confirm_dvc(mdvc) +#endif + #endif -- cgit v1.1 From 5c6901d98b35c3192336e419344f0f22f86b8845 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:03:25 +0000 Subject: ASoC: rsnd: ADG initial setup on rsnd_adg_ssi_clk_init() ADG is special IP since it doesn't have MSTP. And now, ADG has common mod base register access. We can now setup ADG initial setting when probe timing. It is needed if sound card is based on AUIDO_CLK which is used as Master clock. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 48bb38d..276703b 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -25,7 +25,6 @@ struct rsnd_adg { int rbga_rate_for_441khz_div_6; /* RBGA */ int rbgb_rate_for_48khz_div_6; /* RBGB */ - u32 ckr; }; #define for_each_rsnd_clk(pos, adg, i) \ @@ -323,7 +322,6 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; int i; @@ -366,11 +364,6 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) found_clock: - /* see rsnd_adg_ssi_clk_init() */ - rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, adg->ckr); - rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ - rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ - /* * This "mod" = "ssi" here. * we can get "ssi id" from mod @@ -386,6 +379,7 @@ found_clock: static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) { struct clk *clk; + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); unsigned long rate; u32 ckr; int i; @@ -405,6 +399,7 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) * and, BRGB outputs 48.0kHz base parent clock 1/32 here. * see * rsnd_adg_ssi_clk_try_start() + * rsnd_ssi_master_clk_start() */ ckr = 0; adg->rbga_rate_for_441khz_div_6 = 0; @@ -428,7 +423,9 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) } } - adg->ckr = ckr; + rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr); + rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ + rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ } int rsnd_adg_probe(struct platform_device *pdev, -- cgit v1.1 From eae6fff4f15a9d1969412bb5aa5a3585f00821fb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:03:48 +0000 Subject: ASoC: rsnd: tidyup ADG clock calculate method Current ADG clock calculation needs ADG and SSI settings. Thus, SSI side clock request function depends on ADG settings. After reconsideration, we can close this method inside ADG. This function uses new method. And it becomes preparation for AUDIO_CLKOUT support. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 77 ++++++++++++++++++++++++++++++++++--------------- sound/soc/sh/rcar/ssi.c | 46 +++++++++++++---------------- 2 files changed, 74 insertions(+), 49 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 276703b..a0b9aaa 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -15,6 +15,8 @@ #define CLKI 3 #define CLKMAX 4 +#define BRRx_MASK(x) (0x3FF & x) + static struct rsnd_mod_ops adg_ops = { .name = "adg", }; @@ -23,8 +25,8 @@ struct rsnd_adg { struct clk *clk[CLKMAX]; struct rsnd_mod mod; - int rbga_rate_for_441khz_div_6; /* RBGA */ - int rbgb_rate_for_48khz_div_6; /* RBGB */ + int rbga_rate_for_441khz; /* RBGA */ + int rbgb_rate_for_48khz; /* RBGB */ }; #define for_each_rsnd_clk(pos, adg, i) \ @@ -34,6 +36,21 @@ struct rsnd_adg { i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) +static u32 rsnd_adg_calculate_rbgx(unsigned long div) +{ + int i, ratio; + + if (!div) + return 0; + + for (i = 3; i >= 0; i--) { + ratio = 2 << (i * 2); + if (0 == (div % ratio)) + return (u32)((i << 8) | ((div / ratio) - 1)); + } + + return ~0; +} static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) { @@ -146,8 +163,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ - adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */ - adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ + adg->rbga_rate_for_441khz, /* 0011: RBGA */ + adg->rbgb_rate_for_48khz, /* 0100: RBGB */ }; rsnd_mod_confirm_src(src_mod); @@ -228,8 +245,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */ clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */ 0, /* 011: MLBCLK (not used) */ - adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */ - adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */ + adg->rbga_rate_for_441khz, /* 100: RBGA */ + adg->rbgb_rate_for_48khz, /* 101: RBGB */ }; /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */ @@ -348,14 +365,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) } /* - * find 1/6 clock from BRGA/BRGB + * find divided clock from BRGA/BRGB */ - if (rate == adg->rbga_rate_for_441khz_div_6) { + if (rate == adg->rbga_rate_for_441khz) { data = 0x10; goto found_clock; } - if (rate == adg->rbgb_rate_for_48khz_div_6) { + if (rate == adg->rbgb_rate_for_48khz) { data = 0x20; goto found_clock; } @@ -380,8 +397,9 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) { struct clk *clk; struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - unsigned long rate; - u32 ckr; + struct device *dev = rsnd_priv_to_dev(priv); + unsigned long rate, div; + u32 ckr, rbgx, rbga, rbgb; int i; int brg_table[] = { [CLKA] = 0x0, @@ -395,15 +413,15 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) * have 44.1kHz or 48kHz base clocks for now. * * SSI itself can divide parent clock by 1/1 - 1/16 - * So, BRGA outputs 44.1kHz base parent clock 1/32, - * and, BRGB outputs 48.0kHz base parent clock 1/32 here. * see * rsnd_adg_ssi_clk_try_start() * rsnd_ssi_master_clk_start() */ ckr = 0; - adg->rbga_rate_for_441khz_div_6 = 0; - adg->rbgb_rate_for_48khz_div_6 = 0; + rbga = 2; /* default 1/6 */ + rbgb = 2; /* default 1/6 */ + adg->rbga_rate_for_441khz = 0; + adg->rbgb_rate_for_48khz = 0; for_each_rsnd_clk(clk, adg, i) { rate = clk_get_rate(clk); @@ -411,21 +429,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) continue; /* RBGA */ - if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) { - adg->rbga_rate_for_441khz_div_6 = rate / 6; - ckr |= brg_table[i] << 20; + if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) { + div = 6; + rbgx = rsnd_adg_calculate_rbgx(div); + if (BRRx_MASK(rbgx) == rbgx) { + rbga = rbgx; + adg->rbga_rate_for_441khz = rate / div; + ckr |= brg_table[i] << 20; + } } /* RBGB */ - if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) { - adg->rbgb_rate_for_48khz_div_6 = rate / 6; - ckr |= brg_table[i] << 16; + if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) { + div = 6; + rbgx = rsnd_adg_calculate_rbgx(div); + if (BRRx_MASK(rbgx) == rbgx) { + rbgb = rbgx; + adg->rbgb_rate_for_48khz = rate / div; + ckr |= brg_table[i] << 16; + } } } rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr); - rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ - rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ + rsnd_mod_write(adg_mod, BRRA, rbga); + rsnd_mod_write(adg_mod, BRRB, rbgb); + + dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", + ckr, rbga, rbgb); } int rsnd_adg_probe(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 91712e8..5e05f942 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -129,10 +129,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod *mod = rsnd_mod_get(ssi); - int i, j, ret; - int adg_clk_div_table[] = { - 1, 6, /* see adg.c */ - }; + int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, }; @@ -142,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, /* * Find best clock, and try to start ADG */ - for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { - for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { - - /* - * this driver is assuming that - * system word is 64fs (= 2 x 32bit) - * see rsnd_ssi_init() - */ - main_rate = rate / adg_clk_div_table[i] - * 32 * 2 * ssi_clk_mul_table[j]; - - ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); - if (0 == ret) { - ssi->cr_clk = FORCE | SWL_32 | - SCKD | SWSD | CKDV(j); - - dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod), rate); - - return 0; - } + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * this driver is assuming that + * system word is 64fs (= 2 x 32bit) + * see rsnd_ssi_init() + */ + main_rate = rate * 32 * 2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); + if (0 == ret) { + ssi->cr_clk = FORCE | SWL_32 | + SCKD | SWSD | CKDV(j); + + dev_dbg(dev, "%s[%d] outputs %u Hz\n", + rsnd_mod_name(mod), + rsnd_mod_id(mod), rate); + + return 0; } } -- cgit v1.1 From 3af6c3ac91eb937fc611f9c745f89e7c53d55282 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:04:06 +0000 Subject: ASoC: rsnd: tidyup ADG debug message for clock selection It didn't have "\n", and indicated different data Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index a0b9aaa..606d416 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -387,8 +387,9 @@ found_clock: */ rsnd_adg_set_ssi_clk(mod, data); - dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", - rsnd_mod_id(mod), i, rate); + dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + data, rate); return 0; } -- cgit v1.1 From 248e88c2fb5a09eb87b21b00fd4167cc99c05759 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:04:24 +0000 Subject: ASoC: rsnd: adg: ignore undefined clock error undefined clock is not error. Accept such case. And this is prepare for clock out support in the same time. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 606d416..d4fb11a 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -394,6 +394,28 @@ found_clock: return 0; } +static void rsnd_adg_get_clkin(struct rsnd_priv *priv, + struct rsnd_adg *adg) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + static const char * const clk_name[] = { + [CLKA] = "clk_a", + [CLKB] = "clk_b", + [CLKC] = "clk_c", + [CLKI] = "clk_i", + }; + int i; + + for (i = 0; i < CLKMAX; i++) { + clk = devm_clk_get(dev, clk_name[i]); + adg->clk[i] = IS_ERR(clk) ? NULL : clk; + } + + for_each_rsnd_clk(clk, adg, i) + dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); +} + static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) { struct clk *clk; @@ -466,8 +488,6 @@ int rsnd_adg_probe(struct platform_device *pdev, { struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); - struct clk *clk; - int i; adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); if (!adg) { @@ -483,13 +503,7 @@ int rsnd_adg_probe(struct platform_device *pdev, adg->mod.ops = &adg_ops; adg->mod.priv = priv; - adg->clk[CLKA] = devm_clk_get(dev, "clk_a"); - adg->clk[CLKB] = devm_clk_get(dev, "clk_b"); - adg->clk[CLKC] = devm_clk_get(dev, "clk_c"); - adg->clk[CLKI] = devm_clk_get(dev, "clk_i"); - - for_each_rsnd_clk(clk, adg, i) - dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); + rsnd_adg_get_clkin(priv, adg); rsnd_adg_ssi_clk_init(priv, adg); -- cgit v1.1 From 2a46db4a3787edb0dc07276f21f33bbaf01938f1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:04:45 +0000 Subject: ASoC: rsnd: add AUDIO_CLKOUT support Renesas sound has AUDIO_CLKOUT (in Gen1/Gen2) AUDIO_CLKOUT1/2/3 (in Gen3) This patch support these patches as clock provider. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d4fb11a..3fecb87 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -7,6 +7,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ +#include #include "rsnd.h" #define CLKA 0 @@ -15,6 +16,12 @@ #define CLKI 3 #define CLKMAX 4 +#define CLKOUT 0 +#define CLKOUT1 1 +#define CLKOUT2 2 +#define CLKOUT3 3 +#define CLKOUTMAX 4 + #define BRRx_MASK(x) (0x3FF & x) static struct rsnd_mod_ops adg_ops = { @@ -23,6 +30,8 @@ static struct rsnd_mod_ops adg_ops = { struct rsnd_adg { struct clk *clk[CLKMAX]; + struct clk *clkout[CLKOUTMAX]; + struct clk_onecell_data onecell; struct rsnd_mod mod; int rbga_rate_for_441khz; /* RBGA */ @@ -34,6 +43,11 @@ struct rsnd_adg { (i < CLKMAX) && \ ((pos) = adg->clk[i]); \ i++) +#define for_each_rsnd_clkout(pos, adg, i) \ + for (i = 0; \ + (i < CLKOUTMAX) && \ + ((pos) = adg->clkout[i]); \ + i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) static u32 rsnd_adg_calculate_rbgx(unsigned long div) @@ -416,14 +430,25 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv, dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); } -static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) +static void rsnd_adg_get_clkout(struct rsnd_priv *priv, + struct rsnd_adg *adg) { struct clk *clk; struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); - unsigned long rate, div; + struct device_node *np = dev->of_node; u32 ckr, rbgx, rbga, rbgb; + u32 rate, req_rate, div; + uint32_t count = 0; + unsigned long req_48kHz_rate, req_441kHz_rate; int i; + const char *parent_clk_name = NULL; + static const char * const clkout_name[] = { + [CLKOUT] = "audio_clkout", + [CLKOUT1] = "audio_clkout1", + [CLKOUT2] = "audio_clkout2", + [CLKOUT3] = "audio_clkout3", + }; int brg_table[] = { [CLKA] = 0x0, [CLKB] = 0x1, @@ -431,6 +456,20 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) [CLKI] = 0x2, }; + of_property_read_u32(np, "#clock-cells", &count); + + /* + * ADG supports BRRA/BRRB output only + * this means all clkout0/1/2/3 will be same rate + */ + of_property_read_u32(np, "clock-frequency", &req_rate); + req_48kHz_rate = 0; + req_441kHz_rate = 0; + if (0 == (req_rate % 44100)) + req_441kHz_rate = req_rate; + if (0 == (req_rate % 48000)) + req_48kHz_rate = req_rate; + /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC * have 44.1kHz or 48kHz base clocks for now. @@ -454,22 +493,72 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) /* RBGA */ if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) { div = 6; + if (req_441kHz_rate) + div = rate / req_441kHz_rate; rbgx = rsnd_adg_calculate_rbgx(div); if (BRRx_MASK(rbgx) == rbgx) { rbga = rbgx; adg->rbga_rate_for_441khz = rate / div; ckr |= brg_table[i] << 20; + if (req_441kHz_rate) + parent_clk_name = __clk_get_name(clk); } } /* RBGB */ if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) { div = 6; + if (req_48kHz_rate) + div = rate / req_48kHz_rate; rbgx = rsnd_adg_calculate_rbgx(div); if (BRRx_MASK(rbgx) == rbgx) { rbgb = rbgx; adg->rbgb_rate_for_48khz = rate / div; ckr |= brg_table[i] << 16; + if (req_48kHz_rate) { + parent_clk_name = __clk_get_name(clk); + ckr |= 0x80000000; + } + } + } + } + + /* + * ADG supports BRRA/BRRB output only. + * this means all clkout0/1/2/3 will be * same rate + */ + + /* + * for clkout + */ + if (!count) { + clk = clk_register_fixed_rate(dev, clkout_name[i], + parent_clk_name, + (parent_clk_name) ? + 0 : CLK_IS_ROOT, req_rate); + if (!IS_ERR(clk)) { + adg->clkout[CLKOUT] = clk; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + } + } + /* + * for clkout0/1/2/3 + */ + else { + for (i = 0; i < CLKOUTMAX; i++) { + clk = clk_register_fixed_rate(dev, clkout_name[i], + parent_clk_name, + (parent_clk_name) ? + 0 : CLK_IS_ROOT, + req_rate); + if (!IS_ERR(clk)) { + adg->onecell.clks = adg->clkout; + adg->onecell.clk_num = CLKOUTMAX; + + adg->clkout[i] = clk; + + of_clk_add_provider(np, of_clk_src_onecell_get, + &adg->onecell); } } } @@ -478,6 +567,8 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) rsnd_mod_write(adg_mod, BRRA, rbga); rsnd_mod_write(adg_mod, BRRB, rbgb); + for_each_rsnd_clkout(clk, adg, i) + dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk)); dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", ckr, rbga, rbgb); } @@ -504,8 +595,7 @@ int rsnd_adg_probe(struct platform_device *pdev, adg->mod.priv = priv; rsnd_adg_get_clkin(priv, adg); - - rsnd_adg_ssi_clk_init(priv, adg); + rsnd_adg_get_clkout(priv, adg); priv->adg = adg; -- cgit v1.1 From 03d3964c517b24b11923978e2ed4a2a19b702f8a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 12:31:24 +0800 Subject: ASoC: SPEAr: Make SND_SPEAR_SOC select SND_SOC_GENERIC_DMAENGINE_PCM devm_snd_dmaengine_pcm_register() is guarded by CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/spear/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 0a53053..4fb9141 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,6 +1,6 @@ config SND_SPEAR_SOC tristate - select SND_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SPEAR_SPDIF_OUT tristate -- cgit v1.1 From 3e8f5263bd83fd0fe7dacba2de347b17ac99fc91 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Sep 2015 21:27:46 +0300 Subject: ASoC: tlv320aic3x: Improve tdm support Before this patch the set_tdm_slots() callback did not store the value of slot width anywhere. The tdm support only worked if selected slot width was equal to the sample width. With this patch all sample widths that fit into the slot width are supported. There unused bits are filled unnecessarily in the capture direction, but the other end of the i2s bus should be able to ignore them. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic3x.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 1a82b19..f1c9fff 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -80,6 +80,7 @@ struct aic3x_priv { unsigned int sysclk; unsigned int dai_fmt; unsigned int tdm_delay; + unsigned int slot_width; struct list_head list; int master; int gpio_reset; @@ -1025,10 +1026,14 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u16 d, pll_d = 1; int clk; + int width = aic3x->slot_width; + + if (!width) + width = params_width(params); /* select data word length */ data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4)); - switch (params_width(params)) { + switch (width) { case 16: break; case 20: @@ -1170,12 +1175,16 @@ static int aic3x_prepare(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); int delay = 0; + int width = aic3x->slot_width; + + if (!width) + width = substream->runtime->sample_bits; /* TDM slot selection only valid in DSP_A/_B mode */ if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A) - delay += (aic3x->tdm_delay + 1); + delay += (aic3x->tdm_delay*width + 1); else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B) - delay += aic3x->tdm_delay; + delay += aic3x->tdm_delay*width; /* Configure data delay */ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay); @@ -1296,7 +1305,20 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, return -EINVAL; } - aic3x->tdm_delay = lsb * slot_width; + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(codec->dev, "Unsupported slot width %d\n", slot_width); + return -EINVAL; + } + + + aic3x->tdm_delay = lsb; + aic3x->slot_width = slot_width; /* DOUT in high-impedance on inactive bit clocks */ snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, -- cgit v1.1 From dd55ff8346a972cca1ad056c8258ee96d090633e Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Sep 2015 21:27:44 +0300 Subject: ASoC: davinci-mcasp: Add set_tdm_slots() support Implements set_tdm_slot() callback for mcasp. Channel constraints are updated according to the configured tdm mask and slots each time set_tdm_slot() is called. The special case when slot width is set to zero is allowed and it means that slot width is the same as the sample width. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 255 ++++++++++++++++++++++++++------------ 1 file changed, 174 insertions(+), 81 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index add6bb9..fa47a39 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -80,6 +80,8 @@ struct davinci_mcasp { /* McASP specific data */ int tdm_slots; + u32 tdm_mask[2]; + int slot_width; u8 op_mode; u8 num_serializer; u8 *serial_dir; @@ -596,6 +598,84 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; } +/* All serializers must have equal number of channels */ +static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, + int serializers) +{ + struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream]; + unsigned int *list = (unsigned int *) cl->list; + int slots = mcasp->tdm_slots; + int i, count = 0; + + if (mcasp->tdm_mask[stream]) + slots = hweight32(mcasp->tdm_mask[stream]); + + for (i = 2; i <= slots; i++) + list[count++] = i; + + for (i = 2; i <= serializers; i++) + list[count++] = i*slots; + + cl->count = count; + + return 0; +} + +static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp) +{ + int rx_serializers = 0, tx_serializers = 0, ret, i; + + for (i = 0; i < mcasp->num_serializer; i++) + if (mcasp->serial_dir[i] == TX_MODE) + tx_serializers++; + else if (mcasp->serial_dir[i] == RX_MODE) + rx_serializers++; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK, + tx_serializers); + if (ret) + return ret; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE, + rx_serializers); + + return ret; +} + + +static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(mcasp->dev, + "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + if (tx_mask >= (1<= (1<dev, + "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n", + tx_mask, rx_mask, slots); + return -EINVAL; + } + + if (slot_width && + (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) { + dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n", + __func__, slot_width); + return -EINVAL; + } + + mcasp->tdm_slots = slots; + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask; + mcasp->slot_width = slot_width; + + return davinci_mcasp_set_ch_constraints(mcasp); +} + static int davinci_config_channel_size(struct davinci_mcasp *mcasp, int word_length) { @@ -632,6 +712,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, */ rx_rotate = (slot_length - word_length) / 4; word_length = slot_length; + } else if (mcasp->slot_width) { + rx_rotate = (mcasp->slot_width - word_length) / 4; + word_length = mcasp->slot_width; } /* mapping of the XSSZ bit-field as described in the datasheet */ @@ -777,33 +860,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, /* * If more than one serializer is needed, then use them with - * their specified tdm_slots count. Otherwise, one serializer - * can cope with the transaction using as many slots as channels - * in the stream, requires channels symmetry + * all the specified tdm_slots. Otherwise, one serializer can + * cope with the transaction using just as many slots as there + * are channels in the stream. */ - active_serializers = (channels + total_slots - 1) / total_slots; - if (active_serializers == 1) - active_slots = channels; - else - active_slots = total_slots; - - for (i = 0; i < active_slots; i++) - mask |= (1 << i); + if (mcasp->tdm_mask[stream]) { + active_slots = hweight32(mcasp->tdm_mask[stream]); + active_serializers = (channels + active_slots - 1) / + active_slots; + if (active_serializers == 1) { + active_slots = channels; + for (i = 0; i < total_slots; i++) { + if ((1 << i) & mcasp->tdm_mask[stream]) { + mask |= (1 << i); + if (--active_slots <= 0) + break; + } + } + } + } else { + active_serializers = (channels + total_slots - 1) / total_slots; + if (active_serializers == 1) + active_slots = channels; + else + active_slots = total_slots; + for (i = 0; i < active_slots; i++) + mask |= (1 << i); + } mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); if (!mcasp->dat_port) busel = TXSEL; - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(total_slots), FSXMOD(0x1FF)); - - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(total_slots), FSRMOD(0x1FF)); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(total_slots), FSXMOD(0x1FF)); + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, + FSRMOD(total_slots), FSRMOD(0x1FF)); + } return 0; } @@ -923,6 +1023,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int sbits = params_width(params); int ppm, div; + if (mcasp->slot_width) + sbits = mcasp->slot_width; + div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots, &ppm); if (ppm) @@ -1028,6 +1131,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval range; int i; + if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + snd_interval_any(&range); range.empty = 1; @@ -1070,10 +1176,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { if (snd_mask_test(fmt, i)) { - uint bclk_freq = snd_pcm_format_width(i)*slots*rate; + uint sbits = snd_pcm_format_width(i); int ppm; - davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + + davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate, + &ppm); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { snd_mask_set(&nfmt, i); count++; @@ -1095,6 +1205,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, &mcasp->ruledata[substream->stream]; u32 max_channels = 0; int i, dir; + int tdm_slots = mcasp->tdm_slots; + + if (mcasp->tdm_mask[substream->stream]) + tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); mcasp->substreams[substream->stream] = substream; @@ -1115,7 +1229,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, max_channels++; } ruledata->serializers = max_channels; - max_channels *= mcasp->tdm_slots; + max_channels *= tdm_slots; /* * If the already active stream has less channels than the calculated * limnit based on the seirializers * tdm_slots, we need to use that as @@ -1125,15 +1239,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, */ if (mcasp->channels && mcasp->channels < max_channels) max_channels = mcasp->channels; + /* + * But we can always allow channels upto the amount of + * the available tdm_slots. + */ + if (max_channels < tdm_slots) + max_channels = tdm_slots; snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, max_channels); - if (mcasp->chconstr[substream->stream].count) - snd_pcm_hw_constraint_list(substream->runtime, - 0, SNDRV_PCM_HW_PARAM_CHANNELS, - &mcasp->chconstr[substream->stream]); + snd_pcm_hw_constraint_list(substream->runtime, + 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &mcasp->chconstr[substream->stream]); + + if (mcasp->slot_width) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + 8, mcasp->slot_width); /* * If we rely on implicit BCLK divider setting we should @@ -1185,6 +1309,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { .set_fmt = davinci_mcasp_set_dai_fmt, .set_clkdiv = davinci_mcasp_set_clkdiv, .set_sysclk = davinci_mcasp_set_sysclk, + .set_tdm_slot = davinci_mcasp_set_tdm_slot, }; static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) @@ -1514,59 +1639,6 @@ nodata: return pdata; } -/* All serializers must have equal number of channels */ -static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, - struct snd_pcm_hw_constraint_list *cl, - int serializers) -{ - unsigned int *list; - int i, count = 0; - - if (serializers <= 1) - return 0; - - list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) * - (mcasp->tdm_slots + serializers - 2), - GFP_KERNEL); - if (!list) - return -ENOMEM; - - for (i = 2; i <= mcasp->tdm_slots; i++) - list[count++] = i; - - for (i = 2; i <= serializers; i++) - list[count++] = i*mcasp->tdm_slots; - - cl->count = count; - cl->list = list; - - return 0; -} - - -static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp) -{ - int rx_serializers = 0, tx_serializers = 0, ret, i; - - for (i = 0; i < mcasp->num_serializer; i++) - if (mcasp->serial_dir[i] == TX_MODE) - tx_serializers++; - else if (mcasp->serial_dir[i] == RX_MODE) - rx_serializers++; - - ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ - SNDRV_PCM_STREAM_PLAYBACK], - tx_serializers); - if (ret) - return ret; - - ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ - SNDRV_PCM_STREAM_CAPTURE], - rx_serializers); - - return ret; -} - enum { PCM_EDMA, PCM_SDMA, @@ -1783,7 +1855,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; } - ret = davinci_mcasp_init_ch_constraints(mcasp); + /* Allocate memory for long enough list for all possible + * scenarios. Maximum number tdm slots is 32 and there cannot + * be more serializers than given in the configuration. The + * serializer directions could be taken into account, but it + * would make code much more complex and save only couple of + * bytes. + */ + mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list = + devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (32 + mcasp->num_serializer - 2), + GFP_KERNEL); + + mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list = + devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (32 + mcasp->num_serializer - 2), + GFP_KERNEL); + + if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || + !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) + return -ENOMEM; + + ret = davinci_mcasp_set_ch_constraints(mcasp); if (ret) goto err; -- cgit v1.1 From 6131084a0bc966107021d8c89489f9cd1663b902 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Sep 2015 21:27:43 +0300 Subject: ASoC: simple-card: Add tdm slot mask support to simple-card Adds DT binding for explicitly choosing a tdm mask for DAI and uses it in simple-card. The API for snd_soc_of_parse_tdm_slot() has also been changed. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 8 ++++++-- sound/soc/soc-core.c | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3ff76d4..54c3320 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -151,7 +151,9 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, } if (set->slots) { - ret = snd_soc_dai_set_tdm_slot(dai, 0, 0, + ret = snd_soc_dai_set_tdm_slot(dai, + set->tx_slot_mask, + set->rx_slot_mask, set->slots, set->slot_width); if (ret && ret != -ENOTSUPP) { @@ -243,7 +245,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np, return ret; /* Parse TDM slot */ - ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); + ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask, + &dai->rx_slot_mask, + &dai->slots, &dai->slot_width); if (ret) return ret; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6173d15..c5e21ca 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3291,13 +3291,38 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); +static int snd_soc_of_get_slot_mask(struct device_node *np, + const char *prop_name, + unsigned int *mask) +{ + u32 val; + const u32 *of_slot_mask = of_get_property(np, prop_name, &val); + int i; + + if (!of_slot_mask) + return 0; + val /= sizeof(u32); + for (i = 0; i < val; i++) + if (be32_to_cpup(&of_slot_mask[i])) + *mask |= (1 << i); + + return val; +} + int snd_soc_of_parse_tdm_slot(struct device_node *np, + unsigned int *tx_mask, + unsigned int *rx_mask, unsigned int *slots, unsigned int *slot_width) { u32 val; int ret; + if (tx_mask) + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask); + if (rx_mask) + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); + if (of_property_read_bool(np, "dai-tdm-slot-num")) { ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); if (ret) -- cgit v1.1 From 2fc171e69e0dc0f5cce805ec40923c4e7ff78e94 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 10 Sep 2015 18:01:45 +0530 Subject: ASoC: Intel: remove unused function The function get_current_pipe_id() was not being used. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 683e501..5e9c316 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -368,23 +368,6 @@ static void sst_media_close(struct snd_pcm_substream *substream, kfree(stream); } -static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) -{ - struct sst_data *sst = snd_soc_dai_get_drvdata(dai); - struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; - struct sst_runtime_stream *stream = - substream->runtime->private_data; - u32 str_id = stream->stream_info.str_id; - unsigned int pipe_id; - - pipe_id = map[str_id].device_id; - - dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n", - pipe_id, str_id); - return pipe_id; -} - static int sst_media_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { -- cgit v1.1 From 45fb6b6f2aa3f6b22b81078db0dba4b26c9d0bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Sat, 12 Sep 2015 15:26:24 +0200 Subject: ASoC: sunxi: add support for the on-chip codec on early Allwinner SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sun4i, sun5i and sun7i SoC families have a built-in codec, capable of both audio capture and playback. While this is called a codec by Allwinner, it really is an in-SoC combination of a codec and a DAI, with its own DAC/ADC and amplifiers in a single memory-mapped controller. The capture part has been left out for now, and will be added eventually. Signed-off-by: Emilio López Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/sunxi/Kconfig | 11 + sound/soc/sunxi/Makefile | 2 + sound/soc/sunxi/sun4i-codec.c | 720 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 735 insertions(+) create mode 100644 sound/soc/sunxi/Kconfig create mode 100644 sound/soc/sunxi/Makefile create mode 100644 sound/soc/sunxi/sun4i-codec.c (limited to 'sound') diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 225bfda..7de792b 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -58,6 +58,7 @@ source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sti/Kconfig" +source "sound/soc/sunxi/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 134aca1..af0a571 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sti/ +obj-$(CONFIG_SND_SOC) += sunxi/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig new file mode 100644 index 0000000..84c72ec --- /dev/null +++ b/sound/soc/sunxi/Kconfig @@ -0,0 +1,11 @@ +menu "Allwinner SoC Audio support" + +config SND_SUN4I_CODEC + tristate "Allwinner A10 Codec Support" + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Select Y or M to add support for the Codec embedded in the Allwinner + A10 and affiliated SoCs. + +endmenu diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile new file mode 100644 index 0000000..ea8a08c --- /dev/null +++ b/sound/soc/sunxi/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o + diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c new file mode 100644 index 0000000..6e83e62 --- /dev/null +++ b/sound/soc/sunxi/sun4i-codec.c @@ -0,0 +1,720 @@ +/* + * Copyright 2014 Emilio López + * Copyright 2014 Jon Smirl + * Copyright 2015 Maxime Ripard + * + * Based on the Allwinner SDK driver, released under the GPL. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Codec DAC register offsets and bit fields */ +#define SUN4I_CODEC_DAC_DPC (0x00) +#define SUN4I_CODEC_DAC_DPC_EN_DA (31) +#define SUN4I_CODEC_DAC_DPC_DVOL (12) +#define SUN4I_CODEC_DAC_FIFOC (0x04) +#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29) +#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28) +#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26) +#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24) +#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21) +#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8) +#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6) +#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5) +#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4) +#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) +#define SUN4I_CODEC_DAC_FIFOS (0x08) +#define SUN4I_CODEC_DAC_TXDATA (0x0c) +#define SUN4I_CODEC_DAC_ACTL (0x10) +#define SUN4I_CODEC_DAC_ACTL_DACAENR (31) +#define SUN4I_CODEC_DAC_ACTL_DACAENL (30) +#define SUN4I_CODEC_DAC_ACTL_MIXEN (29) +#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15) +#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14) +#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13) +#define SUN4I_CODEC_DAC_ACTL_DACPAS (8) +#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7) +#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6) +#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0) +#define SUN4I_CODEC_DAC_TUNE (0x14) +#define SUN4I_CODEC_DAC_DEBUG (0x18) + +/* Codec ADC register offsets and bit fields */ +#define SUN4I_CODEC_ADC_FIFOC (0x1c) +#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) +#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24) +#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8) +#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7) +#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6) +#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4) +#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) +#define SUN4I_CODEC_ADC_FIFOS (0x20) +#define SUN4I_CODEC_ADC_RXDATA (0x24) +#define SUN4I_CODEC_ADC_ACTL (0x28) +#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) +#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) +#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29) +#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28) +#define SUN4I_CODEC_ADC_ACTL_VMICEN (27) +#define SUN4I_CODEC_ADC_ACTL_VADCG (20) +#define SUN4I_CODEC_ADC_ACTL_ADCIS (17) +#define SUN4I_CODEC_ADC_ACTL_PA_EN (4) +#define SUN4I_CODEC_ADC_ACTL_DDE (3) +#define SUN4I_CODEC_ADC_DEBUG (0x2c) + +/* Other various ADC registers */ +#define SUN4I_CODEC_DAC_TXCNT (0x30) +#define SUN4I_CODEC_ADC_RXCNT (0x34) +#define SUN4I_CODEC_AC_SYS_VERI (0x38) +#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c) + +struct sun4i_codec { + struct device *dev; + struct regmap *regmap; + struct clk *clk_apb; + struct clk *clk_module; + + struct snd_dmaengine_dai_dma_data playback_dma_data; +}; + +static void sun4i_codec_start_playback(struct sun4i_codec *scodec) +{ + /* + * FIXME: according to the BSP, we might need to drive a PA + * GPIO high here on some boards + */ + + /* Flush TX FIFO */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH), + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); + + /* Enable DAC DRQ */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN), + BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN)); +} + +static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) +{ + /* + * FIXME: according to the BSP, we might need to drive a PA + * GPIO low here on some boards + */ + + /* Disable DAC DRQ */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN), + 0); +} + +static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENOTSUPP; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + sun4i_codec_start_playback(scodec); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + sun4i_codec_stop_playback(scodec); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int sun4i_codec_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + u32 val; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENOTSUPP; + + /* Flush the TX FIFO */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH), + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); + + /* Set TX FIFO Empty Trigger Level */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL, + 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL); + + if (substream->runtime->rate > 32000) + /* Use 64 bits FIR filter */ + val = 0; + else + /* Use 32 bits FIR filter */ + val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION); + + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION), + val); + + /* Send zeros when we have an underrun */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT), + 0); + + return 0; +} + +static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params) +{ + unsigned int rate = params_rate(params); + + switch (rate) { + case 176400: + case 88200: + case 44100: + case 33075: + case 22050: + case 14700: + case 11025: + case 7350: + return 22579200; + + case 192000: + case 96000: + case 48000: + case 32000: + case 24000: + case 16000: + case 12000: + case 8000: + return 24576000; + + default: + return 0; + } +} + +static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params) +{ + unsigned int rate = params_rate(params); + + switch (rate) { + case 192000: + case 176400: + return 6; + + case 96000: + case 88200: + return 7; + + case 48000: + case 44100: + return 0; + + case 32000: + case 33075: + return 1; + + case 24000: + case 22050: + return 2; + + case 16000: + case 14700: + return 3; + + case 12000: + case 11025: + return 4; + + case 8000: + case 7350: + return 5; + + default: + return -EINVAL; + } +} + +static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + unsigned long clk_freq; + int hwrate; + u32 val; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENOTSUPP; + + clk_freq = sun4i_codec_get_mod_freq(params); + if (!clk_freq) + return -EINVAL; + + if (clk_set_rate(scodec->clk_module, clk_freq)) + return -EINVAL; + + hwrate = sun4i_codec_get_hw_rate(params); + if (hwrate < 0) + return hwrate; + + /* Set DAC sample rate */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS, + hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS); + + /* Set the number of channels we want to use */ + if (params_channels(params) == 1) + val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN); + else + val = 0; + + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN), + val); + + /* Set the number of sample bits to either 16 or 24 bits */ + if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS), + BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS)); + + /* Set TX FIFO mode to padding the LSBs with 0 */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE), + 0); + + scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + } else { + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS), + 0); + + /* Set TX FIFO mode to repeat the MSB */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE), + BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE)); + + scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + } + + return 0; +} + +static int sun4i_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + + /* + * Stop issuing DRQ when we have room for less than 16 samples + * in our TX FIFO + */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT, + 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT); + + return clk_prepare_enable(scodec->clk_module); +} + +static void sun4i_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(scodec->clk_module); +} + +static const struct snd_soc_dai_ops sun4i_codec_dai_ops = { + .startup = sun4i_codec_startup, + .shutdown = sun4i_codec_shutdown, + .trigger = sun4i_codec_trigger, + .hw_params = sun4i_codec_hw_params, + .prepare = sun4i_codec_prepare, +}; + +static struct snd_soc_dai_driver sun4i_codec_dai = { + .name = "Codec", + .ops = &sun4i_codec_dai_ops, + .playback = { + .stream_name = "Codec Playback", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, +}; + +/*** Codec ***/ +static const struct snd_kcontrol_new sun4i_codec_pa_mute = + SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); + +static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); + +static const struct snd_kcontrol_new sun4i_codec_widgets[] = { + SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, + sun4i_codec_pa_volume_scale), +}; + +static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0), +}; + +static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = { + SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0), + SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0), +}; + +static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0), + SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0), +}; + +static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = { + /* Digital parts of the DACs */ + SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_EN_DA, 0, + NULL, 0), + + /* Analog parts of the DACs */ + SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_DACAENL, 0), + SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_DACAENR, 0), + + /* Mixers */ + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + sun4i_codec_left_mixer_controls, + ARRAY_SIZE(sun4i_codec_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + sun4i_codec_right_mixer_controls, + ARRAY_SIZE(sun4i_codec_right_mixer_controls)), + + /* Global Mixer Enable */ + SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0), + + /* Pre-Amplifier */ + SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_PA_EN, 0, + sun4i_codec_pa_mixer_controls, + ARRAY_SIZE(sun4i_codec_pa_mixer_controls)), + SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0, + &sun4i_codec_pa_mute), + + SND_SOC_DAPM_OUTPUT("HP Right"), + SND_SOC_DAPM_OUTPUT("HP Left"), +}; + +static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = { + /* Left DAC Routes */ + { "Left DAC", NULL, "DAC" }, + + /* Right DAC Routes */ + { "Right DAC", NULL, "DAC" }, + + /* Right Mixer Routes */ + { "Right Mixer", NULL, "Mixer Enable" }, + { "Right Mixer", "Left DAC Playback Switch", "Left DAC" }, + { "Right Mixer", "Right DAC Playback Switch", "Right DAC" }, + + /* Left Mixer Routes */ + { "Left Mixer", NULL, "Mixer Enable" }, + { "Left Mixer", "Left DAC Playback Switch", "Left DAC" }, + + /* Pre-Amplifier Mixer Routes */ + { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" }, + { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" }, + { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" }, + { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" }, + + /* PA -> HP path */ + { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" }, + { "HP Right", NULL, "Pre-Amplifier Mute" }, + { "HP Left", NULL, "Pre-Amplifier Mute" }, +}; + +static struct snd_soc_codec_driver sun4i_codec_codec = { + .controls = sun4i_codec_widgets, + .num_controls = ARRAY_SIZE(sun4i_codec_widgets), + .dapm_widgets = sun4i_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets), + .dapm_routes = sun4i_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes), +}; + +static const struct snd_soc_component_driver sun4i_codec_component = { + .name = "sun4i-codec", +}; + +#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_8000_192000 +#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static int sun4i_codec_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); + + snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data, + NULL); + + return 0; +} + +static struct snd_soc_dai_driver dummy_cpu_dai = { + .name = "sun4i-codec-cpu-dai", + .probe = sun4i_codec_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SUN4I_CODEC_RATES, + .formats = SUN4I_CODEC_FORMATS, + .sig_bits = 24, + }, +}; + +static const struct regmap_config sun4i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL, +}; + +static const struct of_device_id sun4i_codec_of_match[] = { + { .compatible = "allwinner,sun4i-a10-codec" }, + { .compatible = "allwinner,sun7i-a20-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); + +static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, + int *num_links) +{ + struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link), + GFP_KERNEL); + if (!link) + return NULL; + + link->name = "cdc"; + link->stream_name = "CDC PCM"; + link->codec_dai_name = "Codec"; + link->cpu_dai_name = dev_name(dev); + link->codec_name = dev_name(dev); + link->platform_name = dev_name(dev); + link->dai_fmt = SND_SOC_DAIFMT_I2S; + + *num_links = 1; + + return link; +}; + +static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return NULL; + + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); + if (!card->dai_link) + return NULL; + + card->dev = dev; + card->name = "sun4i-codec"; + + ret = snd_soc_of_parse_audio_routing(card, "routing"); + if (ret) { + dev_err(dev, "Failed to create our audio routing\n"); + return NULL; + } + + return card; +}; + +static int sun4i_codec_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct sun4i_codec *scodec; + struct resource *res; + void __iomem *base; + int ret; + + scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL); + if (!scodec) + return -ENOMEM; + + scodec->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Failed to map the registers\n"); + return PTR_ERR(base); + } + + scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_codec_regmap_config); + if (IS_ERR(scodec->regmap)) { + dev_err(&pdev->dev, "Failed to create our regmap\n"); + return PTR_ERR(scodec->regmap); + } + + /* Get the clocks from the DT */ + scodec->clk_apb = devm_clk_get(&pdev->dev, "apb"); + if (IS_ERR(scodec->clk_apb)) { + dev_err(&pdev->dev, "Failed to get the APB clock\n"); + return PTR_ERR(scodec->clk_apb); + } + + scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); + if (IS_ERR(scodec->clk_module)) { + dev_err(&pdev->dev, "Failed to get the module clock\n"); + return PTR_ERR(scodec->clk_module); + } + + /* Enable the bus clock */ + if (clk_prepare_enable(scodec->clk_apb)) { + dev_err(&pdev->dev, "Failed to enable the APB clock\n"); + return -EINVAL; + } + + /* DMA configuration for TX FIFO */ + scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA; + scodec->playback_dma_data.maxburst = 4; + scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec, + &sun4i_codec_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register our codec\n"); + goto err_clk_disable; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &sun4i_codec_component, + &dummy_cpu_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register our DAI\n"); + goto err_unregister_codec; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); + goto err_unregister_codec; + } + + card = sun4i_codec_create_card(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "Failed to create our card\n"); + goto err_unregister_codec; + } + + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, scodec); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "Failed to register our card\n"); + goto err_unregister_codec; + } + + return 0; + +err_unregister_codec: + snd_soc_unregister_codec(&pdev->dev); +err_clk_disable: + clk_disable_unprepare(scodec->clk_apb); + return ret; +} + +static int sun4i_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + snd_soc_unregister_codec(&pdev->dev); + clk_disable_unprepare(scodec->clk_apb); + + return 0; +} + +static struct platform_driver sun4i_codec_driver = { + .driver = { + .name = "sun4i-codec", + .owner = THIS_MODULE, + .of_match_table = sun4i_codec_of_match, + }, + .probe = sun4i_codec_probe, + .remove = sun4i_codec_remove, +}; +module_platform_driver(sun4i_codec_driver); + +MODULE_DESCRIPTION("Allwinner A10 codec driver"); +MODULE_AUTHOR("Emilio López "); +MODULE_AUTHOR("Jon Smirl "); +MODULE_AUTHOR("Maxime Ripard "); +MODULE_LICENSE("GPL"); -- cgit v1.1 From e2f8445176c9e6e6b152b792d08abca8308bb8ff Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 02:41:05 +0000 Subject: ASoC: rsnd: SND_SOC_RCAR depends on COMMON_CLK 2a46db4a3("ASoC: rsnd: add AUDIO_CLKOUT support") uses of_clk_add_provider() which is requesting struct clk_onecell_data. But it is COMMON_CLK feature. SND_SOC_RCAR depends on COMMON_CLK This patch also solved compile error of 7486d80f7("ASoC: rsnd: remove unneeded sh_clk header") Reported-by: kbuild test robot Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 07114b0..6ca90aa 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" depends on DMA_OF + depends on COMMON_CLK select SND_SIMPLE_CARD select REGMAP_MMIO help -- cgit v1.1 From 544637bf3f4ddc1971740026e1cf58d8516b53fa Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 02:42:42 +0000 Subject: ASoC: ak4642: exchange macro name to avoid redefinition 71a0138ab("ASoC: ak4642: enable to use MCKO as fixed rate output pin on DT") added new FS() macro, but x86 already has it in arch/x86/include/uapi/asm/ptrace-abi.h This patch exchange FS() to FSs() to avoid redefinition warning Reported-by: kbuild test robot Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4642.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index b5c4981..cda27c2 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -130,8 +130,8 @@ #define I2S (3 << 0) /* MD_CTL2 */ -#define FS(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) -#define PS(val) ((val & 0x3) << 6) +#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) +#define PSs(val) ((val & 0x3) << 6) /* MD_CTL3 */ #define BST1 (1 << 3) @@ -458,7 +458,8 @@ static int ak4642_set_mcko(struct snd_soc_codec *codec, for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) { for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) { if (frequency == ps_list[ps] * fs_list[fs]) { - snd_soc_write(codec, MD_CTL2, PS(ps) | FS(fs)); + snd_soc_write(codec, MD_CTL2, + PSs(ps) | FSs(fs)); return 0; } } -- cgit v1.1 From 462c30bc8b3457e2904c45babf4cb06c3b38ed4e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 02:44:37 +0000 Subject: ASoC: rsnd: fixup clkout_name[] index error for single AUDIO_CLKOUT SoC 2a46db4a3("ASoC: rsnd: add AUDIO_CLKOUT support") added AUDIO_CLKOUT support for ADG. But single AUDIO_CLKOUT needs clkout_name[CLKOUT], not clkout_name[i]. Kernel will have NULL pointer access without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 3fecb87..c4ebbb7 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -532,7 +532,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, * for clkout */ if (!count) { - clk = clk_register_fixed_rate(dev, clkout_name[i], + clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT], parent_clk_name, (parent_clk_name) ? 0 : CLK_IS_ROOT, req_rate); -- cgit v1.1 From 3c8f7710c1c44fb650bc29b6ef78ed8b60cfaa28 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 15 Sep 2015 20:51:31 +0200 Subject: ASoC: fix broken pxa SoC support The previous fix of pxa library support, which was introduced to fix the library dependency, broke the previous SoC behavior, where a machine code binding pxa2xx-ac97 with a coded relied on : - sound/soc/pxa/pxa2xx-ac97.c - sound/soc/codecs/XXX.c For example, the mioa701_wm9713.c machine code is currently broken. The "select ARM" statement wrongly selects the soc/arm/pxa2xx-ac97 for compilation, as per an unfortunate fate SND_PXA2XX_AC97 is both declared in sound/arm/Kconfig and sound/soc/pxa/Kconfig. Fix this by ensuring that SND_PXA2XX_SOC correctly triggers the correct pxa2xx-ac97 compilation. Fixes: 846172dfe33c ("ASoC: fix SND_PXA2XX_LIB Kconfig warning") Signed-off-by: Robert Jarzmik Signed-off-by: Mark Brown --- sound/arm/Kconfig | 15 ++++++++------- sound/soc/pxa/Kconfig | 2 -- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 885683a..e040621 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -9,6 +9,14 @@ menuconfig SND_ARM Drivers that are implemented on ASoC can be found in "ALSA for SoC audio support" section. +config SND_PXA2XX_LIB + tristate + select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 + select SND_DMAENGINE_PCM + +config SND_PXA2XX_LIB_AC97 + bool + if SND_ARM config SND_ARMAACI @@ -21,13 +29,6 @@ config SND_PXA2XX_PCM tristate select SND_PCM -config SND_PXA2XX_LIB - tristate - select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 - -config SND_PXA2XX_LIB_AC97 - bool - config SND_PXA2XX_AC97 tristate "AC97 driver for the Intel PXA2xx chip" depends on ARCH_PXA diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 39cea80..f2bf866 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,7 +1,6 @@ config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" depends on ARCH_PXA - select SND_ARM select SND_PXA2XX_LIB help Say Y or M if you want to add support for codecs attached to @@ -25,7 +24,6 @@ config SND_PXA2XX_AC97 config SND_PXA2XX_SOC_AC97 tristate select AC97_BUS - select SND_ARM select SND_PXA2XX_LIB_AC97 select SND_SOC_AC97_BUS -- cgit v1.1 From bee261b8964d7751fdd63d7f636c42741294a30c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Sep 2015 13:59:40 +0100 Subject: ASoC: arizona: Add default cases for event switches Since the addition of the WILL_PMU / WILL_PMD several of the switches in arizona.c no longer cover all cases or have a default case. Whilst this isn't causing any problems in the interests of robustness add default cases. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8a2221a..1bc19f1 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, 0x4f5, 0x0da); } break; + default: + break; } return 0; @@ -725,6 +727,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES); if (reg == 0) arizona_in_set_vu(codec, 0); + break; + default: + break; } return 0; @@ -806,6 +811,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, break; } break; + default: + break; } return 0; -- cgit v1.1 From 002b083b8da96fd6c546fab2608803e7ce47627d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Sep 2015 13:59:41 +0100 Subject: ASoC: arizona: Add utility function to check if an input is analog We will occasionally require to take different action based on if an input is analog or digital so add a helper function to return if an input is analog. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 9 +++++++++ sound/soc/codecs/arizona.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 1bc19f1..ac21b85 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -691,6 +691,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena) ARIZONA_IN_VU, val); } +bool arizona_input_analog(struct snd_soc_codec *codec, int shift) +{ + unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8); + unsigned int val = snd_soc_read(codec, reg); + + return !(val & ARIZONA_IN1_MODE_MASK); +} +EXPORT_SYMBOL_GPL(arizona_input_analog); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index ada0a41..7b68d05 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -294,4 +294,6 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai); int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff); +extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift); + #endif -- cgit v1.1 From ccaadda319ffa42385d8eabde0efc3c544bebce3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 17 Sep 2015 10:45:24 +0100 Subject: ASoC: wm5110: Add additional analogue input enable for early revs Earlier revisions of the wm5110/8280 silicon require a slightly more complex procedure to enable analogue inputs. This patch adds this into the driver. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5110.c | 187 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 163 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 9756578..c04c0bc 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -38,6 +38,12 @@ struct wm5110_priv { struct arizona_priv core; struct arizona_fll fll[2]; + + unsigned int in_value; + int in_pre_pending; + int in_post_pending; + + unsigned int in_pga_cache[6]; }; static const struct wm_adsp_region wm5110_dsp1_regions[] = { @@ -428,6 +434,127 @@ err: return ret; } +static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = dapm->card; + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_get_volsw_range(kcontrol, ucontrol); + + mutex_unlock(&card->dapm_mutex); + + return ret; +} + +static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = dapm->card; + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_put_volsw_range(kcontrol, ucontrol); + + mutex_unlock(&card->dapm_mutex); + + return ret; +} + +static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + unsigned int reg, mask; + struct reg_sequence analog_seq[] = { + { 0x80, 0x3 }, + { 0x35d, 0 }, + { 0x80, 0x0 }, + }; + + reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4); + mask = ARIZONA_IN1L_PGA_VOL_MASK; + + switch (event) { + case SND_SOC_DAPM_WILL_PMU: + wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2); + wm5110->in_pre_pending++; + wm5110->in_post_pending++; + return 0; + case SND_SOC_DAPM_PRE_PMU: + wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg); + + snd_soc_update_bits(codec, reg, mask, + 0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT); + + wm5110->in_pre_pending--; + if (wm5110->in_pre_pending == 0) { + analog_seq[1].def = wm5110->in_value; + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + + msleep(55); + + wm5110->in_value = 0; + } + + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, reg, mask, + wm5110->in_pga_cache[w->shift]); + + wm5110->in_post_pending--; + if (wm5110->in_post_pending == 0) + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + break; + default: + break; + } + + return 0; +} + +static int wm5110_in_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + + switch (arizona->rev) { + case 0 ... 4: + if (arizona_input_analog(codec, w->shift)) + wm5110_in_analog_ev(w, kcontrol, event); + + break; + default: + break; + } + + return arizona_in_ev(w, kcontrol, event); +} + static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); @@ -454,18 +581,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]), SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]), -SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, - ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, - ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, - ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), @@ -896,29 +1029,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT, 0, NULL, 0, arizona_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | -- cgit v1.1 From 6c84e5917f83334f9196a50cfa31e72207b1a7f6 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 17 Sep 2015 13:13:38 +0300 Subject: ASoC: soc-core: Fix sparse warning in be32_to_cpup() call Store return value of of_get_property() to a pointer of __be32 type as device tree has big endian type. This fixes a sparse warning couple of lines later when be32_to_cpup() is used to convert from big endian to cpu endian. The whole conversion is not really necessary, as we are only checking if the value is zero or not, but I wanted to add it to remind in the future that the data has to be converted before use. Compiler should optimize the unnecessary operations away. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c5e21ca..3b471f9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3296,7 +3296,7 @@ static int snd_soc_of_get_slot_mask(struct device_node *np, unsigned int *mask) { u32 val; - const u32 *of_slot_mask = of_get_property(np, prop_name, &val); + const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); int i; if (!of_slot_mask) -- cgit v1.1 From 8985729578cb42f9e781a8e38e5b6b1ee90c1018 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 17 Sep 2015 10:47:33 +0200 Subject: ASoC: qcom: change PTR_ERR argument Apply PTR_ERR to the value that was recently assigned. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,y; @@ if (IS_ERR(x) || ...) { ... when any when != IS_ERR(...) ( PTR_ERR(x) | * PTR_ERR(y) ) ... when any } // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 97bc202..e5101e0 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -438,7 +438,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n", - __func__, PTR_ERR(drvdata->mi2s_bit_clk[i])); + __func__, + PTR_ERR(drvdata->mi2s_bit_clk[dai_id])); return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]); } } -- cgit v1.1 From 14a998be08e5286741021d3c81cc81e8d6d8a270 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 17 Sep 2015 10:39:05 +0300 Subject: ASoC: davinci-mcasp: Get rid of bclk_lrclk_ratio in private data The slot_width is for essentially same thing. Instead of storing bclk_lrclk_ratio, just store the slot_width. Comments has been updated accordingly and some variable names changed to more descriptive. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 56 ++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index fa47a39..452f2a3 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -87,7 +87,6 @@ struct davinci_mcasp { u8 *serial_dir; u8 version; u8 bclk_div; - u16 bclk_lrclk_ratio; int streams; u32 irq_request[2]; int dma_request[2]; @@ -558,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, mcasp->bclk_div = div; break; - case 2: /* BCLK/LRCLK ratio */ - mcasp->bclk_lrclk_ratio = div; + case 2: /* + * BCLK/LRCLK ratio descries how many bit-clock cycles + * fit into one frame. The clock ratio is given for a + * full period of data (for I2S format both left and + * right channels), so it has to be divided by number + * of tdm-slots (for I2S - divided by 2). + * Instead of storing this ratio, we calculate a new + * tdm_slot width by dividing the the ratio by the + * number of configured tdm slots. + */ + mcasp->slot_width = div / mcasp->tdm_slots; + if (div % mcasp->tdm_slots) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots", + __func__, div, mcasp->tdm_slots); break; default: @@ -677,11 +689,13 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, } static int davinci_config_channel_size(struct davinci_mcasp *mcasp, - int word_length) + int sample_width) { u32 fmt; - u32 tx_rotate = (word_length / 4) & 0x7; - u32 mask = (1ULL << word_length) - 1; + u32 tx_rotate = (sample_width / 4) & 0x7; + u32 mask = (1ULL << sample_width) - 1; + u32 slot_width = sample_width; + /* * For captured data we should not rotate, inversion and masking is * enoguh to get the data to the right position: @@ -694,31 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, u32 rx_rotate = 0; /* - * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv() - * callback, take it into account here. That allows us to for example - * send 32 bits per channel to the codec, while only 16 of them carry - * audio payload. - * The clock ratio is given for a full period of data (for I2S format - * both left and right channels), so it has to be divided by number of - * tdm-slots (for I2S - divided by 2). + * Setting the tdm slot width either with set_clkdiv() or + * set_tdm_slot() allows us to for example send 32 bits per + * channel to the codec, while only 16 of them carry audio + * payload. */ - if (mcasp->bclk_lrclk_ratio) { - u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; - + if (mcasp->slot_width) { /* - * When we have more bclk then it is needed for the data, we - * need to use the rotation to move the received samples to have - * correct alignment. + * When we have more bclk then it is needed for the + * data, we need to use the rotation to move the + * received samples to have correct alignment. */ - rx_rotate = (slot_length - word_length) / 4; - word_length = slot_length; - } else if (mcasp->slot_width) { - rx_rotate = (mcasp->slot_width - word_length) / 4; - word_length = mcasp->slot_width; + slot_width = mcasp->slot_width; + rx_rotate = (slot_width - sample_width) / 4; } /* mapping of the XSSZ bit-field as described in the datasheet */ - fmt = (word_length >> 1) - 1; + fmt = (slot_width >> 1) - 1; if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), -- cgit v1.1 From 31f4f0c640bd2d144ab16666c76d45f173f952f5 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Thu, 17 Sep 2015 04:55:55 +0800 Subject: ASoC: sunxi: fix platform_no_drv_owner.cocci warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/soc/sunxi/sun4i-codec.c:708:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Emilio López Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 6e83e62..8d59d83 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -705,7 +705,6 @@ static int sun4i_codec_remove(struct platform_device *pdev) static struct platform_driver sun4i_codec_driver = { .driver = { .name = "sun4i-codec", - .owner = THIS_MODULE, .of_match_table = sun4i_codec_of_match, }, .probe = sun4i_codec_probe, -- cgit v1.1 From b07570628471777aabb5695284e1af4533e502da Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 08:26:36 +0000 Subject: ASoC: add ak4613 support Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ak4613.c | 469 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 476 insertions(+) create mode 100644 sound/soc/codecs/ak4613.c (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733e..a92e4d4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C select SND_SOC_AK4554 + select SND_SOC_AK4613 if I2C select SND_SOC_AK4641 if I2C select SND_SOC_AK4642 if I2C select SND_SOC_AK4671 if I2C @@ -319,6 +320,10 @@ config SND_SOC_AK4535 config SND_SOC_AK4554 tristate "AKM AK4554 CODEC" +config SND_SOC_AK4613 + tristate "AKM AK4613 CODEC" + depends on I2C + config SND_SOC_AK4641 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077..5b6c8af 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o +snd-soc-ak4613-objs := ak4613.o snd-soc-ak4641-objs := ak4641.o snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o @@ -216,6 +217,7 @@ obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o +obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c new file mode 100644 index 0000000..fd96a8f --- /dev/null +++ b/sound/soc/codecs/ak4613.c @@ -0,0 +1,469 @@ +/* + * ak4613.c -- Asahi Kasei ALSA Soc Audio driver + * + * Copyright (C) 2015 Renesas Electronics Corporation + * Kuninori Morimoto + * + * Based on ak4642.c by Kuninori Morimoto + * Based on wm8731.c by Richard Purdie + * Based on ak4535.c by Richard Purdie + * Based on wm8753.c by Liam Girdwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PW_MGMT1 0x00 /* Power Management 1 */ +#define PW_MGMT2 0x01 /* Power Management 2 */ +#define PW_MGMT3 0x02 /* Power Management 3 */ +#define CTRL1 0x03 /* Control 1 */ +#define CTRL2 0x04 /* Control 2 */ +#define DEMP1 0x05 /* De-emphasis1 */ +#define DEMP2 0x06 /* De-emphasis2 */ +#define OFD 0x07 /* Overflow Detect */ +#define ZRD 0x08 /* Zero Detect */ +#define ICTRL 0x09 /* Input Control */ +#define OCTRL 0x0a /* Output Control */ +#define LOUT1 0x0b /* LOUT1 Volume Control */ +#define ROUT1 0x0c /* ROUT1 Volume Control */ +#define LOUT2 0x0d /* LOUT2 Volume Control */ +#define ROUT2 0x0e /* ROUT2 Volume Control */ +#define LOUT3 0x0f /* LOUT3 Volume Control */ +#define ROUT3 0x10 /* ROUT3 Volume Control */ +#define LOUT4 0x11 /* LOUT4 Volume Control */ +#define ROUT4 0x12 /* ROUT4 Volume Control */ +#define LOUT5 0x13 /* LOUT5 Volume Control */ +#define ROUT5 0x14 /* ROUT5 Volume Control */ +#define LOUT6 0x15 /* LOUT6 Volume Control */ +#define ROUT6 0x16 /* ROUT6 Volume Control */ + +/* PW_MGMT1 */ +#define RSTN BIT(0) +#define PMDAC BIT(1) +#define PMADC BIT(2) +#define PMVR BIT(3) + +/* PW_MGMT2 */ +#define PMAD_ALL 0x7 + +/* PW_MGMT3 */ +#define PMDA_ALL 0x3f + +/* CTRL1 */ +#define DIF0 BIT(3) +#define DIF1 BIT(4) +#define DIF2 BIT(5) +#define TDM0 BIT(6) +#define TDM1 BIT(7) +#define NO_FMT (0xff) +#define FMT_MASK (0xf8) + +/* CTRL2 */ +#define DFS_NORMAL_SPEED (0 << 2) +#define DFS_DOUBLE_SPEED (1 << 2) +#define DFS_QUAD_SPEED (2 << 2) + +struct ak4613_priv { + struct mutex lock; + + unsigned int fmt; + u8 fmt_ctrl; + int cnt; +}; + +struct ak4613_formats { + unsigned int width; + unsigned int fmt; +}; + +struct ak4613_interface { + struct ak4613_formats capture; + struct ak4613_formats playback; +}; + +static const struct reg_default ak4613_reg[] = { + { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 }, + { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 }, + { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 }, + { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 }, + { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 }, + { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, +}; + +#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3) +#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } +static const struct ak4613_interface ak4613_iface[] = { + /* capture */ /* playback */ + [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) }, + [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) }, + [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) }, + [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) }, + [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) }, +}; + +static const struct regmap_config ak4613_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x16, + .reg_defaults = ak4613_reg, + .num_reg_defaults = ARRAY_SIZE(ak4613_reg), +}; + +static const struct of_device_id ak4613_of_match[] = { + { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4613_of_match); + +static const struct i2c_device_id ak4613_i2c_id[] = { + { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id); + +static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = { + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("LOUT4"), + SND_SOC_DAPM_OUTPUT("LOUT5"), + SND_SOC_DAPM_OUTPUT("LOUT6"), + + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("ROUT3"), + SND_SOC_DAPM_OUTPUT("ROUT4"), + SND_SOC_DAPM_OUTPUT("ROUT5"), + SND_SOC_DAPM_OUTPUT("ROUT6"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0), + SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0), + SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0), + SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0), + SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0), +}; + +static const struct snd_soc_dapm_route ak4613_intercon[] = { + {"LOUT1", NULL, "DAC1"}, + {"LOUT2", NULL, "DAC2"}, + {"LOUT3", NULL, "DAC3"}, + {"LOUT4", NULL, "DAC4"}, + {"LOUT5", NULL, "DAC5"}, + {"LOUT6", NULL, "DAC6"}, + + {"ROUT1", NULL, "DAC1"}, + {"ROUT2", NULL, "DAC2"}, + {"ROUT3", NULL, "DAC3"}, + {"ROUT4", NULL, "DAC4"}, + {"ROUT5", NULL, "DAC5"}, + {"ROUT6", NULL, "DAC6"}, + + {"DAC1", NULL, "Playback"}, + {"DAC2", NULL, "Playback"}, + {"DAC3", NULL, "Playback"}, + {"DAC4", NULL, "Playback"}, + {"DAC5", NULL, "Playback"}, + {"DAC6", NULL, "Playback"}, + + {"Capture", NULL, "ADC1"}, + {"Capture", NULL, "ADC2"}, + + {"ADC1", NULL, "LIN1"}, + {"ADC2", NULL, "LIN2"}, + + {"ADC1", NULL, "RIN1"}, + {"ADC2", NULL, "RIN2"}, +}; + +static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + struct device *dev = codec->dev; + + mutex_lock(&priv->lock); + priv->cnt--; + if (priv->cnt < 0) { + dev_err(dev, "unexpected counter error\n"); + priv->cnt = 0; + } + if (!priv->cnt) + priv->fmt_ctrl = NO_FMT; + mutex_unlock(&priv->lock); +} + +static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + fmt &= SND_SOC_DAIFMT_FORMAT_MASK; + + switch (fmt) { + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + priv->fmt = fmt; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + const struct ak4613_formats *fmts; + struct device *dev = codec->dev; + unsigned int width = params_width(params); + unsigned int fmt = priv->fmt; + unsigned int rate; + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int i, ret; + u8 fmt_ctrl, ctrl2; + + rate = params_rate(params); + switch (rate) { + case 32000: + case 44100: + case 48000: + ctrl2 = DFS_NORMAL_SPEED; + break; + case 88200: + case 96000: + ctrl2 = DFS_DOUBLE_SPEED; + break; + case 176400: + case 192000: + ctrl2 = DFS_QUAD_SPEED; + break; + default: + return -EINVAL; + } + + /* + * FIXME + * + * It doesn't support TDM at this point + */ + fmt_ctrl = NO_FMT; + for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) { + fmts = (is_play) ? &ak4613_iface[i].playback : + &ak4613_iface[i].capture; + + if (fmts->fmt != fmt) + continue; + + if (fmt == SND_SOC_DAIFMT_RIGHT_J) { + if (fmts->width != width) + continue; + } else { + if (fmts->width < width) + continue; + } + + fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i); + break; + } + + ret = -EINVAL; + if (fmt_ctrl == NO_FMT) + goto hw_params_end; + + mutex_lock(&priv->lock); + if ((priv->fmt_ctrl == NO_FMT) || + (priv->fmt_ctrl == fmt_ctrl)) { + priv->fmt_ctrl = fmt_ctrl; + priv->cnt++; + ret = 0; + } + mutex_unlock(&priv->lock); + + if (ret < 0) + goto hw_params_end; + + snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); + snd_soc_write(codec, CTRL2, ctrl2); + +hw_params_end: + if (ret < 0) + dev_warn(dev, "unsupported data width/format combination\n"); + + return ret; +} + +static int ak4613_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u8 mgmt1 = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + mgmt1 |= RSTN; + /* fall through */ + case SND_SOC_BIAS_PREPARE: + mgmt1 |= PMADC | PMDAC; + /* fall through */ + case SND_SOC_BIAS_STANDBY: + mgmt1 |= PMVR; + /* fall through */ + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_write(codec, PW_MGMT1, mgmt1); + + return 0; +} + +static const struct snd_soc_dai_ops ak4613_dai_ops = { + .shutdown = ak4613_dai_shutdown, + .set_fmt = ak4613_dai_set_fmt, + .hw_params = ak4613_dai_hw_params, +}; + +#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_64000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4613_dai = { + .name = "ak4613-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .ops = &ak4613_dai_ops, + .symmetric_rates = 1, +}; + +static int ak4613_resume(struct snd_soc_codec *codec) +{ + struct regmap *regmap = dev_get_regmap(codec->dev, NULL); + + regcache_mark_dirty(regmap); + return regcache_sync(regmap); +} + +static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { + .resume = ak4613_resume, + .set_bias_level = ak4613_set_bias_level, + .dapm_widgets = ak4613_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), + .dapm_routes = ak4613_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), +}; + +static int ak4613_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; + const struct regmap_config *regmap_cfg; + struct regmap *regmap; + struct ak4613_priv *priv; + + regmap_cfg = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4613_of_match, dev); + if (of_id) + regmap_cfg = of_id->data; + } else { + regmap_cfg = (const struct regmap_config *)id->driver_data; + } + + if (!regmap_cfg) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->fmt_ctrl = NO_FMT; + priv->cnt = 0; + + mutex_init(&priv->lock); + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return snd_soc_register_codec(dev, &soc_codec_dev_ak4613, + &ak4613_dai, 1); +} + +static int ak4613_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver ak4613_i2c_driver = { + .driver = { + .name = "ak4613-codec", + .owner = THIS_MODULE, + .of_match_table = ak4613_of_match, + }, + .probe = ak4613_i2c_probe, + .remove = ak4613_i2c_remove, + .id_table = ak4613_i2c_id, +}; + +module_i2c_driver(ak4613_i2c_driver); + +MODULE_DESCRIPTION("Soc AK4613 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From ab1fffe3a73c694d698645451ba61255ec4ba5e6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 18 Sep 2015 15:02:50 +0300 Subject: ASoC: davinci-mcasp: Fix devm_kasprintf format string The '\n' at the end of the format string is not needed. It adds an extra line break when doing cat /proc/interrupts Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 2c5a2c5..7d45d98 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1685,7 +1685,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) irq = platform_get_irq_byname(pdev, "common"); if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common\n", + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common", dev_name(&pdev->dev)); ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_common_irq_handler, @@ -1702,7 +1702,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) irq = platform_get_irq_byname(pdev, "rx"); if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n", + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx", dev_name(&pdev->dev)); ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_rx_irq_handler, @@ -1717,7 +1717,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) irq = platform_get_irq_byname(pdev, "tx"); if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n", + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx", dev_name(&pdev->dev)); ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_tx_irq_handler, -- cgit v1.1 From e3a4d958d78e4f1709f3d4611849617ac9222a0c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 18 Sep 2015 00:52:44 +0000 Subject: ASoC: ak4613: add Digital Playback Volume support For L/ROUT1 to L/ROUT6 Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4613.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index fd96a8f..07a2664 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -22,6 +22,7 @@ #include #include #include +#include #define PW_MGMT1 0x00 /* Power Management 1 */ #define PW_MGMT2 0x01 /* Power Management 2 */ @@ -91,6 +92,31 @@ struct ak4613_interface { struct ak4613_formats playback; }; +/* + * Playback Volume + * + * max : 0x00 : 0 dB + * ( 0.5 dB step ) + * min : 0xFE : -127.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1); + +static const struct snd_kcontrol_new ak4613_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6, + 0, 0xFF, 1, out_tlv), +}; + static const struct reg_default ak4613_reg[] = { { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 }, { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 }, @@ -397,6 +423,8 @@ static int ak4613_resume(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { .resume = ak4613_resume, .set_bias_level = ak4613_set_bias_level, + .controls = ak4613_snd_controls, + .num_controls = ARRAY_SIZE(ak4613_snd_controls), .dapm_widgets = ak4613_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), .dapm_routes = ak4613_intercon, -- cgit v1.1 From 9d70594760a94523c550d2cc0f47b3c0af63c8e9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 18 Sep 2015 15:25:32 +0800 Subject: ASoC: rockchip: Fix missing CLKDEV_LOOKUP dependency Make SND_SOC_ROCKCHIP_MAX98090 and SND_SOC_ROCKCHIP_RT5645 depend on CLKDEV_LOOKUP to fix below build warning: warning: (SND_SOC_ROCKCHIP_MAX98090 && SND_SOC_ROCKCHIP_RT5645) selects SND_SOC_ROCKCHIP_I2S which has unmet direct dependencies (SOUND && !M68K && !UML && SND && SND_SOC && CLKDEV_LOOKUP && SND_SOC_ROCKCHIP) Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/rockchip/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 58bae8e..5709057 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -17,7 +17,7 @@ config SND_SOC_ROCKCHIP_I2S config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98090 select SND_SOC_TS3A227E @@ -27,7 +27,7 @@ config SND_SOC_ROCKCHIP_MAX98090 config SND_SOC_ROCKCHIP_RT5645 tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP select SND_SOC_ROCKCHIP_I2S select SND_SOC_RT5645 help -- cgit v1.1 From ee92cfb030c16ddb01f6543968f13bcb61ed9da5 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 17:19:25 +0800 Subject: ASoC: wm8962: remove 64k sample rate support wm8962 can't support 64k sample rate. When playing a 64KHz wave file, 'Unsupported rate 64000Hz' will be prompted. Signed-off-by: Zidan Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b4eb975..293e47a 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2944,7 +2944,8 @@ static int wm8962_mute(struct snd_soc_dai *dai, int mute) WM8962_DAC_MUTE, val); } -#define WM8962_RATES SNDRV_PCM_RATE_8000_96000 +#define WM8962_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) #define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) -- cgit v1.1 From 8524bb0c7ac688a3cd6ba12dae6104c54d0566b9 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 17:19:43 +0800 Subject: ASoC: wm8960: correct the max register value of mic boost pga the max register value of mic boost pga should be 3. Signed-off-by: Zidan Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 70816f0..d9b43b0 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -234,9 +234,9 @@ SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", - WM8960_RINPATH, 4, 4, 0, micboost_tlv), + WM8960_RINPATH, 4, 3, 0, micboost_tlv), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume", - WM8960_LINPATH, 4, 4, 0, micboost_tlv), + WM8960_LINPATH, 4, 3, 0, micboost_tlv), SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 0, 255, 0, dac_tlv), -- cgit v1.1 From 2ace47be5a315def8f493ca77aa59c077ade30a1 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 18 Sep 2015 16:02:20 +0530 Subject: ASoC: wm0010: fix memory leak We have requested for the firmware but we have missed releasing it both on success and on error path. While checking the code it turned out that the requested firmware is not even used. More over the same firmware is being loaded by wm0010_stage2_load(). Signed-off-by: Sudip Mukherjee Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 9d370a4..7a1bc40 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -577,7 +577,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); unsigned long flags; int ret; - const struct firmware *fw; struct spi_message m; struct spi_transfer t; struct dfw_pllrec pll_rec; @@ -623,14 +622,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) wm0010->state = WM0010_OUT_OF_RESET; spin_unlock_irqrestore(&wm0010->irq_lock, flags); - /* First the bootloader */ - ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev); - if (ret != 0) { - dev_err(codec->dev, "Failed to request stage2 loader: %d\n", - ret); - goto abort; - } - if (!wait_for_completion_timeout(&wm0010->boot_completion, msecs_to_jiffies(20))) dev_err(codec->dev, "Failed to get interrupt from DSP\n"); -- cgit v1.1 From f072f91aa7517386344476813ca0799e08fd0c35 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 18 Sep 2015 16:02:21 +0530 Subject: ASoC: wm0010: fix error path Fix the error path so that we can free the allocated memory on the error path instead of releasing them individually on each error. Signed-off-by: Sudip Mukherjee Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 7a1bc40..76b2f88 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -663,10 +663,8 @@ static int wm0010_boot(struct snd_soc_codec *codec) } img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!img_swap) { - kfree(out); - goto abort; - } + if (!img_swap) + goto abort_out; /* We need to re-order for 0010 */ byte_swap_64((u64 *)&pll_rec, img_swap, len); @@ -681,20 +679,16 @@ static int wm0010_boot(struct snd_soc_codec *codec) spi_message_add_tail(&t, &m); ret = spi_sync(spi, &m); - if (ret != 0) { + if (ret) { dev_err(codec->dev, "First PLL write failed: %d\n", ret); - kfree(img_swap); - kfree(out); - goto abort; + goto abort_swap; } /* Use a second send of the message to get the return status */ ret = spi_sync(spi, &m); - if (ret != 0) { + if (ret) { dev_err(codec->dev, "Second PLL write failed: %d\n", ret); - kfree(img_swap); - kfree(out); - goto abort; + goto abort_swap; } p = (u32 *)out; @@ -727,6 +721,10 @@ static int wm0010_boot(struct snd_soc_codec *codec) return 0; +abort_swap: + kfree(img_swap); +abort_out: + kfree(out); abort: /* Put the chip back into reset */ wm0010_halt(codec); -- cgit v1.1 From 5b97c0f18a1781f50db96baa020f913886d1972a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Mon, 17 Aug 2015 22:56:44 +0530 Subject: ASoC: Intel: Skylake: Remove unused CPU dais We need to create CPU DAI for each endpoint instance. For this we should have one DMIC DAI, one HDA DAI and SSP DAI. Thus, DMIC23, HDA-SPK/AMIC was not required so this patch removes them Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 33 --------------------------------- 1 file changed, 33 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7d617bf..bea2673 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -510,17 +510,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { - .name = "DMIC23 Pin", - .ops = &skl_dmic_dai_ops, - .capture = { - .stream_name = "DMIC23 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, - }, -}, -{ .name = "HD-Codec Pin", .ops = &skl_link_dai_ops, .playback = { @@ -538,28 +527,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }, -{ - .name = "HD-Codec-SPK Pin", - .ops = &skl_link_dai_ops, - .playback = { - .stream_name = "HD-Codec-SPK Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -{ - .name = "HD-Codec-AMIC Pin", - .ops = &skl_link_dai_ops, - .capture = { - .stream_name = "HD-Codec-AMIC Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, }; static int skl_platform_open(struct snd_pcm_substream *substream) -- cgit v1.1 From 9529138276c852297967b5d3cc2f6dda3ddb9526 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Sep 2015 14:06:38 +0300 Subject: ASoC: hdmi: Remove obsolete dummy HDMI codec The hdmi stub codec has not been used since refactoring of OMAP HDMI audio support. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 -- sound/soc/codecs/Makefile | 2 - sound/soc/codecs/hdmi.c | 109 ---------------------------------------------- 3 files changed, 115 deletions(-) delete mode 100644 sound/soc/codecs/hdmi.c (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733e..0142396 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -79,7 +79,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C - select SND_SOC_HDMI_CODEC select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 @@ -442,9 +441,6 @@ config SND_SOC_BT_SCO config SND_SOC_DMIC tristate -config SND_SOC_HDMI_CODEC - tristate "HDMI stub CODEC" - config SND_SOC_ES8328 tristate "Everest Semi ES8328 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077..7d7cc1b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -72,7 +72,6 @@ snd-soc-max98925-objs := max98925.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o -snd-soc-hdmi-codec-objs := hdmi.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o @@ -264,7 +263,6 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o -obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c deleted file mode 100644 index bd42ad3..0000000 --- a/sound/soc/codecs/hdmi.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * ALSA SoC codec driver for HDMI audio codecs. - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Ricardo Neri - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#include -#include -#include -#include - -#define DRV_NAME "hdmi-audio-codec" - -static const struct snd_soc_dapm_widget hdmi_widgets[] = { - SND_SOC_DAPM_INPUT("RX"), - SND_SOC_DAPM_OUTPUT("TX"), -}; - -static const struct snd_soc_dapm_route hdmi_routes[] = { - { "Capture", NULL, "RX" }, - { "TX", NULL, "Playback" }, -}; - -static struct snd_soc_dai_driver hdmi_codec_dai = { - .name = "hdmi-hifi", - .playback = { - .stream_name = "Playback", - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, - .sig_bits = 24, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - -}; - -#ifdef CONFIG_OF -static const struct of_device_id hdmi_audio_codec_ids[] = { - { .compatible = "linux,hdmi-audio", }, - { } -}; -MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids); -#endif - -static struct snd_soc_codec_driver hdmi_codec = { - .dapm_widgets = hdmi_widgets, - .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), - .dapm_routes = hdmi_routes, - .num_dapm_routes = ARRAY_SIZE(hdmi_routes), - .ignore_pmdown_time = true, -}; - -static int hdmi_codec_probe(struct platform_device *pdev) -{ - return snd_soc_register_codec(&pdev->dev, &hdmi_codec, - &hdmi_codec_dai, 1); -} - -static int hdmi_codec_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); - return 0; -} - -static struct platform_driver hdmi_codec_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(hdmi_audio_codec_ids), - }, - - .probe = hdmi_codec_probe, - .remove = hdmi_codec_remove, -}; - -module_platform_driver(hdmi_codec_driver); - -MODULE_AUTHOR("Ricardo Neri "); -MODULE_DESCRIPTION("ASoC generic HDMI codec driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.1 From cde79035c6cf578dd33dfea3e39666efc27cbcf2 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 24 Aug 2015 14:16:51 +0200 Subject: ASoC: Handle multiple codecs with split playback / capture Add the capability to use multiple codecs on the same DAI linke where one codec is used for playback and another one is used for capture. Tested on a setup using an SSM2518 for playback and an ICS43432 I2S MEMS microphone for capture. No verification is made that the playback and capture codec setups are compatible in terms of number of TDM slots (where applicable). Signed-off-by: Ricard Wanderlof Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 70e4b9d..3173958 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -34,6 +34,24 @@ #define DPCM_MAX_BE_USERS 8 +/* + * snd_soc_dai_stream_valid() - check if a DAI supports the given stream + * + * Returns true if the DAI supports the indicated stream type. + */ +static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_pcm_stream *codec_stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &dai->driver->playback; + else + codec_stream = &dai->driver->capture; + + /* If the codec specifies any rate at all, it supports the stream. */ + return codec_stream->rates; +} + /** * snd_soc_runtime_activate() - Increment active count for PCM runtime components * @rtd: ASoC PCM runtime that is activated @@ -371,6 +389,20 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) /* first calculate min/max only for CODECs in the DAI link */ for (i = 0; i < rtd->num_codecs; i++) { + + /* + * Skip CODECs which don't support the current stream type. + * Otherwise, since the rate, channel, and format values will + * zero in that case, we would have no usable settings left, + * causing the resulting setup to fail. + * At least one CODEC should match, otherwise we should have + * bailed out on a higher level, since there would be no + * CODEC to support the transfer direction in that case. + */ + if (!snd_soc_dai_stream_valid(rtd->codec_dais[i], + substream->stream)) + continue; + codec_dai_drv = rtd->codec_dais[i]->driver; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) codec_stream = &codec_dai_drv->playback; @@ -827,6 +859,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; struct snd_pcm_hw_params codec_params; + /* + * Skip CODECs which don't support the current stream type, + * the idea being that if a CODEC is not used for the currently + * set up transfer direction, it should not need to be + * configured, especially since the configuration used might + * not even be supported by that CODEC. There may be cases + * however where a CODEC needs to be set up although it is + * actually not being used for the transfer, e.g. if a + * capture-only CODEC is acting as an LRCLK and/or BCLK master + * for the DAI link including a playback-only CODEC. + * If this becomes necessary, we will have to augment the + * machine driver setup with information on how to act, so + * we can do the right thing here. + */ + if (!snd_soc_dai_stream_valid(codec_dai, substream->stream)) + continue; + /* copy params for each codec */ codec_params = *params; -- cgit v1.1 From 5b64c173cdea21105eb4794487b3d593f0a2e6c3 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 16 Sep 2015 10:13:19 +0100 Subject: ASoC: fsl_ssi: Fix checking of dai format for AC97 mode Current code incorrectly treats dai format for AC97 as bit mask whereas it's actually an integer value. This causes DAI formats other than AC97 (e.g. DSP_B) to trigger AC97 related code, which is incorrect and breaks functionality. This patch fixes the code to correctly compare values to determine AC97 or not. Signed-off-by: Adam Thomson Acked-by: Timur Tabi Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8ec6fb2..37c5cd4 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -249,7 +249,8 @@ MODULE_DEVICE_TABLE(of, fsl_ssi_ids); static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private) { - return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97); + return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == + SND_SOC_DAIFMT_AC97; } static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private) @@ -947,7 +948,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, CCSR_SSI_SCR_TCH_EN); } - if (fmt & SND_SOC_DAIFMT_AC97) + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) fsl_ssi_setup_ac97(ssi_private); return 0; -- cgit v1.1 From e9159e7577cfd1ac8b3c5ea8916ad37bcd87e629 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 21 Sep 2015 14:12:00 +0800 Subject: ASoC: rt5645: Add dmi for Broadwell Add DMI data for Buddy project. Signed-off-by: Bard Liao Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index dbc1d76..e830688 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3209,6 +3209,31 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { { } }; +static struct rt5645_platform_data buddy_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 3, +}; + +static int buddy_quirk_cb(const struct dmi_system_id *id) +{ + rt5645_pdata = &buddy_platform_data; + + return 1; +} + +static struct dmi_system_id dmi_platform_intel_broadwell[] __initdata = { + { + .ident = "Chrome Buddy", + .callback = buddy_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"), + }, + }, + { } +}; + + static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) { rt5645->pdata.in2_diff = device_property_read_bool(dev, @@ -3241,7 +3266,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (pdata) rt5645->pdata = *pdata; - else if (dmi_check_system(dmi_platform_intel_braswell)) + else if (dmi_check_system(dmi_platform_intel_braswell) || + dmi_check_system(dmi_platform_intel_broadwell)) rt5645->pdata = *rt5645_pdata; else rt5645_parse_dt(rt5645, &i2c->dev); -- cgit v1.1 From 917536aeb88d34e06c1353b0dd144f0987bb66bd Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 21 Sep 2015 14:12:01 +0800 Subject: ASoC: rt5645: Add jd_invert for Broadwell Broadwell can not triger the IRQ falling and rising simultaneously, so it can not detect jack-in and jack-out simultaneously. We add a flag "jd_invert" to platform data. If this flag is set, codec IRQ will be set to invert that forces IRQ as pulse when jack-in and jack-out. Signed-off-by: John Lin Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 7 +++++++ sound/soc/codecs/rt5645.h | 4 ++++ 2 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e830688..b0d96b6 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2829,6 +2829,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; } + if (rt5645->pdata.jd_invert) + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); } else { /* jack out */ rt5645->jack_type = 0; @@ -2844,6 +2847,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_disable_pin(dapm, "LDO2"); snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); snd_soc_dapm_sync(dapm); + if (rt5645->pdata.jd_invert) + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR); } return rt5645->jack_type; @@ -3213,6 +3219,7 @@ static struct rt5645_platform_data buddy_platform_data = { .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, .jd_mode = 3, + .jd_invert = true, }; static int buddy_quirk_cb(const struct dmi_system_id *id) diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 0e4cfc6..90325d9 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -1626,6 +1626,10 @@ #define RT5645_OT_P_NOR (0x0 << 10) #define RT5645_OT_P_INV (0x1 << 10) #define RT5645_IRQ_JD_1_1_EN (0x1 << 9) +#define RT5645_JD_1_1_MASK (0x1 << 7) +#define RT5645_JD_1_1_SFT 7 +#define RT5645_JD_1_1_NOR (0x0 << 7) +#define RT5645_JD_1_1_INV (0x1 << 7) /* IRQ Control 2 (0xbe) */ #define RT5645_IRQ_MB1_OC_MASK (0x1 << 15) -- cgit v1.1 From 8811191fdf7ed02ee07cb8469428158572d355a2 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 22 Sep 2015 21:20:22 +0200 Subject: ASoC: pxa: pxa2xx-ac97: fix dma requestor lines PCM receive and transmit DMA requestor lines were reverted, breaking the PCM playback interface for PXA platforms using the sound/soc/ variant instead of the sound/arm variant. The commit below shows the inversion in the requestor lines. Fixes: d65a14587a9b ("ASoC: pxa: use snd_dmaengine_dai_dma_data") Signed-off-by: Robert Jarzmik Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/pxa/pxa2xx-ac97.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 1f60546..9e4b04e 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -49,7 +49,7 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; +static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -57,7 +57,7 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; +static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, -- cgit v1.1 From de3f8fdf7393e4259b68ae8cea806c39d602769e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 23 Sep 2015 14:35:27 +0800 Subject: ASoC: rt5645: Remove the repeated definitions Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 90325d9..f45861c 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -777,8 +777,6 @@ #define RT5645_PWR_CLS_D_R_BIT 9 #define RT5645_PWR_CLS_D_L (0x1 << 8) #define RT5645_PWR_CLS_D_L_BIT 8 -#define RT5645_PWR_ADC_R (0x1 << 1) -#define RT5645_PWR_ADC_R_BIT 1 #define RT5645_PWR_DAC_L2 (0x1 << 7) #define RT5645_PWR_DAC_L2_BIT 7 #define RT5645_PWR_DAC_R2 (0x1 << 6) -- cgit v1.1 From 21cb13e72b02dbbb5a02477d4dd46bc2bc1cfd08 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 23 Sep 2015 14:35:28 +0800 Subject: ASoC: rt5645: Use the type SOC_DAPM_SINGLE_AUTODISABLE to prevent the weird sound in runtime of power up Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index dbc1d76..25c34fb 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -732,14 +732,14 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = { static const struct snd_kcontrol_new rt5645_dac_l_mix[] = { SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, RT5645_M_ADCMIX_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER, RT5645_M_DAC1_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5645_dac_r_mix[] = { SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, RT5645_M_ADCMIX_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER, RT5645_M_DAC1_R_SFT, 1, 1), }; -- cgit v1.1 From 4f4794124e0421b080a0f1f5f1207636ba55eb85 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 23 Sep 2015 14:35:29 +0800 Subject: ASoC: rt5645: Increase the delay time to remove the pop sound Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 25c34fb..61384ee9 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -1381,7 +1381,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); - mdelay(5); + msleep(40); rt5645->hp_on = true; } else { /* depop parameters */ -- cgit v1.1 From fce97b4d70ad632dd9c6058622492501377bbaaa Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 23 Sep 2015 14:35:30 +0800 Subject: ASoC: rt5645: Prevent the pop sound in case of playback and the jack is plugging Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 61384ee9..268a28b 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2832,6 +2832,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) } else { /* jack out */ rt5645->jack_type = 0; + regmap_update_bits(rt5645->regmap, RT5645_HP_VOL, + RT5645_L_MUTE | RT5645_R_MUTE, + RT5645_L_MUTE | RT5645_R_MUTE); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, -- cgit v1.1 From 83510441bc08bee201c0ded9d81da6dfd008d69a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Sep 2015 11:00:18 +0200 Subject: ALSA: hda/tegra - async probe for avoiding module loading deadlock The Tegra HD-audio controller driver causes deadlocks when loaded as a module since the driver invokes request_module() at binding with the codec driver. This patch works around it by deferring the probe in a work like Intel HD-audio controller driver does. Although hovering the codec probe stuff into udev would be a better solution, it may cause other regressions, so let's try this band-aid fix until the more proper solution gets landed. Reported-by: Thierry Reding Tested-by: Thierry Reding Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_tegra.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 477742c..58c0aad 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -73,6 +73,7 @@ struct hda_tegra { struct clk *hda2codec_2x_clk; struct clk *hda2hdmi_clk; void __iomem *regs; + struct work_struct probe_work; }; #ifdef CONFIG_PM @@ -294,7 +295,9 @@ static int hda_tegra_dev_disconnect(struct snd_device *device) static int hda_tegra_dev_free(struct snd_device *device) { struct azx *chip = device->device_data; + struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); + cancel_work_sync(&hda->probe_work); if (azx_bus(chip)->chip_init) { azx_stop_all_streams(chip); azx_stop_chip(chip); @@ -426,6 +429,9 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) /* * constructor */ + +static void hda_tegra_probe_work(struct work_struct *work); + static int hda_tegra_create(struct snd_card *card, unsigned int driver_caps, struct hda_tegra *hda) @@ -452,6 +458,8 @@ static int hda_tegra_create(struct snd_card *card, chip->single_cmd = false; chip->snoop = true; + INIT_WORK(&hda->probe_work, hda_tegra_probe_work); + err = azx_bus_init(chip, NULL, &hda_tegra_io_ops); if (err < 0) return err; @@ -499,6 +507,21 @@ static int hda_tegra_probe(struct platform_device *pdev) card->private_data = chip; dev_set_drvdata(&pdev->dev, card); + schedule_work(&hda->probe_work); + + return 0; + +out_free: + snd_card_free(card); + return err; +} + +static void hda_tegra_probe_work(struct work_struct *work) +{ + struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work); + struct azx *chip = &hda->chip; + struct platform_device *pdev = to_platform_device(hda->dev); + int err; err = hda_tegra_first_init(chip, pdev); if (err < 0) @@ -520,11 +543,8 @@ static int hda_tegra_probe(struct platform_device *pdev) chip->running = 1; snd_hda_set_power_save(&chip->bus, power_save * 1000); - return 0; - -out_free: - snd_card_free(card); - return err; + out_free: + return; /* no error return from async probe */ } static int hda_tegra_remove(struct platform_device *pdev) -- cgit v1.1 From 0f7e17740612ca0f3b8baf91645075d07bb52b2c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 24 Sep 2015 23:34:01 +0800 Subject: ASoC: rl6347a: Clean up unneeded inclusion of header files Also move the include of sound/hda_verbs.h to rl6347a.h because it is used in rl6347a.h. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rl6347a.c | 19 +------------------ sound/soc/codecs/rl6347a.h | 2 ++ sound/soc/codecs/rt286.c | 1 - sound/soc/codecs/rt298.c | 1 - 4 files changed, 3 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c index 91d5166..a4b910e 100644 --- a/sound/soc/codecs/rl6347a.c +++ b/sound/soc/codecs/rl6347a.c @@ -11,25 +11,8 @@ */ #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "rl6347a.h" diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h index 1cb56e5..e127919 100644 --- a/sound/soc/codecs/rl6347a.h +++ b/sound/soc/codecs/rl6347a.h @@ -12,6 +12,8 @@ #ifndef __RL6347A_H__ #define __RL6347A_H__ +#include + #define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) #define RL6347A_VENDOR_REGISTERS 0x20 diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index bd93658..1fbdb4f 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -29,7 +29,6 @@ #include #include #include -#include #include "rl6347a.h" #include "rt286.h" diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 3c2f0f8..2a6c2ba 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "rl6347a.h" #include "rt298.h" -- cgit v1.1 From 7f57d803ee03730d570dc59a9e3e4842b58dd5cc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Sep 2015 17:36:51 +0200 Subject: ALSA: hda - Disable power_save_node for Thinkpads Lenovo Thinkpads with recent Realtek codecs seem suffering from click noises at power transition since the introduction of widget power saving in 4.1 kernel. Although this might be solved by some delays in appropriate points, as a quick workaround, just disable the power_save_node feature for now. The gain it gives is relatively small, and this makes the situation back to pre 4.1 time. This patch ended up with a bit more code changes than usual because the existing fixup for Thinkpads is highly chained. Instead of adding yet another chain, combine a few of them into a single fixup entry, as a gratis cleanup. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=943982 Cc: # v4.1+ Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a75b561..afec6dc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4188,6 +4188,24 @@ static void alc_fixup_disable_aamix(struct hda_codec *codec, } } +/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */ +static void alc_fixup_tpt440_dock(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const struct hda_pintbl pincfgs[] = { + { 0x16, 0x21211010 }, /* dock headphone */ + { 0x19, 0x21a11010 }, /* dock mic */ + { } + }; + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; + codec->power_save_node = 0; /* avoid click noises */ + snd_hda_apply_pincfgs(codec, pincfgs); + } +} + static void alc_shutup_dell_xps13(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -4562,7 +4580,6 @@ enum { ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_FIXUP_TPT440_DOCK, - ALC292_FIXUP_TPT440_DOCK2, ALC283_FIXUP_BXBT2807_MIC, ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, ALC282_FIXUP_ASPIRE_V5_PINS, @@ -5029,17 +5046,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC292_FIXUP_TPT440_DOCK] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pincfg_no_hp_to_lineout, - .chained = true, - .chain_id = ALC292_FIXUP_TPT440_DOCK2 - }, - [ALC292_FIXUP_TPT440_DOCK2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x21211010 }, /* dock headphone */ - { 0x19, 0x21a11010 }, /* dock mic */ - { } - }, + .v.func = alc_fixup_tpt440_dock, .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, -- cgit v1.1 From e74679b38c9417c1c524081121cdcdb36f82264d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 25 Sep 2015 11:07:04 +0200 Subject: ASoC: db1200: Fix DAI link format for db1300 and db1550 Commit b4508d0f95fa ("ASoC: db1200: Use static DAI format setup") switched the db1200 driver over to using static DAI format setup instead of a callback function. But the commit only added the dai_fmt field to one of the three DAI links in the driver. This breaks audio on db1300 and db1550. Add the two missing dai_fmt settings to fix the issue. Fixes: b4508d0f95fa ("ASoC: db1200: Use static DAI format setup") Reported-by: Manuel Lauss Tested-by: Manuel Lauss Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/au1x/db1200.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 58c3164..8c907eb 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -129,6 +129,8 @@ static struct snd_soc_dai_link db1300_i2s_dai = { .cpu_dai_name = "au1xpsc_i2s.2", .platform_name = "au1xpsc-pcm.2", .codec_name = "wm8731.0-001b", + .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &db1200_i2s_wm8731_ops, }; @@ -146,6 +148,8 @@ static struct snd_soc_dai_link db1550_i2s_dai = { .cpu_dai_name = "au1xpsc_i2s.3", .platform_name = "au1xpsc-pcm.3", .codec_name = "wm8731.0-001b", + .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &db1200_i2s_wm8731_ops, }; -- cgit v1.1 From fb97d75b038659998257f7dd767d8229dce50b74 Mon Sep 17 00:00:00 2001 From: Gianluca Renzi Date: Fri, 25 Sep 2015 21:33:42 +0200 Subject: ASoC: sgtl5000: fix error message output for MicBias voltage Cc: Liam Girdwood Cc: Takashi Iwai Cc: Fabio Estevam Cc: alsa-devel@alsa-project.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Gianluca Renzi Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index bfda25e..6e7843e 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1549,7 +1549,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, else { sgtl5000->micbias_voltage = 0; dev_err(&client->dev, - "Unsuitable MicBias resistor\n"); + "Unsuitable MicBias voltage\n"); } } else { sgtl5000->micbias_voltage = 0; -- cgit v1.1 From b7b01d345b83602a42b6ff02cacb9d9ada5ecd0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 29 Sep 2015 17:59:14 +0200 Subject: ASoC: imx-ssi: Fix DAI hardware signal inversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SND_SOC_DAIFMT_{IB|NB}_{IF|NF} are defined as inverting or not BCLK or FRM relatively to what is standard for the specified DAI hardware audio format. Consequently, the absolute polarities of these signals cannot be derived only from these settings as this driver did. The format has to be taken into account too. This fixes inverted left/right channels in I²S mode. Signed-off-by: Benoît Thébaudeau Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/imx-ssi.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 48b2d24..b95132e 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -95,7 +95,8 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* data on rising edge of bclk, frame low 1clk before data */ - strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; + strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | + SSI_STCR_TEFS; scr |= SSI_SCR_NET; if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) { scr &= ~SSI_I2S_MODE_MASK; @@ -104,33 +105,31 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) break; case SND_SOC_DAIFMT_LEFT_J: /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TXBIT0; + strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_DSP_B: /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0; + strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL; break; case SND_SOC_DAIFMT_DSP_A: /* data on rising edge of bclk, frame high 1clk before data */ - strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0 | SSI_STCR_TEFS; + strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL | + SSI_STCR_TEFS; break; } /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: - strcr |= SSI_STCR_TFSI; - strcr &= ~SSI_STCR_TSCKP; + strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI; break; case SND_SOC_DAIFMT_IB_NF: - strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); + strcr ^= SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_NB_IF: - strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; + strcr ^= SSI_STCR_TFSI; break; case SND_SOC_DAIFMT_NB_NF: - strcr &= ~SSI_STCR_TFSI; - strcr |= SSI_STCR_TSCKP; break; } -- cgit v1.1 From 57622aef86d21d459e937c72f578050ff4a91af5 Mon Sep 17 00:00:00 2001 From: Jiada Wang Date: Wed, 30 Sep 2015 13:54:13 +0900 Subject: ASoC: wm8962: balance pm_runtime_enable pm_runtime_enable is called in probe to enable runtime PM for wm8962 codec, but pm_runtime_disable isn't called in remove callback, nor is called in error path if probe fails after runtime PM is enabled, this causes unbalanced pm_runtime_enable. This patch Adds pm_runtime_disable in remove callback and error path, to balance pm_runtime_enable. Signed-off-by: Jiada Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b4eb975..85a2c54 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3759,7 +3759,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8962, &wm8962_dai, 1); if (ret < 0) - goto err_enable; + goto err_pm_runtime; regcache_cache_only(wm8962->regmap, true); @@ -3768,6 +3768,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, return 0; +err_pm_runtime: + pm_runtime_disable(&i2c->dev); err_enable: regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); err: @@ -3777,6 +3779,7 @@ err: static int wm8962_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); + pm_runtime_disable(&client->dev); return 0; } -- cgit v1.1 From 98a4b665bc91480cc4c054093a59249e153ac17e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 29 Sep 2015 21:43:17 +0200 Subject: ASoC: sun4i-codec: pass through clk_set_rate error The current code, disregarding the clk_set_rate error code, was always returning -EINVAL. Fix that and return the code in order to have more clue about what's going on. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 8d59d83..77408af 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -283,7 +283,7 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); unsigned long clk_freq; - int hwrate; + int ret, hwrate; u32 val; if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) @@ -293,8 +293,9 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, if (!clk_freq) return -EINVAL; - if (clk_set_rate(scodec->clk_module, clk_freq)) - return -EINVAL; + ret = clk_set_rate(scodec->clk_module, clk_freq); + if (ret) + return ret; hwrate = sun4i_codec_get_hw_rate(params); if (hwrate < 0) -- cgit v1.1 From debb97248741597fa9f38f563f2b9a17250ee4c5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 29 Sep 2015 21:43:18 +0200 Subject: ASoC: sun4i-codec: Remove redundant SND_PCM_RATE_KNOT The SND_PCM_RATE_KNOT covers all the rate settings, even though some that we don't support, while we also list all the rate we support. Simply remove it. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 77408af..4778055 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -389,8 +389,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { .rate_max = 192000, .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000 | - SNDRV_PCM_RATE_KNOT, + SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 24, -- cgit v1.1 From e256da84a04ea31c3c215997c847609af224e8f4 Mon Sep 17 00:00:00 2001 From: Gianluca Renzi Date: Fri, 25 Sep 2015 21:33:41 +0200 Subject: ASoC: sgtl5000: fix wrong register MIC_BIAS_VOLTAGE setup on probe Signed-off-by: Gianluca Renzi Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/sgtl5000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 6e7843e..f540f82 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1376,8 +1376,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, - SGTL5000_BIAS_R_MASK, - sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT); + SGTL5000_BIAS_VOLT_MASK, + sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT); /* * disable DAP * TODO: -- cgit v1.1 From 58ceb57ec1be928bec2faeca11fe0752f930669d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 30 Sep 2015 22:51:52 +0200 Subject: ASoC: pxa: pxa-pcm-lib: switch over to snd-soc-dmaengine-pcm This patch removes the old PXA DMA API usage and switches over to generic functions provided by snd-soc-dmaengine-pcm. More cleanups may be done on top of this, and some function stubs can now be removed completetly. However, the intention here was to keep the transition as small as possible. This was tested on the mioa701 pxa27x board. Signed-off-by: Daniel Mack [trivial change from mmp-dma to pxa-dma] Signed-off-by: Robert Jarzmik Signed-off-by: Mark Brown --- sound/arm/pxa2xx-ac97.c | 13 ++- sound/arm/pxa2xx-pcm-lib.c | 201 ++++++++------------------------------------ sound/arm/pxa2xx-pcm.c | 12 +-- sound/arm/pxa2xx-pcm.h | 2 - sound/soc/pxa/pxa2xx-ac97.c | 49 +++++++---- sound/soc/pxa/pxa2xx-i2s.c | 3 + sound/soc/pxa/pxa2xx-pcm.c | 32 ------- 7 files changed, 85 insertions(+), 227 deletions(-) (limited to 'sound') diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 38590b3..fbd5dad 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct pxad_param pxa2xx_ac97_pcm_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 12, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct pxad_param pxa2xx_ac97_pcm_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 11, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 01f8fdc..e9b98af 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,8 +16,6 @@ #include #include -#include - #include "pxa2xx-pcm.h" static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { @@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { .period_bytes_min = 32, .period_bytes_max = 8192 - 32, .periods_min = 1, - .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), + .periods_max = 256, .buffer_bytes_max = 128 * 1024, .fifo_size = 32, }; @@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd = runtime->private_data; - size_t totsize = params_buffer_bytes(params); - size_t period = params_period_bytes(params); - pxa_dma_desc *dma_desc; - dma_addr_t dma_buff_phys, next_desc_phys; - u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_params; + struct dma_slave_config config; + int ret; - /* temporary transition hack */ - switch (rtd->params->addr_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - dcmd |= DCMD_WIDTH1; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - dcmd |= DCMD_WIDTH2; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - dcmd |= DCMD_WIDTH4; - break; - default: - /* can't happen */ - break; - } + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; - switch (rtd->params->maxburst) { - case 8: - dcmd |= DCMD_BURST8; - break; - case 16: - dcmd |= DCMD_BURST16; - break; - case 32: - dcmd |= DCMD_BURST32; - break; - } + ret = snd_hwparams_to_dma_slave_config(substream, params, &config); + if (ret) + return ret; - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = totsize; + snd_dmaengine_pcm_set_config_from_dai_data(substream, + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), + &config); - dma_desc = rtd->dma_desc_array; - next_desc_phys = rtd->dma_desc_array_phys; - dma_buff_phys = runtime->dma_addr; - do { - next_desc_phys += sizeof(pxa_dma_desc); - dma_desc->ddadr = next_desc_phys; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->addr; - } else { - dma_desc->dsadr = rtd->params->addr; - dma_desc->dtadr = dma_buff_phys; - } - if (period > totsize) - period = totsize; - dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; - dma_desc++; - dma_buff_phys += period; - } while (totsize -= period); - dma_desc[-1].ddadr = rtd->dma_desc_array_phys; + ret = dmaengine_slave_config(chan, &config); + if (ret) + return ret; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); return 0; } @@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - - if (rtd && rtd->params && rtd->params->filter_data) { - unsigned long req = *(unsigned long *) rtd->params->filter_data; - DRCMR(req) = 0; - } - snd_pcm_set_runtime_buffer(substream, NULL); return 0; } @@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; - DCSR(prtd->dma_ch) = DCSR_RUN; - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - DCSR(prtd->dma_ch) &= ~DCSR_RUN; - break; - - case SNDRV_PCM_TRIGGER_RESUME: - DCSR(prtd->dma_ch) |= DCSR_RUN; - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; - DCSR(prtd->dma_ch) |= DCSR_RUN; - break; - - default: - ret = -EINVAL; - } - - return ret; + return snd_dmaengine_pcm_trigger(substream, cmd); } EXPORT_SYMBOL(pxa2xx_pcm_trigger); snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *prtd = runtime->private_data; - - dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); - snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); - - if (x == runtime->buffer_size) - x = 0; - return x; + return snd_dmaengine_pcm_pointer(substream); } EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - unsigned long req; - - if (!prtd || !prtd->params) - return 0; - - if (prtd->dma_ch == -1) - return -EINVAL; - - DCSR(prtd->dma_ch) &= ~DCSR_RUN; - DCSR(prtd->dma_ch) = 0; - DCMD(prtd->dma_ch) = 0; - req = *(unsigned long *) prtd->params->filter_data; - DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; - return 0; } EXPORT_SYMBOL(__pxa2xx_pcm_prepare); -void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) -{ - struct snd_pcm_substream *substream = dev_id; - int dcsr; - - dcsr = DCSR(dma_ch); - DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; - - if (dcsr & DCSR_ENDINTR) { - snd_pcm_period_elapsed(substream); - } else { - printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", - dma_ch, dcsr); - snd_pcm_stop_xrun(substream); - } -} -EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); - int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd; + struct snd_dmaengine_dai_dma_data *dma_params; int ret; runtime->hw = pxa2xx_pcm_hardware; + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; + /* * For mysterious reasons (and despite what the manual says) * playback samples are lost if the DMA count is not a multiple @@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); if (ret) - goto out; + return ret; ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); if (ret) - goto out; + return ret; ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) - goto out; - - ret = -ENOMEM; - rtd = kzalloc(sizeof(*rtd), GFP_KERNEL); - if (!rtd) - goto out; - rtd->dma_desc_array = - dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, - &rtd->dma_desc_array_phys, GFP_KERNEL); - if (!rtd->dma_desc_array) - goto err1; + return ret; - rtd->dma_ch = -1; - runtime->private_data = rtd; - return 0; - - err1: - kfree(rtd); - out: - return ret; + return snd_dmaengine_pcm_open_request_chan(substream, + pxad_filter_fn, + dma_params->filter_data); } EXPORT_SYMBOL(__pxa2xx_pcm_open); int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd = runtime->private_data; - - dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, - rtd->dma_desc_array, rtd->dma_desc_array_phys); - kfree(rtd); - return 0; + return snd_dmaengine_pcm_close_release_chan(substream); } EXPORT_SYMBOL(__pxa2xx_pcm_close); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 83be8e3..83fcfac 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma("dma", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - goto err2; - rtd->dma_ch = ret; ret = client->startup(substream); if (!ret) - goto out; + goto err2; + + return 0; - pxa_free_dma(rtd->dma_ch); err2: __pxa2xx_pcm_close(substream); out: @@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) { struct pxa2xx_pcm_client *client = substream->private_data; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - pxa_free_dma(rtd->dma_ch); client->shutdown(substream); return __pxa2xx_pcm_close(substream); diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 0033098..8fa2b7c 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -13,8 +13,6 @@ struct pxa2xx_runtime_data { int dma_ch; struct snd_dmaengine_dai_dma_data *params; - struct pxa_dma_desc *dma_desc_array; - dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 9e4b04e..f3de615 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11; +static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 11, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12; +static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 12, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 10, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 9, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 8, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .addr = __PREG(MCDR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -89,9 +107,8 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; -static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -105,9 +122,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, return 0; } -static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -121,9 +137,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, return 0; } -static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; @@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, SNDRV_PCM_RATE_48000) static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { - .hw_params = pxa2xx_ac97_hw_params, + .startup = pxa2xx_ac97_hifi_startup, }; static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { - .hw_params = pxa2xx_ac97_hw_aux_params, + .startup = pxa2xx_ac97_aux_startup, }; static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { - .hw_params = pxa2xx_ac97_hw_mic_params, + .startup = pxa2xx_ac97_mic_startup, }; /* diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 6b4e400..0389cf7 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai) /* Along with FIFO servicing */ SAIMR &= ~(SAIMR_RFS | SAIMR_TFS); + snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out, + &pxa2xx_i2s_pcm_stereo_in); + return 0; } diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 29a3fdb..9f39039 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -15,8 +15,6 @@ #include #include -#include - #include #include #include @@ -27,11 +25,8 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_dmaengine_dai_dma_data *dma; - int ret; dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, if (!dma) return 0; - /* this may get called several times by oss emulation - * with different params */ - if (prtd->params == NULL) { - prtd->params = dma; - ret = pxa_request_dma("name", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - return ret; - prtd->dma_ch = ret; - } else if (prtd->params != dma) { - pxa_free_dma(prtd->dma_ch); - prtd->params = dma; - ret = pxa_request_dma("name", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - return ret; - prtd->dma_ch = ret; - } - return __pxa2xx_pcm_hw_params(substream, params); } static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - __pxa2xx_pcm_hw_free(substream); - if (prtd->dma_ch >= 0) { - pxa_free_dma(prtd->dma_ch); - prtd->dma_ch = -1; - prtd->params = NULL; - } - return 0; } -- cgit v1.1 From e29fd55d8d23ea7afcadd15a87700583e7da5035 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 1 Oct 2015 14:47:10 +0800 Subject: ASoC: rt5645: Add the control of ClassD modulator Speaker Gain Ratio Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 12 ++++++++++++ sound/soc/codecs/rt5645.h | 4 ++++ 2 files changed, 16 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index b0d96b6..1f7045b 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -469,6 +469,14 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv, 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) ); +/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */ +static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv, + 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0), + 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0) +); + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, @@ -476,6 +484,10 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL, RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + /* ClassD modulator Speaker Gain Ratio */ + SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO, + RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv), + /* Headphone Output Volume */ SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL, RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index f45861c..d0e0ce5 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -668,6 +668,10 @@ #define RT5645_M_SV_R_SPM_R (0x1 << 0) #define RT5645_M_SV_R_SPM_R_SFT 0 +/* SPOMIX Ratio Control (0x4a) */ +#define RT5645_SPK_G_CLSD_MASK (0x7 << 0) +#define RT5645_SPK_G_CLSD_SFT 0 + /* Mono Output Mixer Control (0x4c) */ #define RT5645_M_OV_L_MM (0x1 << 9) #define RT5645_M_OV_L_MM_SFT 9 -- cgit v1.1 From 8c1a9d6323abf0fb1e5dad96cf3f1c783505ea5a Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 1 Oct 2015 14:47:09 +0800 Subject: ASoC: rt5645: Correct the naming and setting of ADC Boost Volume Control Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 6 +++--- sound/soc/codecs/rt5645.h | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 4972bf3..aaf08cd3 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -519,11 +519,11 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), /* ADC Boost Volume Control */ - SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1, + SOC_DOUBLE_TLV("ADC Boost Capture Volume", RT5645_ADC_BST_VOL1, RT5645_STO1_ADC_L_BST_SFT, RT5645_STO1_ADC_R_BST_SFT, 3, 0, adc_bst_tlv), - SOC_DOUBLE_TLV("STO2 ADC Boost Gain", RT5645_ADC_BST_VOL1, - RT5645_STO2_ADC_L_BST_SFT, RT5645_STO2_ADC_R_BST_SFT, 3, 0, + SOC_DOUBLE_TLV("Mono ADC Boost Capture Volume", RT5645_ADC_BST_VOL2, + RT5645_MONO_ADC_L_BST_SFT, RT5645_MONO_ADC_R_BST_SFT, 3, 0, adc_bst_tlv), /* I2S2 function select */ diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 0e4cfc6..8c964cfb 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -39,8 +39,8 @@ #define RT5645_STO1_ADC_DIG_VOL 0x1c #define RT5645_MONO_ADC_DIG_VOL 0x1d #define RT5645_ADC_BST_VOL1 0x1e -/* Mixer - D-D */ #define RT5645_ADC_BST_VOL2 0x20 +/* Mixer - D-D */ #define RT5645_STO1_ADC_MIXER 0x27 #define RT5645_MONO_ADC_MIXER 0x28 #define RT5645_AD_DA_MIXER 0x29 @@ -315,12 +315,14 @@ #define RT5645_STO1_ADC_R_BST_SFT 12 #define RT5645_STO1_ADC_COMP_MASK (0x3 << 10) #define RT5645_STO1_ADC_COMP_SFT 10 -#define RT5645_STO2_ADC_L_BST_MASK (0x3 << 8) -#define RT5645_STO2_ADC_L_BST_SFT 8 -#define RT5645_STO2_ADC_R_BST_MASK (0x3 << 6) -#define RT5645_STO2_ADC_R_BST_SFT 6 -#define RT5645_STO2_ADC_COMP_MASK (0x3 << 4) -#define RT5645_STO2_ADC_COMP_SFT 4 + +/* ADC Boost Volume Control (0x20) */ +#define RT5645_MONO_ADC_L_BST_MASK (0x3 << 14) +#define RT5645_MONO_ADC_L_BST_SFT 14 +#define RT5645_MONO_ADC_R_BST_MASK (0x3 << 12) +#define RT5645_MONO_ADC_R_BST_SFT 12 +#define RT5645_MONO_ADC_COMP_MASK (0x3 << 10) +#define RT5645_MONO_ADC_COMP_SFT 10 /* Stereo2 ADC Mixer Control (0x26) */ #define RT5645_STO2_ADC_SRC_MASK (0x1 << 15) -- cgit v1.1 From 2d1180e36905bf92892e265c662e5e597be01505 Mon Sep 17 00:00:00 2001 From: Rick Mann Date: Wed, 30 Sep 2015 16:24:53 -0700 Subject: ASoC: tlv320aic3x: Prevent writing reserved registers on tlv320aic3104 CODECs The current code writes a set of registers that are reserved on the tlc320aic3104. The change skips those registers for that IC. Signed-off-by: Rick Mann Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic3x.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 1a82b19..8739126 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1509,14 +1509,17 @@ static int aic3x_init(struct snd_soc_codec *codec) snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL); - /* Line2 to HP Bypass default volume, disconnect from Output Mixer */ - snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL); - snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL); - snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL); - snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL); - /* Line2 Line Out default volume, disconnect from Output Mixer */ - snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL); - snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL); + /* On tlv320aic3104, these registers are reserved and must not be written */ + if (aic3x->model != AIC3X_MODEL_3104) { + /* Line2 to HP Bypass default volume, disconnect from Output Mixer */ + snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL); + snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL); + snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL); + snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL); + /* Line2 Line Out default volume, disconnect from Output Mixer */ + snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL); + snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL); + } switch (aic3x->model) { case AIC3X_MODEL_3X: -- cgit v1.1 From 4873867e5f2bd90faad861dd94865099fc3140f3 Mon Sep 17 00:00:00 2001 From: Yitian Bu Date: Fri, 2 Oct 2015 15:18:41 +0800 Subject: ASoC: dwc: correct irq clear method from Designware I2S datasheet, tx/rx XRUN irq is cleared by reading register TOR/ROR, rather than by writing into them. Signed-off-by: Yitian Bu Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/dwc/designware_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index a3e97b4..0d28e3b 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -131,10 +131,10 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, TOR(i), 0); + i2s_read_reg(dev->i2s_base, TOR(i)); } else { for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, ROR(i), 0); + i2s_read_reg(dev->i2s_base, ROR(i)); } } -- cgit v1.1 From 50e6168f27fdc1f915d3341743b46147c626dc6e Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 28 Sep 2015 14:01:08 +0100 Subject: ASoC: arizona: add defines for single-input gain control Adds convenience defines for declaring a gain control that has an input mux. These blocks are functionally equivalent to the existing mixer blocks but can only have a single input active at once. Signed-off-by: Richard Fitzgerald Acked-by: Mark Brown Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 7b68d05..b73920a 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -99,6 +99,11 @@ extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; +#define ARIZONA_GAINMUX_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv) + #define ARIZONA_MIXER_CONTROLS(name, base) \ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ -- cgit v1.1 From 6ebbce0a6e15fd52ef029f780ec5fb3282fc22e3 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 28 Sep 2015 14:01:09 +0100 Subject: ASoC: wm8998: Initial WM8998 codec driver Signed-off-by: Richard Fitzgerald Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/arizona.c | 33 + sound/soc/codecs/arizona.h | 7 +- sound/soc/codecs/wm8998.c | 1430 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8998.h | 23 + 6 files changed, 1500 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/wm8998.c create mode 100644 sound/soc/codecs/wm8998.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733e..6c30bad 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -171,6 +171,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8996 if I2C select SND_SOC_WM8997 if MFD_WM8997 + select SND_SOC_WM8998 if MFD_WM8998 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS @@ -195,9 +196,11 @@ config SND_SOC_ARIZONA default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM8997=y + default y if SND_SOC_WM8998=y default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM8997=m + default m if SND_SOC_WM8998=m config SND_SOC_WM_HUBS tristate @@ -865,6 +868,9 @@ config SND_SOC_WM8996 config SND_SOC_WM8997 tristate +config SND_SOC_WM8998 + tristate + config SND_SOC_WM9081 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077..a9d4769 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -176,6 +176,7 @@ snd-soc-wm8993-objs := wm8993.o snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o snd-soc-wm8995-objs := wm8995.o snd-soc-wm8997-objs := wm8997.o +snd-soc-wm8998-objs := wm8998.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9090-objs := wm9090.o snd-soc-wm9705-objs := wm9705.o @@ -364,6 +365,7 @@ obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o +obj-$(CONFIG_SND_SOC_WM8998) += snd-soc-wm8998.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ac21b85..9929efc 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -316,6 +316,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "Tone Generator 2", "Haptics", "AEC", + "AEC2", "Mic Mute Mixer", "Noise Generator", "IN1L", @@ -423,6 +424,7 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x05, 0x06, /* Haptics */ 0x08, /* AEC */ + 0x09, /* AEC2 */ 0x0c, /* Noise mixer */ 0x0d, /* Comfort noise */ 0x10, /* IN1L */ @@ -527,6 +529,32 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values); const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0); EXPORT_SYMBOL_GPL(arizona_mixer_tlv); +const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + "12kHz", "24kHz", "48kHz", "96kHz", "192kHz", + "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz", + "4kHz", "8kHz", "16kHz", "32kHz", +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_text); + +const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_val); + +const char *arizona_sample_rate_val_to_name(unsigned int rate_val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) { + if (arizona_sample_rate_val[i] == rate_val) + return arizona_sample_rate_text[i]; + } + + return "Illegal"; +} +EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name); + const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = { "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate", }; @@ -1884,6 +1912,11 @@ static int arizona_calc_fratio(struct arizona_fll *fll, if (fll->arizona->rev < 3 || sync) return init_ratio; break; + case WM8998: + case WM1814: + if (sync) + return init_ratio; + break; default: return init_ratio; } diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index b73920a..fea8b8a 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -93,7 +93,7 @@ struct arizona_priv { bool dvfs_cached; }; -#define ARIZONA_NUM_MIXER_INPUTS 103 +#define ARIZONA_NUM_MIXER_INPUTS 104 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; @@ -214,8 +214,12 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; .num_regs = 1 }) } #define ARIZONA_RATE_ENUM_SIZE 4 +#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14 + extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; +extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; +extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; extern const struct soc_enum arizona_isrc_fsl[]; extern const struct soc_enum arizona_isrc_fsh[]; @@ -301,4 +305,5 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift); +extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val); #endif diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c new file mode 100644 index 0000000..8782dfb --- /dev/null +++ b/sound/soc/codecs/wm8998.c @@ -0,0 +1,1430 @@ +/* + * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm8998.h" + +struct wm8998_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = snd_soc_read(codec, ARIZONA_ASRC_RATE1); + val &= ARIZONA_ASRC_RATE1_MASK; + val >>= ARIZONA_ASRC_RATE1_SHIFT; + + switch (val) { + case 0: + case 1: + case 2: + val = snd_soc_read(codec, + ARIZONA_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(codec->dev, + "Unsupported ASRC rate1 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, + "Illegal ASRC rate1 selector (0x%x)\n", + val); + return -EINVAL; + } + + val = snd_soc_read(codec, ARIZONA_ASRC_RATE2); + val &= ARIZONA_ASRC_RATE2_MASK; + val >>= ARIZONA_ASRC_RATE2_SHIFT; + + switch (val) { + case 8: + case 9: + val -= 0x8; + val = snd_soc_read(codec, + ARIZONA_ASYNC_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(codec->dev, + "Unsupported ASRC rate2 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, + "Illegal ASRC rate2 selector (0x%x)\n", + val); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = wm8998->core.arizona; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, inmode; + unsigned int mode_val, src_val; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + /* L and R registers have same shift and mask */ + inmode = arizona->pdata.inmode[2 * mux]; + src_val = mux << ARIZONA_IN1L_SRC_SHIFT; + if (inmode & ARIZONA_INMODE_SE) + src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; + + switch (arizona->pdata.inmode[0]) { + case ARIZONA_INMODE_DMIC: + if (mux) + mode_val = 0; /* B always analogue */ + else + mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; + + snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_MODE_MASK, mode_val); + + /* IN1A is digital so L and R must change together */ + /* src_val setting same for both registers */ + snd_soc_update_bits(codec, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_MASK | + ARIZONA_IN1L_SRC_SE_MASK, src_val); + snd_soc_update_bits(codec, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_MASK | + ARIZONA_IN1R_SRC_SE_MASK, src_val); + break; + default: + /* both analogue */ + snd_soc_update_bits(codec, + e->reg, + ARIZONA_IN1L_SRC_MASK | + ARIZONA_IN1L_SRC_SE_MASK, + src_val); + break; + } + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = wm8998->core.arizona; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, inmode, src_val, mode_val; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + inmode = arizona->pdata.inmode[1 + (2 * mux)]; + if (inmode & ARIZONA_INMODE_DMIC) + mode_val = 1 << ARIZONA_IN2_MODE_SHIFT; + else + mode_val = 0; + + src_val = mux << ARIZONA_IN2L_SRC_SHIFT; + if (inmode & ARIZONA_INMODE_SE) + src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT; + + snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_MODE_MASK, mode_val); + + snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK, + src_val); + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static const char * const wm8998_inmux_texts[] = { + "A", + "B", +}; + +static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_SHIFT, + wm8998_inmux_texts); + +static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_SHIFT, + wm8998_inmux_texts); + +static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_SHIFT, + wm8998_inmux_texts); + +static const struct snd_kcontrol_new wm8998_in1mux[2] = { + SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, + snd_soc_dapm_get_enum_double, wm8998_in1mux_put), + SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum, + snd_soc_dapm_get_enum_double, wm8998_in1mux_put), +}; + +static const struct snd_kcontrol_new wm8998_in2mux = + SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum, + snd_soc_dapm_get_enum_double, wm8998_in2mux_put); + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM8998_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUTL Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUTR Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTL Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTR Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0) + +static const struct snd_kcontrol_new wm8998_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), +SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), +SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), +SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), +SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L), +WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R), +WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L), +WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R), +WM8998_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L), +WM8998_NG_SRC("SPKDATR", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATR, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +static const char * const wm8998_aec_loopback_texts[] = { + "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT", + "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR", +}; + +static const unsigned int wm8998_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, +}; + +static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, + ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, + ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback), +}; + +static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, + ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1AL"), +SND_SOC_DAPM_INPUT("IN1AR"), +SND_SOC_DAPM_INPUT("IN1BL"), +SND_SOC_DAPM_INPUT("IN1BR"), +SND_SOC_DAPM_INPUT("IN2A"), +SND_SOC_DAPM_INPUT("IN2B"), + +SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]), +SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]), +SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2 PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[0]), + +SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[1]), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0), + +ARIZONA_MUX_WIDGETS(EQ1, "EQ1"), +ARIZONA_MUX_WIDGETS(EQ2, "EQ2"), +ARIZONA_MUX_WIDGETS(EQ3, "EQ3"), +ARIZONA_MUX_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MUX_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"), +ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"), +ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"), +ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"), + +ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"), +ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LINEOUTL"), +SND_SOC_DAPM_OUTPUT("LINEOUTR"), +SND_SOC_DAPM_OUTPUT("EPOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDATL"), +SND_SOC_DAPM_OUTPUT("SPKDATR"), +SND_SOC_DAPM_OUTPUT("SPDIF"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2 PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" } + +static const struct snd_soc_dapm_route wm8998_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "IN1AL", NULL, "SYSCLK" }, + { "IN1AR", NULL, "SYSCLK" }, + { "IN1BL", NULL, "SYSCLK" }, + { "IN1BR", NULL, "SYSCLK" }, + { "IN2A", NULL, "SYSCLK" }, + { "IN2B", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + + { "SLIMRX3", NULL, "Slim2 Playback" }, + { "SLIMRX4", NULL, "Slim2 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + + { "IN1L Mux", "A", "IN1AL" }, + { "IN1R Mux", "A", "IN1AR" }, + { "IN1L Mux", "B", "IN1BL" }, + { "IN1R Mux", "B", "IN1BR" }, + + { "IN2 Mux", "A", "IN2A" }, + { "IN2 Mux", "B", "IN2B" }, + + { "IN1L PGA", NULL, "IN1L Mux" }, + { "IN1R PGA", NULL, "IN1R Mux" }, + { "IN2 PGA", NULL, "IN2 Mux" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"), + ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"), + ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"), + ARIZONA_MIXER_ROUTES("OUT3", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"), + + ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"), + ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"), + + ARIZONA_MUX_ROUTES("EQ1", "EQ1"), + ARIZONA_MUX_ROUTES("EQ2", "EQ2"), + ARIZONA_MUX_ROUTES("EQ3", "EQ3"), + ARIZONA_MUX_ROUTES("EQ4", "EQ4"), + + ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC1 Loopback", "HPOUTL", "OUT1L" }, + { "AEC1 Loopback", "HPOUTR", "OUT1R" }, + { "AEC2 Loopback", "HPOUTL", "OUT1L" }, + { "AEC2 Loopback", "HPOUTR", "OUT1R" }, + { "HPOUTL", NULL, "OUT1L" }, + { "HPOUTR", NULL, "OUT1R" }, + + { "AEC1 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC1 Loopback", "LINEOUTR", "OUT2R" }, + { "AEC2 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC2 Loopback", "LINEOUTR", "OUT2R" }, + { "LINEOUTL", NULL, "OUT2L" }, + { "LINEOUTR", NULL, "OUT2R" }, + + { "AEC1 Loopback", "EPOUT", "OUT3" }, + { "AEC2 Loopback", "EPOUT", "OUT3" }, + { "EPOUT", NULL, "OUT3" }, + + { "AEC1 Loopback", "SPKOUTL", "OUT4L" }, + { "AEC2 Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "AEC1 Loopback", "SPKOUTR", "OUT4R" }, + { "AEC2 Loopback", "SPKOUTR", "OUT4R" }, + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "SPDIF", NULL, "SPD1" }, + + { "AEC1 Loopback", "SPKDATL", "OUT5L" }, + { "AEC1 Loopback", "SPKDATR", "OUT5R" }, + { "AEC2 Loopback", "SPKDATL", "OUT5L" }, + { "AEC2 Loopback", "SPKDATR", "OUT5R" }, + { "SPKDATL", NULL, "OUT5L" }, + { "SPKDATR", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, +}; + +#define WM8998_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8998_dai[] = { + { + .name = "wm8998-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8998-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, +}; + +static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case WM8998_FLL1: + return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout); + case WM8998_FLL2: + return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout); + case WM8998_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref, + Fout); + case WM8998_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +static int wm8998_codec_probe(struct snd_soc_codec *codec) +{ + struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + priv->core.arizona->dapm = dapm; + + arizona_init_spk(codec); + arizona_init_gpio(codec); + + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + + return 0; +} + +static int wm8998_codec_remove(struct snd_soc_codec *codec) +{ + struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + +#define WM8998_DIG_VU 0x0200 + +static unsigned int wm8998_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static struct regmap *wm8998_get_regmap(struct device *dev) +{ + struct wm8998_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_wm8998 = { + .probe = wm8998_codec_probe, + .remove = wm8998_codec_remove, + .get_regmap = wm8998_get_regmap, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = wm8998_set_fll, + + .controls = wm8998_snd_controls, + .num_controls = ARRAY_SIZE(wm8998_snd_controls), + .dapm_widgets = wm8998_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets), + .dapm_routes = wm8998_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes), +}; + +static int wm8998_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm8998_priv *wm8998; + int i; + + wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv), + GFP_KERNEL); + if (!wm8998) + return -ENOMEM; + platform_set_drvdata(pdev, wm8998); + + wm8998->core.arizona = arizona; + wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */ + + for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++) + wm8998->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm8998->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm8998->fll[1]); + + for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++) + arizona_init_dai(&wm8998->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm8998_digital_vu[i], + WM8998_DIG_VU, WM8998_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998, + wm8998_dai, ARRAY_SIZE(wm8998_dai)); +} + +static int wm8998_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver wm8998_codec_driver = { + .driver = { + .name = "wm8998-codec", + }, + .probe = wm8998_probe, + .remove = wm8998_remove, +}; + +module_platform_driver(wm8998_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8998 driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:wm8998-codec"); diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h new file mode 100644 index 0000000..1e86472 --- /dev/null +++ b/sound/soc/codecs/wm8998.h @@ -0,0 +1,23 @@ +/* + * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8998_H +#define _WM8998_H + +#include "arizona.h" + +#define WM8998_FLL1 1 +#define WM8998_FLL2 2 +#define WM8998_FLL1_REFCLK 3 +#define WM8998_FLL2_REFCLK 4 + +#endif -- cgit v1.1 From 5b2688a59af686f7c0a80edc49d7f190365ac090 Mon Sep 17 00:00:00 2001 From: Vedang Patel Date: Wed, 30 Sep 2015 17:28:47 +0800 Subject: ASoC: topology: ABI - Add PCM Support and bump ABI version to 4 The struct snd_soc_tplg_pcm_dai is renamed to snd_soc_tplg_pcm. This struct will now be used to handle data related to PCMs (FE DAI & DAI links). It's not for BE, because BE DAI mappings will be provided by ACPI/FDT data. Remove the unused struct snd_soc_tplg_pcm_cfg_caps. We are using snd_soc_tplg_stream and snd_soc_stream_caps instead. Bump ABI version to 4. Signed-off-by: Vedang Patel Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 69d01cd..8d7ec80 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1558,7 +1558,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_pcm_dai), count, + sizeof(struct snd_soc_tplg_pcm), count, hdr->payload_size, "PCM DAI")) { dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", count); @@ -1566,7 +1566,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); - tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count; + tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); if (dobj == NULL) -- cgit v1.1 From 924eb475126fd6bc23c475ac742a69fc466c0b75 Mon Sep 17 00:00:00 2001 From: yitian Date: Tue, 29 Sep 2015 22:43:17 +0800 Subject: ASoC: dwc: fix dma stop transferring issue Designware I2S uses tx empty and rx available signals as the DMA handshaking signals. during music playing, if XRUN occurs, i2s_stop() function will be executed and both tx and rx irq are masked, when music continues to be played, i2s_start() is executed but both tx and rx irq are not unmasked which cause I2S stop sending DMA handshaking signal to DMA controller, and it finally causes music playing will be stopped once XRUN occurs for the first time. [On list discussion suggests this may be partly a race condition on slow systems -- broonie] Signed-off-by: Yitian Bu Signed-off-by: Mark Brown --- sound/soc/dwc/designware_i2s.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 0d28e3b..ba34252 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -141,13 +141,22 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) static void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { - + u32 i, irq; i2s_write_reg(dev->i2s_base, IER, 1); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < 4; i++) { + irq = i2s_read_reg(dev->i2s_base, IMR(i)); + i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30); + } i2s_write_reg(dev->i2s_base, ITER, 1); - else + } else { + for (i = 0; i < 4; i++) { + irq = i2s_read_reg(dev->i2s_base, IMR(i)); + i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03); + } i2s_write_reg(dev->i2s_base, IRER, 1); + } i2s_write_reg(dev->i2s_base, CER, 1); } -- cgit v1.1 From 6d817c0e9fd7536be76690bfdee88e8a81c16f7d Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 29 Sep 2015 16:44:01 +0100 Subject: ASoC: codecs: Add da7219 codec driver This adds support for the DA7219 audio codec with built-in advanced accessory detect features. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/da7219-aad.c | 823 +++++++++++++++++ sound/soc/codecs/da7219-aad.h | 212 +++++ sound/soc/codecs/da7219.c | 1940 +++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/da7219.h | 820 +++++++++++++++++ 6 files changed, 3801 insertions(+) create mode 100644 sound/soc/codecs/da7219-aad.c create mode 100644 sound/soc/codecs/da7219-aad.h create mode 100644 sound/soc/codecs/da7219.c create mode 100644 sound/soc/codecs/da7219.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733e..c9a895c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -57,6 +57,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C + select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C select SND_SOC_DMIC @@ -430,6 +431,9 @@ config SND_SOC_DA7210 config SND_SOC_DA7213 tristate +config SND_SOC_DA7219 + tristate + config SND_SOC_DA732X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077..eb10a34 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -49,6 +49,7 @@ snd-soc-cs4349-objs := cs4349.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o +snd-soc-da7219-objs := da7219.o da7219-aad.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-bt-sco-objs := bt-sco.o @@ -241,6 +242,7 @@ obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o +obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c new file mode 100644 index 0000000..9459593 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.c @@ -0,0 +1,823 @@ +/* + * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * Detection control + */ + +void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + da7219->aad->jack = jack; + da7219->aad->jack_inserted = false; + + /* Send an initial empty report */ + snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK); + + /* Enable/Disable jack detection */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, + (jack ? DA7219_ACCDET_EN_MASK : 0)); +} +EXPORT_SYMBOL_GPL(da7219_aad_jack_det); + +/* + * Button/HPTest work + */ + +static void da7219_aad_btn_det_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, btn_det_work); + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + u8 statusa, micbias_ctrl; + bool micbias_up = false; + int retries = 0; + + /* Drive headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + /* Make sure mic bias is up */ + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + do { + statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A); + if (statusa & DA7219_MICBIAS_UP_STS_MASK) + micbias_up = true; + else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES) + msleep(DA7219_AAD_MICBIAS_CHK_DELAY); + } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES)); + + if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES) + dev_warn(codec->dev, "Mic bias status check timed out"); + + /* + * Mic bias pulse required to enable mic, must be done before enabling + * button detection to prevent erroneous button readings. + */ + if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) { + /* Pulse higher level voltage */ + micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL); + snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_LEVEL_MASK, + da7219_aad->micbias_pulse_lvl); + msleep(da7219_aad->micbias_pulse_time); + snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl); + + } + + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, + da7219_aad->btn_cfg); +} + +static void da7219_aad_hptest_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, hptest_work); + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + u16 tonegen_freq_hptest; + u8 accdet_cfg8; + int report = 0; + + /* Lock DAPM and any Kcontrols that are affected by this test */ + snd_soc_dapm_mutex_lock(dapm); + mutex_lock(&da7219->lock); + + /* Bypass cache so it saves current settings */ + regcache_cache_bypass(da7219->regmap, true); + + /* Make sure Tone Generator is disabled */ + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0); + + /* Enable HPTest block, 1KOhms check */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK, + DA7219_HPTEST_EN_MASK | + DA7219_HPTEST_RES_SEL_1KOHMS); + + /* Set gains to 0db */ + snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB); + snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB); + + /* Disable DAC filters, EQs and soft mute */ + snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK, + 0); + snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK, + 0); + snd_soc_update_bits(codec, DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_MASK, 0); + + /* Enable HP left & right paths */ + snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK, + DA7219_CP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK, + DA7219_DAC_L_SRC_TONEGEN | + DA7219_DAC_R_SRC_TONEGEN); + snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, + DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK, + DA7219_DAC_L_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, + DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK, + DA7219_DAC_R_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_MASK, + DA7219_MIXOUT_L_MIX_SELECT_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_MASK, + DA7219_MIXOUT_R_MIX_SELECT_MASK); + snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_OUTFILT_ST_1L_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1L); + snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R, + DA7219_OUTFILT_ST_1R_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1R); + snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_MASK, + DA7219_MIXOUT_L_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_MASK, + DA7219_MIXOUT_R_AMP_EN_MASK); + snd_soc_write(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); + snd_soc_write(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); + + /* Configure & start Tone Generator */ + snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK); + tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); + regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + &tonegen_freq_hptest, sizeof(tonegen_freq_hptest)); + snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2, + DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK, + DA7219_SWG_SEL_SRAMP | + DA7219_TONE_GEN_GAIN_MINUS_15DB); + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Grab comparator reading */ + accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8); + if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK) + report |= SND_JACK_HEADPHONE; + else + report |= SND_JACK_LINEOUT; + + /* Stop tone generator */ + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Restore original settings from cache */ + regcache_mark_dirty(da7219->regmap); + regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_DROUTING_ST_OUTFILT_1R); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_R_SELECT); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC, + DA7219_DIG_ROUTING_DAC); + regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5, + DA7219_DAC_FILTERS5); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4, + DA7219_DAC_FILTERS1); + regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER, + DA7219_TONE_GEN_ON_PER); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + DA7219_TONE_GEN_FREQ1_U); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1, + DA7219_TONE_GEN_CFG2); + + regcache_cache_bypass(da7219->regmap, false); + + /* Disable HPTest block */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK, 0); + + /* Drive Headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + mutex_unlock(&da7219->lock); + snd_soc_dapm_mutex_unlock(dapm); + + /* + * Only send report if jack hasn't been removed during process, + * otherwise it's invalid and we drop it. + */ + if (da7219_aad->jack_inserted) + snd_soc_jack_report(da7219_aad->jack, report, + SND_JACK_HEADSET | SND_JACK_LINEOUT); +} + + +/* + * IRQ + */ + +static irqreturn_t da7219_aad_irq_thread(int irq, void *data) +{ + struct da7219_aad_priv *da7219_aad = data; + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 events[DA7219_AAD_IRQ_REG_MAX]; + u8 statusa; + int i, report = 0, mask = 0; + + /* Read current IRQ events */ + regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + + if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B]) + return IRQ_NONE; + + /* Read status register for jack insertion & type status */ + statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A); + + /* Clear events */ + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + + dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n", + events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B], + statusa); + + if (statusa & DA7219_JACK_INSERTION_STS_MASK) { + /* Jack Insertion */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_INSERTED_MASK) { + report |= SND_JACK_MECHANICAL; + mask |= SND_JACK_MECHANICAL; + da7219_aad->jack_inserted = true; + } + + /* Jack type detection */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_DETECT_COMPLETE_MASK) { + /* + * If 4-pole, then enable button detection, else perform + * HP impedance test to determine output type to report. + * + * We schedule work here as the tasks themselves can + * take time to complete, and in particular for hptest + * we want to be able to check if the jack was removed + * during the procedure as this will invalidate the + * result. By doing this as work, the IRQ thread can + * handle a removal, and we can check at the end of + * hptest if we have a valid result or not. + */ + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + report |= SND_JACK_HEADSET; + mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT; + schedule_work(&da7219_aad->btn_det_work); + } else { + schedule_work(&da7219_aad->hptest_work); + } + } + + /* Button support for 4-pole jack */ + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Press */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_PRESSED_MASK << i)) { + report |= SND_JACK_BTN_0 >> i; + mask |= SND_JACK_BTN_0 >> i; + } + } + snd_soc_jack_report(da7219_aad->jack, report, mask); + + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Release */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) { + report &= ~(SND_JACK_BTN_0 >> i); + mask |= SND_JACK_BTN_0 >> i; + } + } + } + } else { + /* Jack removal */ + if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) { + report = 0; + mask |= DA7219_AAD_REPORT_ALL_MASK; + da7219_aad->jack_inserted = false; + + /* Un-drive headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, 0); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, 0); + + /* Ensure button detection disabled */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + /* Disable mic bias */ + snd_soc_dapm_disable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + /* Cancel any pending work */ + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); + } + } + + snd_soc_jack_report(da7219_aad->jack, report, mask); + + return IRQ_HANDLED; +} + +/* + * DT to pdata conversion + */ + +static enum da7219_aad_micbias_pulse_lvl + da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 2800: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V; + case 2900: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V; + default: + dev_warn(codec->dev, "Invalid micbias pulse level"); + return DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + } +} + +static enum da7219_aad_btn_cfg + da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 2: + return DA7219_AAD_BTN_CFG_2MS; + case 5: + return DA7219_AAD_BTN_CFG_5MS; + case 10: + return DA7219_AAD_BTN_CFG_10MS; + case 50: + return DA7219_AAD_BTN_CFG_50MS; + case 100: + return DA7219_AAD_BTN_CFG_100MS; + case 200: + return DA7219_AAD_BTN_CFG_200MS; + case 500: + return DA7219_AAD_BTN_CFG_500MS; + default: + dev_warn(codec->dev, "Invalid button config"); + return DA7219_AAD_BTN_CFG_10MS; + } +} + +static enum da7219_aad_mic_det_thr + da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 200: + return DA7219_AAD_MIC_DET_THR_200_OHMS; + case 500: + return DA7219_AAD_MIC_DET_THR_500_OHMS; + case 750: + return DA7219_AAD_MIC_DET_THR_750_OHMS; + case 1000: + return DA7219_AAD_MIC_DET_THR_1000_OHMS; + default: + dev_warn(codec->dev, "Invalid mic detect threshold"); + return DA7219_AAD_MIC_DET_THR_500_OHMS; + } +} + +static enum da7219_aad_jack_ins_deb + da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 5: + return DA7219_AAD_JACK_INS_DEB_5MS; + case 10: + return DA7219_AAD_JACK_INS_DEB_10MS; + case 20: + return DA7219_AAD_JACK_INS_DEB_20MS; + case 50: + return DA7219_AAD_JACK_INS_DEB_50MS; + case 100: + return DA7219_AAD_JACK_INS_DEB_100MS; + case 200: + return DA7219_AAD_JACK_INS_DEB_200MS; + case 500: + return DA7219_AAD_JACK_INS_DEB_500MS; + case 1000: + return DA7219_AAD_JACK_INS_DEB_1S; + default: + dev_warn(codec->dev, "Invalid jack insert debounce"); + return DA7219_AAD_JACK_INS_DEB_20MS; + } +} + +static enum da7219_aad_jack_det_rate + da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "32ms_64ms")) { + return DA7219_AAD_JACK_DET_RATE_32_64MS; + } else if (!strcmp(str, "64ms_128ms")) { + return DA7219_AAD_JACK_DET_RATE_64_128MS; + } else if (!strcmp(str, "128ms_256ms")) { + return DA7219_AAD_JACK_DET_RATE_128_256MS; + } else if (!strcmp(str, "256ms_512ms")) { + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } else { + dev_warn(codec->dev, "Invalid jack detect rate"); + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } +} + +static enum da7219_aad_jack_rem_deb + da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_JACK_REM_DEB_1MS; + case 5: + return DA7219_AAD_JACK_REM_DEB_5MS; + case 10: + return DA7219_AAD_JACK_REM_DEB_10MS; + case 20: + return DA7219_AAD_JACK_REM_DEB_20MS; + default: + dev_warn(codec->dev, "Invalid jack removal debounce"); + return DA7219_AAD_JACK_REM_DEB_1MS; + } +} + +static enum da7219_aad_btn_avg + da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_BTN_AVG_1; + case 2: + return DA7219_AAD_BTN_AVG_2; + case 4: + return DA7219_AAD_BTN_AVG_4; + case 8: + return DA7219_AAD_BTN_AVG_8; + default: + dev_warn(codec->dev, "Invalid button average value"); + return DA7219_AAD_BTN_AVG_2; + } +} + +static enum da7219_aad_adc_1bit_rpt + da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_ADC_1BIT_RPT_1; + case 2: + return DA7219_AAD_ADC_1BIT_RPT_2; + case 4: + return DA7219_AAD_ADC_1BIT_RPT_4; + case 8: + return DA7219_AAD_ADC_1BIT_RPT_8; + default: + dev_warn(codec->dev, "Invalid ADC 1-bit repeat value"); + return DA7219_AAD_ADC_1BIT_RPT_1; + } +} + +static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad"); + struct da7219_aad_pdata *aad_pdata; + const char *of_str; + u32 of_val32; + + if (!aad_np) + return NULL; + + aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL); + if (!aad_pdata) + goto out; + + aad_pdata->irq = irq_of_parse_and_map(np, 0); + + if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl", + &of_val32) >= 0) + aad_pdata->micbias_pulse_lvl = + da7219_aad_of_micbias_pulse_lvl(codec, of_val32); + else + aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + + if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time", + &of_val32) >= 0) + aad_pdata->micbias_pulse_time = of_val32; + + if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0) + aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32); + else + aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS; + + if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0) + aad_pdata->mic_det_thr = + da7219_aad_of_mic_det_thr(codec, of_val32); + else + aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS; + + if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0) + aad_pdata->jack_ins_deb = + da7219_aad_of_jack_ins_deb(codec, of_val32); + else + aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; + + if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str)) + aad_pdata->jack_det_rate = + da7219_aad_of_jack_det_rate(codec, of_str); + else + aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS; + + if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0) + aad_pdata->jack_rem_deb = + da7219_aad_of_jack_rem_deb(codec, of_val32); + else + aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS; + + if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0) + aad_pdata->a_d_btn_thr = (u8) of_val32; + else + aad_pdata->a_d_btn_thr = 0xA; + + if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0) + aad_pdata->d_b_btn_thr = (u8) of_val32; + else + aad_pdata->d_b_btn_thr = 0x16; + + if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0) + aad_pdata->b_c_btn_thr = (u8) of_val32; + else + aad_pdata->b_c_btn_thr = 0x21; + + if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0) + aad_pdata->c_mic_btn_thr = (u8) of_val32; + else + aad_pdata->c_mic_btn_thr = 0x3E; + + if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0) + aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32); + else + aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2; + + if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0) + aad_pdata->adc_1bit_rpt = + da7219_aad_of_adc_1bit_rpt(codec, of_val32); + else + aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; + +out: + of_node_put(aad_np); + + return aad_pdata; +} + +static void da7219_aad_handle_pdata(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct da7219_pdata *pdata = da7219->pdata; + + if ((pdata) && (pdata->aad_pdata)) { + struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata; + u8 cfg, mask; + + da7219_aad->irq = aad_pdata->irq; + + switch (aad_pdata->micbias_pulse_lvl) { + case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V: + case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V: + da7219_aad->micbias_pulse_lvl = + (aad_pdata->micbias_pulse_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + default: + break; + } + + da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time; + + switch (aad_pdata->btn_cfg) { + case DA7219_AAD_BTN_CFG_2MS: + case DA7219_AAD_BTN_CFG_5MS: + case DA7219_AAD_BTN_CFG_10MS: + case DA7219_AAD_BTN_CFG_50MS: + case DA7219_AAD_BTN_CFG_100MS: + case DA7219_AAD_BTN_CFG_200MS: + case DA7219_AAD_BTN_CFG_500MS: + da7219_aad->btn_cfg = (aad_pdata->btn_cfg << + DA7219_BUTTON_CONFIG_SHIFT); + } + + cfg = 0; + mask = 0; + switch (aad_pdata->mic_det_thr) { + case DA7219_AAD_MIC_DET_THR_200_OHMS: + case DA7219_AAD_MIC_DET_THR_500_OHMS: + case DA7219_AAD_MIC_DET_THR_750_OHMS: + case DA7219_AAD_MIC_DET_THR_1000_OHMS: + cfg |= (aad_pdata->mic_det_thr << + DA7219_MIC_DET_THRESH_SHIFT); + mask |= DA7219_MIC_DET_THRESH_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg); + + cfg = 0; + mask = 0; + switch (aad_pdata->jack_ins_deb) { + case DA7219_AAD_JACK_INS_DEB_5MS: + case DA7219_AAD_JACK_INS_DEB_10MS: + case DA7219_AAD_JACK_INS_DEB_20MS: + case DA7219_AAD_JACK_INS_DEB_50MS: + case DA7219_AAD_JACK_INS_DEB_100MS: + case DA7219_AAD_JACK_INS_DEB_200MS: + case DA7219_AAD_JACK_INS_DEB_500MS: + case DA7219_AAD_JACK_INS_DEB_1S: + cfg |= (aad_pdata->jack_ins_deb << + DA7219_JACKDET_DEBOUNCE_SHIFT); + mask |= DA7219_JACKDET_DEBOUNCE_MASK; + } + switch (aad_pdata->jack_det_rate) { + case DA7219_AAD_JACK_DET_RATE_32_64MS: + case DA7219_AAD_JACK_DET_RATE_64_128MS: + case DA7219_AAD_JACK_DET_RATE_128_256MS: + case DA7219_AAD_JACK_DET_RATE_256_512MS: + cfg |= (aad_pdata->jack_det_rate << + DA7219_JACK_DETECT_RATE_SHIFT); + mask |= DA7219_JACK_DETECT_RATE_MASK; + } + switch (aad_pdata->jack_rem_deb) { + case DA7219_AAD_JACK_REM_DEB_1MS: + case DA7219_AAD_JACK_REM_DEB_5MS: + case DA7219_AAD_JACK_REM_DEB_10MS: + case DA7219_AAD_JACK_REM_DEB_20MS: + cfg |= (aad_pdata->jack_rem_deb << + DA7219_JACKDET_REM_DEB_SHIFT); + mask |= DA7219_JACKDET_REM_DEB_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg); + + snd_soc_write(codec, DA7219_ACCDET_CONFIG_3, + aad_pdata->a_d_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_4, + aad_pdata->d_b_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_5, + aad_pdata->b_c_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_6, + aad_pdata->c_mic_btn_thr); + + cfg = 0; + mask = 0; + switch (aad_pdata->btn_avg) { + case DA7219_AAD_BTN_AVG_1: + case DA7219_AAD_BTN_AVG_2: + case DA7219_AAD_BTN_AVG_4: + case DA7219_AAD_BTN_AVG_8: + cfg |= (aad_pdata->btn_avg << + DA7219_BUTTON_AVERAGE_SHIFT); + mask |= DA7219_BUTTON_AVERAGE_MASK; + } + switch (aad_pdata->adc_1bit_rpt) { + case DA7219_AAD_ADC_1BIT_RPT_1: + case DA7219_AAD_ADC_1BIT_RPT_2: + case DA7219_AAD_ADC_1BIT_RPT_4: + case DA7219_AAD_ADC_1BIT_RPT_8: + cfg |= (aad_pdata->adc_1bit_rpt << + DA7219_ADC_1_BIT_REPEAT_SHIFT); + mask |= DA7219_ADC_1_BIT_REPEAT_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg); + } +} + + +/* + * Init/Exit + */ + +int da7219_aad_init(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + int ret; + + da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL); + if (!da7219_aad) + return -ENOMEM; + + da7219->aad = da7219_aad; + da7219_aad->codec = codec; + + /* Handle any DT/platform data */ + if ((codec->dev->of_node) && (da7219->pdata)) + da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec); + + da7219_aad_handle_pdata(codec); + + /* Disable button detection */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work); + INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work); + + ret = request_threaded_irq(da7219_aad->irq, NULL, + da7219_aad_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "da7219-aad", da7219_aad); + if (ret) { + dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); + return ret; + } + + /* Unmask AAD IRQs */ + memset(mask, 0, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + &mask, DA7219_AAD_IRQ_REG_MAX); + + return 0; +} +EXPORT_SYMBOL_GPL(da7219_aad_init); + +void da7219_aad_exit(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + + /* Mask off AAD IRQs */ + memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + mask, DA7219_AAD_IRQ_REG_MAX); + + free_irq(da7219_aad->irq, da7219_aad); + + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); +} +EXPORT_SYMBOL_GPL(da7219_aad_exit); + +MODULE_DESCRIPTION("ASoC DA7219 AAD Driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h new file mode 100644 index 0000000..4fccf67 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.h @@ -0,0 +1,212 @@ +/* + * da7219-aad.h - DA7322 ASoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_AAD_H +#define __DA7219_AAD_H + +#include +#include +#include +#include + +/* + * Registers + */ + +#define DA7219_ACCDET_STATUS_A 0xC0 +#define DA7219_ACCDET_STATUS_B 0xC1 +#define DA7219_ACCDET_IRQ_EVENT_A 0xC2 +#define DA7219_ACCDET_IRQ_EVENT_B 0xC3 +#define DA7219_ACCDET_IRQ_MASK_A 0xC4 +#define DA7219_ACCDET_IRQ_MASK_B 0xC5 +#define DA7219_ACCDET_CONFIG_1 0xC6 +#define DA7219_ACCDET_CONFIG_2 0xC7 +#define DA7219_ACCDET_CONFIG_3 0xC8 +#define DA7219_ACCDET_CONFIG_4 0xC9 +#define DA7219_ACCDET_CONFIG_5 0xCA +#define DA7219_ACCDET_CONFIG_6 0xCB +#define DA7219_ACCDET_CONFIG_7 0xCC +#define DA7219_ACCDET_CONFIG_8 0xCD + + +/* + * Bit Fields + */ + +/* DA7219_ACCDET_STATUS_A = 0xC0 */ +#define DA7219_JACK_INSERTION_STS_SHIFT 0 +#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0) +#define DA7219_JACK_TYPE_STS_SHIFT 1 +#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1) +#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2 +#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2) +#define DA7219_MICBIAS_UP_STS_SHIFT 3 +#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3) + +/* DA7219_ACCDET_STATUS_B = 0xC1 */ +#define DA7219_BUTTON_TYPE_STS_SHIFT 0 +#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0) + +/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */ +#define DA7219_E_JACK_INSERTED_SHIFT 0 +#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_E_JACK_REMOVED_SHIFT 1 +#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */ +#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */ +#define DA7219_M_JACK_INSERTED_SHIFT 0 +#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_M_JACK_REMOVED_SHIFT 1 +#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */ +#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_1 = 0xC6 */ +#define DA7219_ACCDET_EN_SHIFT 0 +#define DA7219_ACCDET_EN_MASK (0x1 << 0) +#define DA7219_BUTTON_CONFIG_SHIFT 1 +#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1) +#define DA7219_MIC_DET_THRESH_SHIFT 4 +#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4) +#define DA7219_JACK_TYPE_DET_EN_SHIFT 6 +#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6) +#define DA7219_PIN_ORDER_DET_EN_SHIFT 7 +#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_2 = 0xC7 */ +#define DA7219_ACCDET_PAUSE_SHIFT 0 +#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0) +#define DA7219_JACKDET_DEBOUNCE_SHIFT 1 +#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1) +#define DA7219_JACK_DETECT_RATE_SHIFT 4 +#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4) +#define DA7219_JACKDET_REM_DEB_SHIFT 6 +#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6) + +/* DA7219_ACCDET_CONFIG_3 = 0xC8 */ +#define DA7219_A_D_BUTTON_THRESH_SHIFT 0 +#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_4 = 0xC9 */ +#define DA7219_D_B_BUTTON_THRESH_SHIFT 0 +#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_5 = 0xCA */ +#define DA7219_B_C_BUTTON_THRESH_SHIFT 0 +#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_6 = 0xCB */ +#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0 +#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_7 = 0xCC */ +#define DA7219_BUTTON_AVERAGE_SHIFT 0 +#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0) +#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2 +#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2) +#define DA7219_PIN_ORDER_FORCE_SHIFT 4 +#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4) +#define DA7219_JACK_TYPE_FORCE_SHIFT 5 +#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5) + +/* DA7219_ACCDET_CONFIG_8 = 0xCD */ +#define DA7219_HPTEST_EN_SHIFT 0 +#define DA7219_HPTEST_EN_MASK (0x1 << 0) +#define DA7219_HPTEST_RES_SEL_SHIFT 1 +#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1) +#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1) +#define DA7219_HPTEST_COMP_SHIFT 4 +#define DA7219_HPTEST_COMP_MASK (0x1 << 4) + + +#define DA7219_AAD_MAX_BUTTONS 4 +#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \ + SND_JACK_HEADSET | SND_JACK_LINEOUT | \ + SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +#define DA7219_AAD_MICBIAS_CHK_DELAY 10 +#define DA7219_AAD_MICBIAS_CHK_RETRIES 5 + +#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28 +#define DA7219_AAD_HPTEST_PERIOD 65 + +enum da7219_aad_event_regs { + DA7219_AAD_IRQ_REG_A = 0, + DA7219_AAD_IRQ_REG_B, + DA7219_AAD_IRQ_REG_MAX, +}; + +/* Private data */ +struct da7219_aad_priv { + struct snd_soc_codec *codec; + int irq; + + u8 micbias_pulse_lvl; + u32 micbias_pulse_time; + + u8 btn_cfg; + + struct work_struct btn_det_work; + struct work_struct hptest_work; + + struct snd_soc_jack *jack; + bool jack_inserted; +}; + +/* AAD control */ +void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack); + +/* Init/Exit */ +int da7219_aad_init(struct snd_soc_codec *codec); +void da7219_aad_exit(struct snd_soc_codec *codec); + +#endif /* __DA7219_AAD_H */ diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c new file mode 100644 index 0000000..76f8fc2 --- /dev/null +++ b/sound/soc/codecs/da7219.c @@ -0,0 +1,1940 @@ +/* + * da7219.c - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * TLVs and Enums + */ + +/* Input TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0); +static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0); + +/* Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0); + +static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv, + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -77.25dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0) +); + +static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0); + +/* Input Enums */ +static const char * const da7219_alc_attack_rate_txt[] = { + "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs", + "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs", + "30024/fs" +}; + +static const struct soc_enum da7219_alc_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT, + DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt); + +static const char * const da7219_alc_release_rate_txt[] = { + "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs", + "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs" +}; + +static const struct soc_enum da7219_alc_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT, + DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt); + +static const char * const da7219_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7219_alc_hold_time = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT, + DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt); + +static const char * const da7219_alc_env_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7219_alc_env_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const struct soc_enum da7219_alc_env_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const char * const da7219_alc_anticlip_step_txt[] = { + "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs" +}; + +static const struct soc_enum da7219_alc_anticlip_step = + SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTICLIP_STEP_SHIFT, + DA7219_ALC_ANTICLIP_STEP_MAX, + da7219_alc_anticlip_step_txt); + +/* Input/Output Enums */ +static const char * const da7219_gain_ramp_rate_txt[] = { + "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8", + "Nominal Rate / 16" +}; + +static const struct soc_enum da7219_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT, + DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt); + +static const char * const da7219_hpf_mode_txt[] = { + "Disabled", "Audio", "Voice" +}; + +static const unsigned int da7219_hpf_mode_val[] = { + DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN, +}; + +static const struct soc_enum da7219_adc_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const struct soc_enum da7219_dac_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const char * const da7219_audio_hpf_corner_txt[] = { + "2Hz", "4Hz", "8Hz", "16Hz" +}; + +static const struct soc_enum da7219_adc_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const struct soc_enum da7219_dac_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const char * const da7219_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da7219_adc_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const struct soc_enum da7219_dac_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const char * const da7219_tonegen_dtmf_key_txt[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", + "*", "#" +}; + +static const struct soc_enum da7219_tonegen_dtmf_key = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT, + DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt); + +static const char * const da7219_tonegen_swg_sel_txt[] = { + "Sum", "SWG1", "SWG2", "SWG1_1-Cos" +}; + +static const struct soc_enum da7219_tonegen_swg_sel = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT, + DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt); + +/* Output Enums */ +static const char * const da7219_dac_softmute_rate_txt[] = { + "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples", + "32 Samples", "64 Samples" +}; + +static const struct soc_enum da7219_dac_softmute_rate = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT, + DA7219_DAC_SOFTMUTE_RATE_MAX, + da7219_dac_softmute_rate_txt); + +static const char * const da7219_dac_ng_setup_time_txt[] = { + "256 Samples", "512 Samples", "1024 Samples", "2048 Samples" +}; + +static const struct soc_enum da7219_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_SETUP_TIME_SHIFT, + DA7219_DAC_NG_SETUP_TIME_MAX, + da7219_dac_ng_setup_time_txt); + +static const char * const da7219_dac_ng_rampup_txt[] = { + "0.22ms/dB", "0.0138ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPUP_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampup_txt); + +static const char * const da7219_dac_ng_rampdown_txt[] = { + "0.88ms/dB", "14.08ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPDN_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampdown_txt); + + +static const char * const da7219_cp_track_mode_txt[] = { + "Largest Volume", "DAC Volume", "Signal Magnitude" +}; + +static const unsigned int da7219_cp_track_mode_val[] = { + DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL, + DA7219_CP_MCHANGE_SIG_MAG +}; + +static const struct soc_enum da7219_cp_track_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT, + DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX, + da7219_cp_track_mode_txt, + da7219_cp_track_mode_val); + + +/* + * Control Functions + */ + +/* Locked Kcontrol calls */ +static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +/* ALC */ +static void da7219_alc_calib(struct snd_soc_codec *codec) +{ + u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl; + + /* Save current state of mic control register */ + mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL); + + /* Save current state of input mixer control register */ + mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL); + + /* Save current state of input ADC control register */ + adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL); + + /* Enable then Mute MIC PGAs */ + snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK, + DA7219_MIC_1_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_MASK, + DA7219_MIC_1_AMP_MUTE_EN_MASK); + + /* Enable input mixers unmuted */ + snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_MASK | + DA7219_MIXIN_L_AMP_MUTE_EN_MASK, + DA7219_MIXIN_L_AMP_EN_MASK); + + /* Enable input filters unmuted */ + snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK, + DA7219_ADC_L_EN_MASK); + + /* Perform auto calibration */ + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_AUTO_CALIB_EN_MASK, + DA7219_ALC_AUTO_CALIB_EN_MASK); + do { + calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1); + } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK); + + /* If auto calibration fails, disable DC offset, hybrid ALC */ + if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) { + dev_warn(codec->dev, + "ALC auto calibration failed with overflow\n"); + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, 0); + } else { + /* Enable DC offset cancellation, hybrid mode */ + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK); + } + + /* Restore input filter control register to original state */ + snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl); + + /* Restore input mixer control registers to original state */ + snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl); + + /* Restore MIC control registers to original states */ + snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl); +} + +static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* + * If ALC in operation and value of control has been updated, + * make sure calibrated offsets are updated. + */ + if ((ret == 1) && (da7219->alc_en)) + da7219_alc_calib(codec); + + return ret; +} + +static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + + /* Force ALC offset calibration if enabling ALC */ + if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) { + da7219_alc_calib(codec); + da7219->alc_en = false; + } else { + da7219->alc_en = false; + } + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* ToneGen */ +static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + mutex_lock(&da7219->lock); + ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); + mutex_unlock(&da7219->lock); + + if (ret) + return ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to host endianness here. + */ + ucontrol->value.integer.value[0] = le16_to_cpu(val); + + return 0; +} + +static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to little endian here to align with + * HW registers. + */ + val = cpu_to_le16(ucontrol->value.integer.value[0]); + + mutex_lock(&da7219->lock); + ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val)); + mutex_unlock(&da7219->lock); + + return ret; +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7219_snd_controls[] = { + /* Mics */ + SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN, + DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX, + DA7219_NO_INVERT, da7219_mic_gain_tlv), + SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Mixer Input */ + SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN, + DA7219_MIXIN_L_AMP_GAIN_SHIFT, + DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_mixin_gain_put, + da7219_mixin_gain_tlv), + SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ADC */ + SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN, + DA7219_ADC_L_DIGITAL_GAIN_SHIFT, + DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_adc_dig_gain_tlv), + SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ALC */ + SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7219_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7219_alc_hold_time), + SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate), + SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate), + SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE, + DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN, + DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX, + DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MIN_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MAX_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step), + SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_alc_sw_put), + + /* Input High-Pass Filters */ + SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode), + SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner), + SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner), + + /* Sidetone Filter */ + SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN, + DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX, + DA7219_NO_INVERT, da7219_sidetone_gain_tlv), + SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Tone Generator */ + SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2, + DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put, da7219_tonegen_gain_tlv), + SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1, + DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L, + DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L, + DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER, + DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER, + DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT), + + /* Gain ramping */ + SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate), + + /* DAC High-Pass Filter */ + SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner), + SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner), + + /* DAC 5-Band Equaliser */ + SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Softmute */ + SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate), + SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Noise Gate */ + SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate), + SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH, + DA7219_DAC_NG_OFF_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH, + DA7219_DAC_NG_ON_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* DACs */ + SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT, + DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_dac_dig_gain_tlv), + SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put), + SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* CP */ + SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode), + SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1, + DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX, + DA7219_NO_INVERT), + + /* Headphones */ + SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT, + DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_hp_gain_tlv), + SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL, + DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + + +/* + * DAPM Mux Controls + */ + +static const char * const da7219_out_sel_txt[] = { + "ADC", "Tone Generator", "DAIL", "DAIR" +}; + +static const struct soc_enum da7219_out_dail_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dail_sel_mux = + SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel); + +static const struct soc_enum da7219_out_dair_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dair_sel_mux = + SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel); + +static const struct soc_enum da7219_out_dacl_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacl_sel_mux = + SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel); + +static const struct soc_enum da7219_out_dacr_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacr_sel_mux = + SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel); + + +/* + * DAPM Mixer Controls + */ + +static const struct snd_kcontrol_new da7219_mixin_controls[] = { + SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT, + DA7219_MIXIN_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_l_controls[] = { + SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_r_controls[] = { + SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +#define DA7219_DMIX_ST_CTRLS(reg) \ + SOC_DAPM_SINGLE("Out FilterL Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Out FilterR Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Sidetone Switch", reg, \ + DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \ + +static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R), +}; + + +/* + * DAPM Events + */ + +static int da7219_dai_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 pll_ctrl, pll_status; + int i = 0; + bool srm_lock = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (da7219->master) + /* Enable DAI clks for master mode */ + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, + DA7219_DAI_CLK_EN_MASK); + + /* PC synchronised to DAI */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, 0); + + /* Slave mode, if SRM not enabled no need for status checks */ + pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL); + if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM) + return 0; + + /* Check SRM has locked */ + do { + pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS); + if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) { + srm_lock = true; + } else { + ++i; + msleep(50); + } + } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock)); + + if (!srm_lock) + dev_warn(codec->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Disable DAI clks if in master mode */ + if (da7219->master) + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* + * DAPM Widgets + */ + +static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { + /* Input Supplies */ + SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC"), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Input Filters */ + SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT, + DA7219_NO_INVERT), + + /* Tone Generator */ + SND_SOC_DAPM_SIGGEN("TONE"), + SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1, + DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0), + + /* Sidetone Input */ + SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT), + + /* Input Mixer Supply */ + SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Input Mixer */ + SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0, + da7219_mixin_controls, + ARRAY_SIZE(da7219_mixin_controls)), + + /* Input Muxes */ + SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dail_sel_mux), + SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dair_sel_mux), + + /* DAI Supply */ + SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT, + DA7219_NO_INVERT, da7219_dai_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DAI */ + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Muxes */ + SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacl_sel_mux), + SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacr_sel_mux), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_mixout_l_controls, + ARRAY_SIZE(da7219_mixout_l_controls)), + SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7219_mixout_r_controls, + ARRAY_SIZE(da7219_mixout_r_controls)), + + /* Sidetone Mixers */ + SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_st_out_filtl_mix_controls, + ARRAY_SIZE(da7219_st_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, + 0, da7219_st_out_filtr_mix_controls, + ARRAY_SIZE(da7219_st_out_filtr_mix_controls)), + + /* DACs */ + SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT, + DA7219_NO_INVERT), + SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT, + DA7219_NO_INVERT), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), + + /* Output Supplies */ + SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT, + DA7219_NO_INVERT, NULL, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + + +/* + * DAPM Mux Routes + */ + +#define DA7219_OUT_DAI_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIOUT"}, \ + {name, "DAIR", "DAIOUT"} + +#define DA7219_OUT_DAC_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIIN"}, \ + {name, "DAIR", "DAIIN"} + +/* + * DAPM Mixer Routes + */ + +#define DA7219_DMIX_ST_ROUTES(name) \ + {name, "Out FilterL Switch", "Mixer Out FilterL"}, \ + {name, "Out FilterR Switch", "Mixer Out FilterR"}, \ + {name, "Sidetone Switch", "Sidetone Filter"} + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7219_audio_map[] = { + /* Input paths */ + {"MIC", NULL, "Mic Bias"}, + {"Mic PGA", NULL, "MIC"}, + {"Mixin PGA", NULL, "Mic PGA"}, + {"ADC", NULL, "Mixin PGA"}, + + {"Sidetone Filter", NULL, "ADC"}, + {"Mixer In", NULL, "Mixer In Supply"}, + {"Mixer In", "Mic Switch", "ADC"}, + + {"Tone Generator", NULL, "TONE"}, + + DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"), + DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"), + + {"DAIOUT", NULL, "Out DAIL Mux"}, + {"DAIOUT", NULL, "Out DAIR Mux"}, + {"DAIOUT", NULL, "DAI"}, + + /* Output paths */ + {"DAIIN", NULL, "DAI"}, + + DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"), + DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"), + + {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"}, + {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"}, + + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"), + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"), + + {"DACL", NULL, "ST Mixer Out FilterL"}, + {"DACR", NULL, "ST Mixer Out FilterR"}, + + {"Mixout Left PGA", NULL, "DACL"}, + {"Mixout Right PGA", NULL, "DACR"}, + + {"Headphone Left PGA", NULL, "Mixout Left PGA"}, + {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + + {"HPL", NULL, "Headphone Left PGA"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"HPL", NULL, "Charge Pump"}, + {"HPR", NULL, "Charge Pump"}, +}; + + +/* + * DAI operations + */ + +static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) + return 0; + + if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + + switch (clk_id) { + case DA7219_CLKSRC_MCLK_SQR: + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, + DA7219_PLL_MCLK_SQR_EN_MASK); + break; + case DA7219_CLKSRC_MCLK: + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, 0); + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + da7219->clk_src = clk_id; + + if (da7219->mclk) { + freq = clk_round_rate(da7219->mclk, freq); + ret = clk_set_rate(da7219->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7219->mclk_rate = freq; + + return 0; +} + +static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ + if (da7219->mclk_rate == 32768) { + indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; + indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; + } else if (da7219->mclk_rate < 2000000) { + dev_err(codec->dev, "PLL input clock %d below valid range\n", + da7219->mclk_rate); + return -EINVAL; + } else if (da7219->mclk_rate <= 5000000) { + indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; + indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; + } else if (da7219->mclk_rate <= 10000000) { + indiv_bits = DA7219_PLL_INDIV_5_10_MHZ; + indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL; + } else if (da7219->mclk_rate <= 20000000) { + indiv_bits = DA7219_PLL_INDIV_10_20_MHZ; + indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL; + } else if (da7219->mclk_rate <= 40000000) { + indiv_bits = DA7219_PLL_INDIV_20_40_MHZ; + indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7219->mclk_rate <= 54000000) { + indiv_bits = DA7219_PLL_INDIV_40_54_MHZ; + indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL; + } else { + dev_err(codec->dev, "PLL input clock %d above valid range\n", + da7219->mclk_rate); + return -EINVAL; + } + freq_ref = (da7219->mclk_rate / indiv); + pll_ctrl = indiv_bits; + + /* Configure PLL */ + switch (source) { + case DA7219_SYSCLK_MCLK: + pll_ctrl |= DA7219_PLL_MODE_BYPASS; + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | + DA7219_PLL_MODE_MASK, pll_ctrl); + return 0; + case DA7219_SYSCLK_PLL: + pll_ctrl |= DA7219_PLL_MODE_NORMAL; + break; + case DA7219_SYSCLK_PLL_SRM: + pll_ctrl |= DA7219_PLL_MODE_SRM; + break; + case DA7219_SYSCLK_PLL_32KHZ: + pll_ctrl |= DA7219_PLL_MODE_32KHZ; + break; + default: + dev_err(codec->dev, "Invalid PLL config\n"); + return -EINVAL; + } + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7219_BYTE_MASK; + + /* Write PLL config & dividers */ + snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top); + snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer); + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK, + pll_ctrl); + + return 0; +} + +static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 dai_clk_mode = 0, dai_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7219->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7219->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7219_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV | + DA7219_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7219_DAI_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_B: + dai_ctrl |= DA7219_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + /* By default 64 BCLKs per WCLK is supported */ + dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64; + + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK | + DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK, + dai_clk_mode); + snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK, + dai_ctrl); + + return 0; +} + +static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 dai_bclks_per_wclk; + u16 offset; + u32 frame_size; + + /* No channels enabled so disable TDM, revert to 64-bit frames */ + if (!tx_mask) { + snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, 0); + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + DA7219_DAI_BCLKS_PER_WCLK_64); + return 0; + } + + /* Check we have valid slots */ + if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) { + dev_err(codec->dev, "Invalid number of slots, max = %d\n", + DA7219_DAI_TDM_MAX_SLOTS); + return -EINVAL; + } + + /* Check we have a valid offset given */ + if (rx_mask > DA7219_DAI_OFFSET_MAX) { + dev_err(codec->dev, "Invalid slot offset, max = %d\n", + DA7219_DAI_OFFSET_MAX); + return -EINVAL; + } + + /* Calculate & validate frame size based on slot info provided. */ + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(codec->dev, "Invalid frame size %d\n", frame_size); + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + + offset = cpu_to_le16(rx_mask); + regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER, + &offset, sizeof(offset)); + + snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, + (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | + DA7219_DAI_TDM_MODE_EN_MASK); + + return 0; +} + +static int da7219_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dai_ctrl = 0, fs; + unsigned int channels; + + switch (params_width(params)) { + case 16: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE; + break; + case 20: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE; + break; + case 24: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE; + break; + case 32: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + channels = params_channels(params); + if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) { + dev_err(codec->dev, + "Invalid number of channels, only 1 to %d supported\n", + DA7219_DAI_CH_NUM_MAX); + return -EINVAL; + } + dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT; + + switch (params_rate(params)) { + case 8000: + fs = DA7219_SR_8000; + break; + case 11025: + fs = DA7219_SR_11025; + break; + case 12000: + fs = DA7219_SR_12000; + break; + case 16000: + fs = DA7219_SR_16000; + break; + case 22050: + fs = DA7219_SR_22050; + break; + case 24000: + fs = DA7219_SR_24000; + break; + case 32000: + fs = DA7219_SR_32000; + break; + case 44100: + fs = DA7219_SR_44100; + break; + case 48000: + fs = DA7219_SR_48000; + break; + case 88200: + fs = DA7219_SR_88200; + break; + case 96000: + fs = DA7219_SR_96000; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7219_DAI_CTRL, + DA7219_DAI_WORD_LENGTH_MASK | + DA7219_DAI_CH_NUM_MASK, + dai_ctrl); + snd_soc_write(codec, DA7219_SR, fs); + + return 0; +} + +static const struct snd_soc_dai_ops da7219_dai_ops = { + .hw_params = da7219_hw_params, + .set_sysclk = da7219_set_dai_sysclk, + .set_pll = da7219_set_dai_pll, + .set_fmt = da7219_set_dai_fmt, + .set_tdm_slot = da7219_set_dai_tdm_slot, +}; + +#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver da7219_dai = { + .name = "da7219-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7219_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7219_FORMATS, + }, + .ops = &da7219_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, +}; + + +/* + * DT + */ + +static const struct of_device_id da7219_of_match[] = { + { .compatible = "dlg,da7219", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7219_of_match); + +static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec, + u32 val) +{ + switch (val) { + case 1050: + return DA7219_LDO_LVL_SEL_1_05V; + case 1100: + return DA7219_LDO_LVL_SEL_1_10V; + case 1200: + return DA7219_LDO_LVL_SEL_1_20V; + case 1400: + return DA7219_LDO_LVL_SEL_1_40V; + default: + dev_warn(codec->dev, "Invalid LDO level"); + return DA7219_LDO_LVL_SEL_1_05V; + } +} + +static enum da7219_micbias_voltage + da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1800: + return DA7219_MICBIAS_1_8V; + case 2000: + return DA7219_MICBIAS_2_0V; + case 2200: + return DA7219_MICBIAS_2_2V; + case 2400: + return DA7219_MICBIAS_2_4V; + case 2600: + return DA7219_MICBIAS_2_6V; + default: + dev_warn(codec->dev, "Invalid micbias level"); + return DA7219_MICBIAS_2_2V; + } +} + +static enum da7219_mic_amp_in_sel + da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "diff")) { + return DA7219_MIC_AMP_IN_SEL_DIFF; + } else if (!strcmp(str, "se_p")) { + return DA7219_MIC_AMP_IN_SEL_SE_P; + } else if (!strcmp(str, "se_n")) { + return DA7219_MIC_AMP_IN_SEL_SE_N; + } else { + dev_warn(codec->dev, "Invalid mic input type selection"); + return DA7219_MIC_AMP_IN_SEL_DIFF; + } +} + +static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct da7219_pdata *pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0) + pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32); + + if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0) + pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32); + else + pdata->micbias_lvl = DA7219_MICBIAS_2_2V; + + if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str)) + pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str); + else + pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF; + + return pdata; +} + + +/* + * Codec driver functions + */ + +static int da7219_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* MCLK */ + clk_prepare_enable(da7219->mclk); + + /* Master bias */ + snd_soc_update_bits(codec, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, + DA7219_BIAS_EN_MASK); + + /* Enable Internal Digital LDO */ + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_EN_MASK, + DA7219_LDO_EN_MASK); + } + break; + case SND_SOC_BIAS_OFF: + /* Only disable if jack detection not active */ + if (!da7219->aad->jack) { + /* Bypass Internal Digital LDO */ + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_EN_MASK, 0); + + /* Master bias */ + snd_soc_update_bits(codec, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, 0); + } + + /* MCLK */ + clk_disable_unprepare(da7219->mclk); + break; + } + + return 0; +} + +static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = { + [DA7219_SUPPLY_VDD] = "VDD", + [DA7219_SUPPLY_VDDMIC] = "VDDMIC", + [DA7219_SUPPLY_VDDIO] = "VDDIO", +}; + +static int da7219_handle_supplies(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct regulator *vddio; + u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; + int i, ret; + + /* Get required supplies */ + for (i = 0; i < DA7219_NUM_SUPPLIES; ++i) + da7219->supplies[i].supply = da7219_supply_names[i]; + + ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES, + da7219->supplies); + if (ret) + return ret; + + /* Determine VDDIO voltage provided */ + vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer; + ret = regulator_get_voltage(vddio); + if (ret < 1200000) + dev_warn(codec->dev, "Invalid VDDIO voltage\n"); + else if (ret < 2800000) + io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V; + + /* Enable main supplies */ + ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); + + /* Ensure device in active mode */ + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK); + + /* Update IO voltage level range */ + snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl); + + return ret; +} + +static void da7219_handle_pdata(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_pdata *pdata = da7219->pdata; + + if (pdata) { + u8 micbias_lvl = 0; + + /* Internal LDO */ + switch (pdata->ldo_lvl_sel) { + case DA7219_LDO_LVL_SEL_1_05V: + case DA7219_LDO_LVL_SEL_1_10V: + case DA7219_LDO_LVL_SEL_1_20V: + case DA7219_LDO_LVL_SEL_1_40V: + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_LEVEL_SELECT_MASK, + (pdata->ldo_lvl_sel << + DA7219_LDO_LEVEL_SELECT_SHIFT)); + break; + } + + /* Mic Bias voltages */ + switch (pdata->micbias_lvl) { + case DA7219_MICBIAS_1_8V: + case DA7219_MICBIAS_2_0V: + case DA7219_MICBIAS_2_2V: + case DA7219_MICBIAS_2_4V: + case DA7219_MICBIAS_2_6V: + micbias_lvl |= (pdata->micbias_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + } + + snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl); + + /* Mic */ + switch (pdata->mic_amp_in_sel) { + case DA7219_MIC_AMP_IN_SEL_DIFF: + case DA7219_MIC_AMP_IN_SEL_SE_P: + case DA7219_MIC_AMP_IN_SEL_SE_N: + snd_soc_write(codec, DA7219_MIC_1_SELECT, + pdata->mic_amp_in_sel); + break; + } + } +} + +static int da7219_probe(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_init(&da7219->lock); + + /* Regulator configuration */ + ret = da7219_handle_supplies(codec); + if (ret) + return ret; + + /* Handle DT/Platform data */ + if (codec->dev->of_node) + da7219->pdata = da7219_of_to_pdata(codec); + else + da7219->pdata = dev_get_platdata(codec->dev); + + da7219_handle_pdata(codec); + + /* Check if MCLK provided */ + da7219->mclk = devm_clk_get(codec->dev, "mclk"); + if (IS_ERR(da7219->mclk)) { + if (PTR_ERR(da7219->mclk) != -ENOENT) + return PTR_ERR(da7219->mclk); + else + da7219->mclk = NULL; + } + + /* Default PC counter to free-running */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Default gain ramping */ + snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK, + DA7219_ADC_L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK, + DA7219_DAC_L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK, + DA7219_DAC_R_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_RAMP_EN_MASK, + DA7219_HP_L_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_RAMP_EN_MASK, + DA7219_HP_R_AMP_RAMP_EN_MASK); + + /* Default infinite tone gen, start/stop by Kcontrol */ + snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK); + + /* Initialise AAD block */ + return da7219_aad_init(codec); +} + +static int da7219_remove(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + da7219_aad_exit(codec); + + /* Supplies */ + return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); +} + +#ifdef CONFIG_PM +static int da7219_suspend(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + + /* Put device into standby mode if jack detection disabled */ + if (!da7219->aad->jack) + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0); + + return 0; +} + +static int da7219_resume(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + /* Put device into active mode if previously pushed to standby */ + if (!da7219->aad->jack) + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK); + + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define da7219_suspend NULL +#define da7219_resume NULL +#endif + +static struct snd_soc_codec_driver soc_codec_dev_da7219 = { + .probe = da7219_probe, + .remove = da7219_remove, + .suspend = da7219_suspend, + .resume = da7219_resume, + .set_bias_level = da7219_set_bias_level, + + .controls = da7219_snd_controls, + .num_controls = ARRAY_SIZE(da7219_snd_controls), + + .dapm_widgets = da7219_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets), + .dapm_routes = da7219_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7219_audio_map), +}; + + +/* + * Regmap configs + */ + +static struct reg_default da7219_reg_defaults[] = { + { DA7219_MIC_1_SELECT, 0x00 }, + { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, + { DA7219_SR_24_48, 0x00 }, + { DA7219_SR, 0x0A }, + { DA7219_CIF_I2C_ADDR_CFG, 0x02 }, + { DA7219_PLL_CTRL, 0x10 }, + { DA7219_PLL_FRAC_TOP, 0x00 }, + { DA7219_PLL_FRAC_BOT, 0x00 }, + { DA7219_PLL_INTEGER, 0x20 }, + { DA7219_DIG_ROUTING_DAI, 0x10 }, + { DA7219_DAI_CLK_MODE, 0x01 }, + { DA7219_DAI_CTRL, 0x28 }, + { DA7219_DAI_TDM_CTRL, 0x40 }, + { DA7219_DIG_ROUTING_DAC, 0x32 }, + { DA7219_DAI_OFFSET_LOWER, 0x00 }, + { DA7219_DAI_OFFSET_UPPER, 0x00 }, + { DA7219_REFERENCES, 0x00 }, + { DA7219_MIXIN_L_SELECT, 0x00 }, + { DA7219_MIXIN_L_GAIN, 0x03 }, + { DA7219_ADC_L_GAIN, 0x6F }, + { DA7219_ADC_FILTERS1, 0x80 }, + { DA7219_MIC_1_GAIN, 0x01 }, + { DA7219_SIDETONE_CTRL, 0x40 }, + { DA7219_SIDETONE_GAIN, 0x0E }, + { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 }, + { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 }, + { DA7219_DAC_FILTERS5, 0x00 }, + { DA7219_DAC_FILTERS2, 0x88 }, + { DA7219_DAC_FILTERS3, 0x88 }, + { DA7219_DAC_FILTERS4, 0x08 }, + { DA7219_DAC_FILTERS1, 0x80 }, + { DA7219_DAC_L_GAIN, 0x6F }, + { DA7219_DAC_R_GAIN, 0x6F }, + { DA7219_CP_CTRL, 0x20 }, + { DA7219_HP_L_GAIN, 0x39 }, + { DA7219_HP_R_GAIN, 0x39 }, + { DA7219_MIXOUT_L_SELECT, 0x00 }, + { DA7219_MIXOUT_R_SELECT, 0x00 }, + { DA7219_MICBIAS_CTRL, 0x03 }, + { DA7219_MIC_1_CTRL, 0x40 }, + { DA7219_MIXIN_L_CTRL, 0x40 }, + { DA7219_ADC_L_CTRL, 0x40 }, + { DA7219_DAC_L_CTRL, 0x40 }, + { DA7219_DAC_R_CTRL, 0x40 }, + { DA7219_HP_L_CTRL, 0x40 }, + { DA7219_HP_R_CTRL, 0x40 }, + { DA7219_MIXOUT_L_CTRL, 0x10 }, + { DA7219_MIXOUT_R_CTRL, 0x10 }, + { DA7219_CHIP_ID1, 0x23 }, + { DA7219_CHIP_ID2, 0x93 }, + { DA7219_CHIP_REVISION, 0x00 }, + { DA7219_LDO_CTRL, 0x00 }, + { DA7219_IO_CTRL, 0x00 }, + { DA7219_GAIN_RAMP_CTRL, 0x00 }, + { DA7219_PC_COUNT, 0x02 }, + { DA7219_CP_VOL_THRESHOLD1, 0x0E }, + { DA7219_DIG_CTRL, 0x00 }, + { DA7219_ALC_CTRL2, 0x00 }, + { DA7219_ALC_CTRL3, 0x00 }, + { DA7219_ALC_NOISE, 0x3F }, + { DA7219_ALC_TARGET_MIN, 0x3F }, + { DA7219_ALC_TARGET_MAX, 0x00 }, + { DA7219_ALC_GAIN_LIMITS, 0xFF }, + { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7219_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7219_ALC_ANTICLIP_LEVEL, 0x00 }, + { DA7219_DAC_NG_SETUP_TIME, 0x00 }, + { DA7219_DAC_NG_OFF_THRESH, 0x00 }, + { DA7219_DAC_NG_ON_THRESH, 0x00 }, + { DA7219_DAC_NG_CTRL, 0x00 }, + { DA7219_TONE_GEN_CFG1, 0x00 }, + { DA7219_TONE_GEN_CFG2, 0x00 }, + { DA7219_TONE_GEN_CYCLES, 0x00 }, + { DA7219_TONE_GEN_FREQ1_L, 0x55 }, + { DA7219_TONE_GEN_FREQ1_U, 0x15 }, + { DA7219_TONE_GEN_FREQ2_L, 0x00 }, + { DA7219_TONE_GEN_FREQ2_U, 0x40 }, + { DA7219_TONE_GEN_ON_PER, 0x02 }, + { DA7219_TONE_GEN_OFF_PER, 0x01 }, + { DA7219_ACCDET_IRQ_MASK_A, 0x00 }, + { DA7219_ACCDET_IRQ_MASK_B, 0x00 }, + { DA7219_ACCDET_CONFIG_1, 0xD6 }, + { DA7219_ACCDET_CONFIG_2, 0x34 }, + { DA7219_ACCDET_CONFIG_3, 0x0A }, + { DA7219_ACCDET_CONFIG_4, 0x16 }, + { DA7219_ACCDET_CONFIG_5, 0x21 }, + { DA7219_ACCDET_CONFIG_6, 0x3E }, + { DA7219_ACCDET_CONFIG_7, 0x01 }, + { DA7219_SYSTEM_ACTIVE, 0x00 }, +}; + +static bool da7219_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7219_MIC_1_GAIN_STATUS: + case DA7219_MIXIN_L_GAIN_STATUS: + case DA7219_ADC_L_GAIN_STATUS: + case DA7219_DAC_L_GAIN_STATUS: + case DA7219_DAC_R_GAIN_STATUS: + case DA7219_HP_L_GAIN_STATUS: + case DA7219_HP_R_GAIN_STATUS: + case DA7219_CIF_CTRL: + case DA7219_PLL_SRM_STS: + case DA7219_ALC_CTRL1: + case DA7219_SYSTEM_MODES_INPUT: + case DA7219_SYSTEM_MODES_OUTPUT: + case DA7219_ALC_OFFSET_AUTO_M_L: + case DA7219_ALC_OFFSET_AUTO_U_L: + case DA7219_TONE_GEN_CFG1: + case DA7219_ACCDET_STATUS_A: + case DA7219_ACCDET_STATUS_B: + case DA7219_ACCDET_IRQ_EVENT_A: + case DA7219_ACCDET_IRQ_EVENT_B: + case DA7219_ACCDET_CONFIG_8: + case DA7219_SYSTEM_STATUS: + return 1; + default: + return 0; + } +} + +static const struct regmap_config da7219_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA7219_SYSTEM_ACTIVE, + .reg_defaults = da7219_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults), + .volatile_reg = da7219_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + + +/* + * I2C layer + */ + +static int da7219_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da7219_priv *da7219; + int ret; + + da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv), + GFP_KERNEL); + if (!da7219) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7219); + + da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config); + if (IS_ERR(da7219->regmap)) { + ret = PTR_ERR(da7219->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219, + &da7219_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n", + ret); + } + return ret; +} + +static int da7219_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id da7219_i2c_id[] = { + { "da7219", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7219_i2c_id); + +static struct i2c_driver da7219_i2c_driver = { + .driver = { + .name = "da7219", + .of_match_table = da7219_of_match, + }, + .probe = da7219_i2c_probe, + .remove = da7219_i2c_remove, + .id_table = da7219_i2c_id, +}; + +module_i2c_driver(da7219_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7219 Codec Driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h new file mode 100644 index 0000000..b514268 --- /dev/null +++ b/sound/soc/codecs/da7219.h @@ -0,0 +1,820 @@ +/* + * da7219.h - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_H +#define __DA7219_H + +#include +#include +#include + +/* + * Registers + */ + +#define DA7219_MIC_1_GAIN_STATUS 0x6 +#define DA7219_MIXIN_L_GAIN_STATUS 0x8 +#define DA7219_ADC_L_GAIN_STATUS 0xA +#define DA7219_DAC_L_GAIN_STATUS 0xC +#define DA7219_DAC_R_GAIN_STATUS 0xD +#define DA7219_HP_L_GAIN_STATUS 0xE +#define DA7219_HP_R_GAIN_STATUS 0xF +#define DA7219_MIC_1_SELECT 0x10 +#define DA7219_CIF_TIMEOUT_CTRL 0x12 +#define DA7219_CIF_CTRL 0x13 +#define DA7219_SR_24_48 0x16 +#define DA7219_SR 0x17 +#define DA7219_CIF_I2C_ADDR_CFG 0x1B +#define DA7219_PLL_CTRL 0x20 +#define DA7219_PLL_FRAC_TOP 0x22 +#define DA7219_PLL_FRAC_BOT 0x23 +#define DA7219_PLL_INTEGER 0x24 +#define DA7219_PLL_SRM_STS 0x25 +#define DA7219_DIG_ROUTING_DAI 0x2A +#define DA7219_DAI_CLK_MODE 0x2B +#define DA7219_DAI_CTRL 0x2C +#define DA7219_DAI_TDM_CTRL 0x2D +#define DA7219_DIG_ROUTING_DAC 0x2E +#define DA7219_ALC_CTRL1 0x2F +#define DA7219_DAI_OFFSET_LOWER 0x30 +#define DA7219_DAI_OFFSET_UPPER 0x31 +#define DA7219_REFERENCES 0x32 +#define DA7219_MIXIN_L_SELECT 0x33 +#define DA7219_MIXIN_L_GAIN 0x34 +#define DA7219_ADC_L_GAIN 0x36 +#define DA7219_ADC_FILTERS1 0x38 +#define DA7219_MIC_1_GAIN 0x39 +#define DA7219_SIDETONE_CTRL 0x3A +#define DA7219_SIDETONE_GAIN 0x3B +#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C +#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D +#define DA7219_DAC_FILTERS5 0x40 +#define DA7219_DAC_FILTERS2 0x41 +#define DA7219_DAC_FILTERS3 0x42 +#define DA7219_DAC_FILTERS4 0x43 +#define DA7219_DAC_FILTERS1 0x44 +#define DA7219_DAC_L_GAIN 0x45 +#define DA7219_DAC_R_GAIN 0x46 +#define DA7219_CP_CTRL 0x47 +#define DA7219_HP_L_GAIN 0x48 +#define DA7219_HP_R_GAIN 0x49 +#define DA7219_MIXOUT_L_SELECT 0x4B +#define DA7219_MIXOUT_R_SELECT 0x4C +#define DA7219_SYSTEM_MODES_INPUT 0x50 +#define DA7219_SYSTEM_MODES_OUTPUT 0x51 +#define DA7219_MICBIAS_CTRL 0x62 +#define DA7219_MIC_1_CTRL 0x63 +#define DA7219_MIXIN_L_CTRL 0x65 +#define DA7219_ADC_L_CTRL 0x67 +#define DA7219_DAC_L_CTRL 0x69 +#define DA7219_DAC_R_CTRL 0x6A +#define DA7219_HP_L_CTRL 0x6B +#define DA7219_HP_R_CTRL 0x6C +#define DA7219_MIXOUT_L_CTRL 0x6E +#define DA7219_MIXOUT_R_CTRL 0x6F +#define DA7219_CHIP_ID1 0x81 +#define DA7219_CHIP_ID2 0x82 +#define DA7219_CHIP_REVISION 0x83 +#define DA7219_LDO_CTRL 0x90 +#define DA7219_IO_CTRL 0x91 +#define DA7219_GAIN_RAMP_CTRL 0x92 +#define DA7219_PC_COUNT 0x94 +#define DA7219_CP_VOL_THRESHOLD1 0x95 +#define DA7219_CP_DELAY 0x96 +#define DA7219_DIG_CTRL 0x99 +#define DA7219_ALC_CTRL2 0x9A +#define DA7219_ALC_CTRL3 0x9B +#define DA7219_ALC_NOISE 0x9C +#define DA7219_ALC_TARGET_MIN 0x9D +#define DA7219_ALC_TARGET_MAX 0x9E +#define DA7219_ALC_GAIN_LIMITS 0x9F +#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0 +#define DA7219_ALC_ANTICLIP_CTRL 0xA1 +#define DA7219_ALC_ANTICLIP_LEVEL 0xA2 +#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3 +#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4 +#define DA7219_DAC_NG_SETUP_TIME 0xAF +#define DA7219_DAC_NG_OFF_THRESH 0xB0 +#define DA7219_DAC_NG_ON_THRESH 0xB1 +#define DA7219_DAC_NG_CTRL 0xB2 +#define DA7219_TONE_GEN_CFG1 0xB4 +#define DA7219_TONE_GEN_CFG2 0xB5 +#define DA7219_TONE_GEN_CYCLES 0xB6 +#define DA7219_TONE_GEN_FREQ1_L 0xB7 +#define DA7219_TONE_GEN_FREQ1_U 0xB8 +#define DA7219_TONE_GEN_FREQ2_L 0xB9 +#define DA7219_TONE_GEN_FREQ2_U 0xBA +#define DA7219_TONE_GEN_ON_PER 0xBB +#define DA7219_TONE_GEN_OFF_PER 0xBC +#define DA7219_SYSTEM_STATUS 0xE0 +#define DA7219_SYSTEM_ACTIVE 0xFD + + +/* + * Bit Fields + */ + +#define DA7219_SWITCH_EN_MAX 0x1 + +/* DA7219_MIC_1_GAIN_STATUS = 0x6 */ +#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0) +#define DA7219_MIC_1_AMP_GAIN_MAX 0x7 + +/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */ +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0) + +/* DA7219_ADC_L_GAIN_STATUS = 0xA */ +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_L_GAIN_STATUS = 0xC */ +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_R_GAIN_STATUS = 0xD */ +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_HP_L_GAIN_STATUS = 0xE */ +#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_HP_R_GAIN_STATUS = 0xF */ +#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_MIC_1_SELECT = 0x10 */ +#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0 +#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */ +#define DA7219_I2C_TIMEOUT_EN_SHIFT 0 +#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7219_CIF_CTRL = 0x13 */ +#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0 +#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0) +#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7 +#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7) + +/* DA7219_SR_24_48 = 0x16 */ +#define DA7219_SR_24_48_SHIFT 0 +#define DA7219_SR_24_48_MASK (0x1 << 0) + +/* DA7219_SR = 0x17 */ +#define DA7219_SR_SHIFT 0 +#define DA7219_SR_MASK (0xF << 0) +#define DA7219_SR_8000 (0x01 << 0) +#define DA7219_SR_11025 (0x02 << 0) +#define DA7219_SR_12000 (0x03 << 0) +#define DA7219_SR_16000 (0x05 << 0) +#define DA7219_SR_22050 (0x06 << 0) +#define DA7219_SR_24000 (0x07 << 0) +#define DA7219_SR_32000 (0x09 << 0) +#define DA7219_SR_44100 (0x0A << 0) +#define DA7219_SR_48000 (0x0B << 0) +#define DA7219_SR_88200 (0x0E << 0) +#define DA7219_SR_96000 (0x0F << 0) + +/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */ +#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0 +#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0) + +/* DA7219_PLL_CTRL = 0x20 */ +#define DA7219_PLL_INDIV_SHIFT 2 +#define DA7219_PLL_INDIV_MASK (0x7 << 2) +#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2) +#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2) +#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2) +#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2) +#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2) +#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5 +#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5) +#define DA7219_PLL_MODE_SHIFT 6 +#define DA7219_PLL_MODE_MASK (0x3 << 6) +#define DA7219_PLL_MODE_BYPASS (0x0 << 6) +#define DA7219_PLL_MODE_NORMAL (0x1 << 6) +#define DA7219_PLL_MODE_SRM (0x2 << 6) +#define DA7219_PLL_MODE_32KHZ (0x3 << 6) + +/* DA7219_PLL_FRAC_TOP = 0x22 */ +#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0) + +/* DA7219_PLL_FRAC_BOT = 0x23 */ +#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0) + +/* DA7219_PLL_INTEGER = 0x24 */ +#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0 +#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0) + +/* DA7219_PLL_SRM_STS = 0x25 */ +#define DA7219_PLL_SRM_STATE_SHIFT 0 +#define DA7219_PLL_SRM_STATE_MASK (0xF << 0) +#define DA7219_PLL_SRM_STATUS_SHIFT 4 +#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4) +#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7) + +/* DA7219_DIG_ROUTING_DAI = 0x2A */ +#define DA7219_DAI_L_SRC_SHIFT 0 +#define DA7219_DAI_L_SRC_MASK (0x3 << 0) +#define DA7219_DAI_R_SRC_SHIFT 4 +#define DA7219_DAI_R_SRC_MASK (0x3 << 4) +#define DA7219_OUT_SRC_MAX 4 + +/* DA7219_DAI_CLK_MODE = 0x2B */ +#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0 +#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7219_DAI_CLK_POL_SHIFT 2 +#define DA7219_DAI_CLK_POL_MASK (0x1 << 2) +#define DA7219_DAI_CLK_POL_INV (0x1 << 2) +#define DA7219_DAI_WCLK_POL_SHIFT 3 +#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3) +#define DA7219_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4 +#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4) +#define DA7219_DAI_CLK_EN_SHIFT 7 +#define DA7219_DAI_CLK_EN_MASK (0x1 << 7) + +/* DA7219_DAI_CTRL = 0x2C */ +#define DA7219_DAI_FORMAT_SHIFT 0 +#define DA7219_DAI_FORMAT_MASK (0x3 << 0) +#define DA7219_DAI_FORMAT_I2S (0x0 << 0) +#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7219_DAI_FORMAT_DSP (0x3 << 0) +#define DA7219_DAI_WORD_LENGTH_SHIFT 2 +#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7219_DAI_CH_NUM_SHIFT 4 +#define DA7219_DAI_CH_NUM_MASK (0x3 << 4) +#define DA7219_DAI_CH_NUM_MAX 2 +#define DA7219_DAI_EN_SHIFT 7 +#define DA7219_DAI_EN_MASK (0x1 << 7) + +/* DA7219_DAI_TDM_CTRL = 0x2D */ +#define DA7219_DAI_TDM_CH_EN_SHIFT 0 +#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0) +#define DA7219_DAI_OE_SHIFT 6 +#define DA7219_DAI_OE_MASK (0x1 << 6) +#define DA7219_DAI_TDM_MODE_EN_SHIFT 7 +#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7) +#define DA7219_DAI_TDM_MAX_SLOTS 2 + +/* DA7219_DIG_ROUTING_DAC = 0x2E */ +#define DA7219_DAC_L_SRC_SHIFT 0 +#define DA7219_DAC_L_SRC_MASK (0x3 << 0) +#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0) +#define DA7219_DAC_L_MONO_SHIFT 3 +#define DA7219_DAC_L_MONO_MASK (0x1 << 3) +#define DA7219_DAC_R_SRC_SHIFT 4 +#define DA7219_DAC_R_SRC_MASK (0x3 << 4) +#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4) +#define DA7219_DAC_R_MONO_SHIFT 7 +#define DA7219_DAC_R_MONO_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL1 = 0x2F */ +#define DA7219_ALC_OFFSET_EN_SHIFT 0 +#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0) +#define DA7219_ALC_SYNC_MODE_SHIFT 1 +#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1) +#define DA7219_ALC_EN_SHIFT 3 +#define DA7219_ALC_EN_MASK (0x1 << 3) +#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4 +#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4) +#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5 +#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5) + +/* DA7219_DAI_OFFSET_LOWER = 0x30 */ +#define DA7219_DAI_OFFSET_LOWER_SHIFT 0 +#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0) + +/* DA7219_DAI_OFFSET_UPPER = 0x31 */ +#define DA7219_DAI_OFFSET_UPPER_SHIFT 0 +#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0) +#define DA7219_DAI_OFFSET_MAX 0x2FF + +/* DA7219_REFERENCES = 0x32 */ +#define DA7219_BIAS_EN_SHIFT 3 +#define DA7219_BIAS_EN_MASK (0x1 << 3) +#define DA7219_VMID_FAST_CHARGE_SHIFT 4 +#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4) + +/* DA7219_MIXIN_L_SELECT = 0x33 */ +#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXIN_L_GAIN = 0x34 */ +#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0) +#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF + +/* DA7219_ADC_L_GAIN = 0x36 */ +#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F + +/* DA7219_ADC_FILTERS1 = 0x38 */ +#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_VOICE_HPF_CORNER_MAX 8 +#define DA7219_ADC_VOICE_EN_SHIFT 3 +#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_AUDIO_HPF_CORNER_MAX 4 +#define DA7219_ADC_HPF_EN_SHIFT 7 +#define DA7219_ADC_HPF_EN_MASK (0x1 << 7) +#define DA7219_HPF_MODE_SHIFT 0 +#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7)) +#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7)) +#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MAX 3 + +/* DA7219_MIC_1_GAIN = 0x39 */ +#define DA7219_MIC_1_AMP_GAIN_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0) + +/* DA7219_SIDETONE_CTRL = 0x3A */ +#define DA7219_SIDETONE_MUTE_EN_SHIFT 6 +#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6) +#define DA7219_SIDETONE_EN_SHIFT 7 +#define DA7219_SIDETONE_EN_MASK (0x1 << 7) + +/* DA7219_SIDETONE_GAIN = 0x3B */ +#define DA7219_SIDETONE_GAIN_SHIFT 0 +#define DA7219_SIDETONE_GAIN_MASK (0xF << 0) +#define DA7219_SIDETONE_GAIN_MAX 0xE + +/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */ +#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0 +#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1 +#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2 +#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1) + +/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */ +#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0) + +/* DA7219_DAC_FILTERS5 = 0x40 */ +#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4 +#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4) +#define DA7219_DAC_SOFTMUTE_RATE_MAX 7 +#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7 +#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS2 = 0x41 */ +#define DA7219_DAC_EQ_BAND1_SHIFT 0 +#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND2_SHIFT 4 +#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4) +#define DA7219_DAC_EQ_BAND_MAX 0xF + +/* DA7219_DAC_FILTERS3 = 0x42 */ +#define DA7219_DAC_EQ_BAND3_SHIFT 0 +#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND4_SHIFT 4 +#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4) + +/* DA7219_DAC_FILTERS4 = 0x43 */ +#define DA7219_DAC_EQ_BAND5_SHIFT 0 +#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0) +#define DA7219_DAC_EQ_EN_SHIFT 7 +#define DA7219_DAC_EQ_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS1 = 0x44 */ +#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_DAC_VOICE_EN_SHIFT 3 +#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_DAC_HPF_EN_SHIFT 7 +#define DA7219_DAC_HPF_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_GAIN = 0x45 */ +#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F +#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0) + +/* DA7219_DAC_R_GAIN = 0x46 */ +#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7219_CP_CTRL = 0x47 */ +#define DA7219_CP_MCHANGE_SHIFT 4 +#define DA7219_CP_MCHANGE_MASK (0x3 << 4) +#define DA7219_CP_MCHANGE_REL_MASK 0x3 +#define DA7219_CP_MCHANGE_MAX 3 +#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1 +#define DA7219_CP_MCHANGE_DAC_VOL 0x2 +#define DA7219_CP_MCHANGE_SIG_MAG 0x3 +#define DA7219_CP_EN_SHIFT 7 +#define DA7219_CP_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_GAIN = 0x48 */ +#define DA7219_HP_L_AMP_GAIN_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0) +#define DA7219_HP_AMP_GAIN_MAX 0x3F +#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0) + +/* DA7219_HP_R_GAIN = 0x49 */ +#define DA7219_HP_R_AMP_GAIN_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0) + +/* DA7219_MIXOUT_L_SELECT = 0x4B */ +#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXOUT_R_SELECT = 0x4C */ +#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_SYSTEM_MODES_INPUT = 0x50 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_ADC_MODE_SHIFT 1 +#define DA7219_ADC_MODE_MASK (0x7F << 1) + +/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_DAC_MODE_SHIFT 1 +#define DA7219_DAC_MODE_MASK (0x7F << 1) + +/* DA7219_MICBIAS_CTRL = 0x62 */ +#define DA7219_MICBIAS1_LEVEL_SHIFT 0 +#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0) +#define DA7219_MICBIAS1_EN_SHIFT 3 +#define DA7219_MICBIAS1_EN_MASK (0x1 << 3) + +/* DA7219_MIC_1_CTRL = 0x63 */ +#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIC_1_AMP_EN_SHIFT 7 +#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXIN_L_CTRL = 0x65 */ +#define DA7219_MIXIN_L_MIX_EN_SHIFT 3 +#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3) +#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIXIN_L_AMP_EN_SHIFT 7 +#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_ADC_L_CTRL = 0x67 */ +#define DA7219_ADC_L_BIAS_SHIFT 0 +#define DA7219_ADC_L_BIAS_MASK (0x3 << 0) +#define DA7219_ADC_L_RAMP_EN_SHIFT 5 +#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_ADC_L_MUTE_EN_SHIFT 6 +#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_ADC_L_EN_SHIFT 7 +#define DA7219_ADC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_CTRL = 0x69 */ +#define DA7219_DAC_L_RAMP_EN_SHIFT 5 +#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_L_MUTE_EN_SHIFT 6 +#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_L_EN_SHIFT 7 +#define DA7219_DAC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_R_CTRL = 0x6A */ +#define DA7219_DAC_R_RAMP_EN_SHIFT 5 +#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_R_MUTE_EN_SHIFT 6 +#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_R_EN_SHIFT 7 +#define DA7219_DAC_R_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_CTRL = 0x6B */ +#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_L_AMP_OE_SHIFT 3 +#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_L_AMP_EN_SHIFT 7 +#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_HP_R_CTRL = 0x6C */ +#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_R_AMP_OE_SHIFT 3 +#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_R_AMP_EN_SHIFT 7 +#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_L_CTRL = 0x6E */ +#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_R_CTRL = 0x6F */ +#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_CHIP_ID1 = 0x81 */ +#define DA7219_CHIP_ID1_SHIFT 0 +#define DA7219_CHIP_ID1_MASK (0xFF << 0) + +/* DA7219_CHIP_ID2 = 0x82 */ +#define DA7219_CHIP_ID2_SHIFT 0 +#define DA7219_CHIP_ID2_MASK (0xFF << 0) + +/* DA7219_CHIP_REVISION = 0x83 */ +#define DA7219_CHIP_MINOR_SHIFT 0 +#define DA7219_CHIP_MINOR_MASK (0xF << 0) +#define DA7219_CHIP_MAJOR_SHIFT 4 +#define DA7219_CHIP_MAJOR_MASK (0xF << 4) + +/* DA7219_LDO_CTRL = 0x90 */ +#define DA7219_LDO_LEVEL_SELECT_SHIFT 4 +#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4) +#define DA7219_LDO_EN_SHIFT 7 +#define DA7219_LDO_EN_MASK (0x1 << 7) + +/* DA7219_IO_CTRL = 0x91 */ +#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0 +#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0) +#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0 +#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1 + +/* DA7219_GAIN_RAMP_CTRL = 0x92 */ +#define DA7219_GAIN_RAMP_RATE_SHIFT 0 +#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0) +#define DA7219_GAIN_RAMP_RATE_MAX 4 + +/* DA7219_PC_COUNT = 0x94 */ +#define DA7219_PC_FREERUN_SHIFT 0 +#define DA7219_PC_FREERUN_MASK (0x1 << 0) +#define DA7219_PC_RESYNC_AUTO_SHIFT 1 +#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1) + +/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */ +#define DA7219_CP_THRESH_VDD2_SHIFT 0 +#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0) +#define DA7219_CP_THRESH_VDD2_MAX 0x3F + +/* DA7219_DIG_CTRL = 0x99 */ +#define DA7219_DAC_L_INV_SHIFT 3 +#define DA7219_DAC_L_INV_MASK (0x1 << 3) +#define DA7219_DAC_R_INV_SHIFT 7 +#define DA7219_DAC_R_INV_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL2 = 0x9A */ +#define DA7219_ALC_ATTACK_SHIFT 0 +#define DA7219_ALC_ATTACK_MASK (0xF << 0) +#define DA7219_ALC_ATTACK_MAX 13 +#define DA7219_ALC_RELEASE_SHIFT 4 +#define DA7219_ALC_RELEASE_MASK (0xF << 4) +#define DA7219_ALC_RELEASE_MAX 11 + +/* DA7219_ALC_CTRL3 = 0x9B */ +#define DA7219_ALC_HOLD_SHIFT 0 +#define DA7219_ALC_HOLD_MASK (0xF << 0) +#define DA7219_ALC_HOLD_MAX 16 +#define DA7219_ALC_INTEG_ATTACK_SHIFT 4 +#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4) +#define DA7219_ALC_INTEG_RELEASE_SHIFT 6 +#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6) +#define DA7219_ALC_INTEG_MAX 4 + +/* DA7219_ALC_NOISE = 0x9C */ +#define DA7219_ALC_NOISE_SHIFT 0 +#define DA7219_ALC_NOISE_MASK (0x3F << 0) +#define DA7219_ALC_THRESHOLD_MAX 0x3F + +/* DA7219_ALC_TARGET_MIN = 0x9D */ +#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0) + +/* DA7219_ALC_TARGET_MAX = 0x9E */ +#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0) + +/* DA7219_ALC_GAIN_LIMITS = 0x9F */ +#define DA7219_ALC_ATTEN_MAX_SHIFT 0 +#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0) +#define DA7219_ALC_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4) +#define DA7219_ALC_ATTEN_GAIN_MAX 0xF + +/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */ +#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0) +#define DA7219_ALC_ANA_GAIN_MIN 0x1 +#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4) +#define DA7219_ALC_ANA_GAIN_MAX 0x7 + +/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */ +#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0 +#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0) +#define DA7219_ALC_ANTICLIP_STEP_MAX 4 +#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7 +#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7) + +/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */ +#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0 +#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0) + +/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */ +#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0) + +/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */ +#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0) + +/* DA7219_DAC_NG_SETUP_TIME = 0xAF */ +#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0) +#define DA7219_DAC_NG_SETUP_TIME_MAX 4 +#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2) +#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3) +#define DA7219_DAC_NG_RAMP_RATE_MAX 2 + +/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */ +#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0) +#define DA7219_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7219_DAC_NG_ON_THRESH = 0xB1 */ +#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0) + +/* DA7219_DAC_NG_CTRL = 0xB2 */ +#define DA7219_DAC_NG_EN_SHIFT 7 +#define DA7219_DAC_NG_EN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG1 = 0xB4 */ +#define DA7219_DTMF_REG_SHIFT 0 +#define DA7219_DTMF_REG_MASK (0xF << 0) +#define DA7219_DTMF_REG_MAX 16 +#define DA7219_DTMF_EN_SHIFT 4 +#define DA7219_DTMF_EN_MASK (0x1 << 4) +#define DA7219_START_STOPN_SHIFT 7 +#define DA7219_START_STOPN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG2 = 0xB5 */ +#define DA7219_SWG_SEL_SHIFT 0 +#define DA7219_SWG_SEL_MASK (0x3 << 0) +#define DA7219_SWG_SEL_MAX 4 +#define DA7219_SWG_SEL_SRAMP (0x3 << 0) +#define DA7219_TONE_GEN_GAIN_SHIFT 4 +#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4) +#define DA7219_TONE_GEN_GAIN_MAX 0xF +#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4) +#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4) + +/* DA7219_TONE_GEN_CYCLES = 0xB6 */ +#define DA7219_BEEP_CYCLES_SHIFT 0 +#define DA7219_BEEP_CYCLES_MASK (0x7 << 0) + +/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */ +#define DA7219_FREQ1_L_SHIFT 0 +#define DA7219_FREQ1_L_MASK (0xFF << 0) +#define DA7219_FREQ_MAX 0xFFFF + +/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */ +#define DA7219_FREQ1_U_SHIFT 0 +#define DA7219_FREQ1_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */ +#define DA7219_FREQ2_L_SHIFT 0 +#define DA7219_FREQ2_L_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_U = 0xBA */ +#define DA7219_FREQ2_U_SHIFT 0 +#define DA7219_FREQ2_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_ON_PER = 0xBB */ +#define DA7219_BEEP_ON_PER_SHIFT 0 +#define DA7219_BEEP_ON_PER_MASK (0x3F << 0) +#define DA7219_BEEP_ON_OFF_MAX 0x3F + +/* DA7219_TONE_GEN_OFF_PER = 0xBC */ +#define DA7219_BEEP_OFF_PER_SHIFT 0 +#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0) + +/* DA7219_SYSTEM_STATUS = 0xE0 */ +#define DA7219_SC1_BUSY_SHIFT 0 +#define DA7219_SC1_BUSY_MASK (0x1 << 0) +#define DA7219_SC2_BUSY_SHIFT 1 +#define DA7219_SC2_BUSY_MASK (0x1 << 1) + +/* DA7219_SYSTEM_ACTIVE = 0xFD */ +#define DA7219_SYSTEM_ACTIVE_SHIFT 0 +#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0) + + +/* + * General defines & data + */ + +/* Register inversion */ +#define DA7219_NO_INVERT 0 +#define DA7219_INVERT 1 + +/* Byte related defines */ +#define DA7219_BYTE_SHIFT 8 +#define DA7219_BYTE_MASK 0xFF + +/* PLL Output Frequencies */ +#define DA7219_PLL_FREQ_OUT_90316 90316800 +#define DA7219_PLL_FREQ_OUT_98304 98304000 + +/* PLL Frequency Dividers */ +#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1 +#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2 +#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4 +#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8 +#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16 + +/* SRM */ +#define DA7219_SRM_CHECK_RETRIES 8 + +enum da7219_clk_src { + DA7219_CLKSRC_MCLK = 0, + DA7219_CLKSRC_MCLK_SQR, +}; + +enum da7219_sys_clk { + DA7219_SYSCLK_MCLK = 0, + DA7219_SYSCLK_PLL, + DA7219_SYSCLK_PLL_SRM, + DA7219_SYSCLK_PLL_32KHZ +}; + +/* Regulators */ +enum da7219_supplies { + DA7219_SUPPLY_VDD = 0, + DA7219_SUPPLY_VDDMIC, + DA7219_SUPPLY_VDDIO, + DA7219_NUM_SUPPLIES, +}; + +struct da7219_aad_priv; + +/* Private data */ +struct da7219_priv { + struct da7219_aad_priv *aad; + struct da7219_pdata *pdata; + + struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES]; + struct regmap *regmap; + struct mutex lock; + + struct clk *mclk; + unsigned int mclk_rate; + int clk_src; + + bool master; + bool alc_en; +}; + +#endif /* __DA7219_H */ -- cgit v1.1 From fbe039bb0815e6113f82021aa8c0e36a1941f511 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 28 Sep 2015 13:32:37 -0700 Subject: ASoC: rt5645: Allow 4 channel recording on AIF1 The codec supports 4 channel recording with TDM on AIF1. This patch modifies the DAI capability to allow it. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 1f7045b..66b7332 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3113,7 +3113,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = { .capture = { .stream_name = "AIF1 Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = RT5645_STEREO_RATES, .formats = RT5645_FORMATS, }, -- cgit v1.1 From d05ea7da0e8f6df3c62cfee75538f347cb3d89ef Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Fri, 2 Oct 2015 11:09:54 -0700 Subject: ALSA: hda: Add dock support for ThinkPad T550 Much like all the other Lenovo laptops, add a quirk to make sound work with docking. Reported-and-tested-by: lacknerflo@gmail.com Signed-off-by: Laura Abbott Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index afec6dc..16b8dcb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5306,6 +5306,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), -- cgit v1.1 From e8ff581f7ac2bc3b8886094b7ca635dcc4d1b0e9 Mon Sep 17 00:00:00 2001 From: John Flatness Date: Fri, 2 Oct 2015 17:07:49 -0400 Subject: ALSA: hda - Apply SPDIF pin ctl to MacBookPro 12,1 The MacBookPro 12,1 has the same setup as the 11 for controlling the status of the optical audio light. Simply apply the existing workaround to the subsystem ID for the 12,1. [sorted the fixup entry by tiwai] Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=105401 Signed-off-by: John Flatness Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cirrus.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 584a034..85813de 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -633,6 +633,7 @@ static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11), SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), + SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11), {} /* terminator */ }; -- cgit v1.1 From c7e1008048a97148d3aecae742f66fb2f944644c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 4 Oct 2015 22:44:12 +0200 Subject: ALSA: hda - Disable power_save_node for IDT 92HD73xx chips The recent widget power saving introduced some unavoidable click noises on old IDT 92HD73xx chips while it still seems working on the compatible new chips. In the bugzilla, we tried lots of tests and workarounds, but they didn't help much. So, let's disable the feature for these specific chips as the least (but safest) fix. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=104981 Cc: # v4.1+ Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 9d947ae..def5cc8 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4520,7 +4520,11 @@ static int patch_stac92hd73xx(struct hda_codec *codec) return err; spec = codec->spec; - codec->power_save_node = 1; + /* enable power_save_node only for new 92HD89xx chips, as it causes + * click noises on old 92HD73xx chips. + */ + if ((codec->core.vendor_id & 0xfffffff0) != 0x111d7670) + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.mixer_nid = 0x1d; spec->have_spdif_mux = 1; -- cgit v1.1 From a2c026cfec3fb84375785dd2d6ec80bd60c5120e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 5 Oct 2015 19:34:16 +0800 Subject: ASoC: rt5645: Prevent the weird sound of the headphone while rebooting The patch adds the codec reset setting in the shutdown function to prevent the weird sound of the headphone happened by rebooting. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 66b7332..cd1a4ec 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3514,6 +3514,8 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c) RT5645_CBJ_MN_JD); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN, 0); + msleep(20); + regmap_write(rt5645->regmap, RT5645_RESET, 0); } static struct i2c_driver rt5645_i2c_driver = { -- cgit v1.1 From 225db5762dc1a35b26850477ffa06e5cd0097243 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 5 Oct 2015 16:55:09 +0200 Subject: ALSA: synth: Fix conflicting OSS device registration on AWE32 When OSS emulation is loaded on ISA SB AWE32 chip, we get now kernel warnings like: WARNING: CPU: 0 PID: 2791 at fs/sysfs/dir.c:31 sysfs_warn_dup+0x51/0x80() sysfs: cannot create duplicate filename '/devices/isa/sbawe.0/sound/card0/seq-oss-0-0' It's because both emux synth and opl3 drivers try to register their OSS device object with the same static index number 0. This hasn't been a big problem until the recent rewrite of device management code (that exposes sysfs at the same time), but it's been an obvious bug. This patch works around it just by using a different index number of emux synth object. There can be a more elegant way to fix, but it's enough for now, as this code won't be touched so often, in anyway. Reported-and-tested-by: Michael Shell Cc: Signed-off-by: Takashi Iwai --- sound/synth/emux/emux_oss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index 82e350e..ac75816 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -69,7 +69,8 @@ snd_emux_init_seq_oss(struct snd_emux *emu) struct snd_seq_oss_reg *arg; struct snd_seq_device *dev; - if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS, + /* using device#1 here for avoiding conflicts with OPL3 */ + if (snd_seq_device_new(emu->card, 1, SNDRV_SEQ_DEV_ID_OSS, sizeof(struct snd_seq_oss_reg), &dev) < 0) return; -- cgit v1.1 From 4e929134eb8271abc9c52c371e056debfea6898b Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Mon, 5 Oct 2015 15:10:04 +0100 Subject: ASoC: da7219: Improve error handling for regulator supplies Currently if bulk_enable() of supplies fails, the code still goes on to try and put the device into active state, and set expected IO voltage of the device. This doesn't really make sense so code now returns on bulk_enable() failure, with an error message. Also, to help with debug, failure to get supplies also provides an error message. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 76f8fc2..c86a833 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1554,8 +1554,10 @@ static int da7219_handle_supplies(struct snd_soc_codec *codec) ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES, da7219->supplies); - if (ret) + if (ret) { + dev_err(codec->dev, "Failed to get supplies"); return ret; + } /* Determine VDDIO voltage provided */ vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer; @@ -1567,6 +1569,10 @@ static int da7219_handle_supplies(struct snd_soc_codec *codec) /* Enable main supplies */ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); + if (ret) { + dev_err(codec->dev, "Failed to enable supplies"); + return ret; + } /* Ensure device in active mode */ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK); @@ -1574,7 +1580,7 @@ static int da7219_handle_supplies(struct snd_soc_codec *codec) /* Update IO voltage level range */ snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl); - return ret; + return 0; } static void da7219_handle_pdata(struct snd_soc_codec *codec) -- cgit v1.1 From 1d957d862ac782eaf5803d4d4cf167708e4dc147 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Fri, 25 Sep 2015 17:48:22 -0400 Subject: ASoC: dwc: support dw i2s in slave mode dw i2s controller can work in slave mode, codec being master. dw i2s is made to support master/slave operation, by reading dwc register. Signed-off-by: Maruthi Bayyavarapu Signed-off-by: Alex Deucher Signed-off-by: Mark Brown --- sound/soc/dwc/designware_i2s.c | 92 +++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 37 deletions(-) (limited to 'sound') diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index a3e97b4..3a52f82 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -273,23 +273,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, config->sample_rate = params_rate(params); - if (dev->i2s_clk_cfg) { - ret = dev->i2s_clk_cfg(config); - if (ret < 0) { - dev_err(dev->dev, "runtime audio clk config fail\n"); - return ret; - } - } else { - u32 bitclk = config->sample_rate * config->data_width * 2; - - ret = clk_set_rate(dev->clk, bitclk); - if (ret) { - dev_err(dev->dev, "Can't set I2S clock rate: %d\n", - ret); - return ret; + if (dev->capability & DW_I2S_MASTER) { + if (dev->i2s_clk_cfg) { + ret = dev->i2s_clk_cfg(config); + if (ret < 0) { + dev_err(dev->dev, "runtime audio clk config fail\n"); + return ret; + } + } else { + u32 bitclk = config->sample_rate * + config->data_width * 2; + + ret = clk_set_rate(dev->clk, bitclk); + if (ret) { + dev_err(dev->dev, "Can't set I2S clock rate: %d\n", + ret); + return ret; + } } } - return 0; } @@ -357,7 +359,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - clk_disable(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable(dev->clk); return 0; } @@ -365,7 +368,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - clk_enable(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_enable(dev->clk); return 0; } @@ -443,6 +447,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, dw_i2s_dai->capture.rates = rates; } + if (COMP1_MODE_EN(comp1)) { + dev_dbg(dev->dev, "designware: i2s master mode supported\n"); + dev->capability |= DW_I2S_MASTER; + } else { + dev_dbg(dev->dev, "designware: i2s slave mode supported\n"); + dev->capability |= DW_I2S_SLAVE; + } + return 0; } @@ -529,6 +541,7 @@ static int dw_i2s_probe(struct platform_device *pdev) struct resource *res; int ret; struct snd_soc_dai_driver *dw_i2s_dai; + const char *clk_id; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { @@ -550,32 +563,35 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; + if (pdata) { + dev->capability = pdata->cap; + clk_id = NULL; ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); - if (ret < 0) - return ret; + } else { + clk_id = "i2sclk"; + ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + } + if (ret < 0) + return ret; - dev->capability = pdata->cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - if (!dev->i2s_clk_cfg) { - dev_err(&pdev->dev, "no clock configure method\n"); - return -ENODEV; + if (dev->capability & DW_I2S_MASTER) { + if (pdata) { + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; + if (!dev->i2s_clk_cfg) { + dev_err(&pdev->dev, "no clock configure method\n"); + return -ENODEV; + } } + dev->clk = devm_clk_get(&pdev->dev, clk_id); - dev->clk = devm_clk_get(&pdev->dev, NULL); - } else { - ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + ret = clk_prepare_enable(dev->clk); if (ret < 0) return ret; - - dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); } - if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); - - ret = clk_prepare_enable(dev->clk); - if (ret < 0) - return ret; dev_set_drvdata(&pdev->dev, dev); ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, @@ -597,7 +613,8 @@ static int dw_i2s_probe(struct platform_device *pdev) return 0; err_clk_disable: - clk_disable_unprepare(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable_unprepare(dev->clk); return ret; } @@ -605,7 +622,8 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - clk_disable_unprepare(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable_unprepare(dev->clk); return 0; } -- cgit v1.1 From 43ac946922b337507c4131c45bf339ddcd7e7402 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 17:18:48 +0800 Subject: ASoC: imx-spdif: add snd_soc_pm_ops for spdif machine driver Add snd_soc_pm_ops in machine driver to make the trigger suspend/resume be called in suspend/resume. Signed-off-by: Zidan Wang Signed-off-by: Mark Brown --- sound/soc/fsl/imx-spdif.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 33da26a..a407e83 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); static struct platform_driver imx_spdif_driver = { .driver = { .name = "imx-spdif", + .pm = &snd_soc_pm_ops, .of_match_table = imx_spdif_dt_ids, }, .probe = imx_spdif_audio_probe, -- cgit v1.1 From 1fde5e83a17acbcfcce27f68be46a6da4344efbd Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:10 +0800 Subject: ASoC: fsl_sai: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, SAI needs to save all the values of registers before the system suspend and restore them after the system resume. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a18fd92..5c737f1 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -637,6 +637,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_RCSR: case FSL_SAI_TFR: case FSL_SAI_RFR: case FSL_SAI_TDR: @@ -681,6 +683,7 @@ static const struct regmap_config fsl_sai_regmap_config = { .readable_reg = fsl_sai_readable_reg, .volatile_reg = fsl_sai_volatile_reg, .writeable_reg = fsl_sai_writeable_reg, + .cache_type = REGCACHE_FLAT, }; static int fsl_sai_probe(struct platform_device *pdev) @@ -802,10 +805,40 @@ static const struct of_device_id fsl_sai_ids[] = { { /* sentinel */ } }; +#if CONFIG_PM_SLEEP +static int fsl_sai_suspend(struct device *dev) +{ + struct fsl_sai *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, true); + regcache_mark_dirty(sai->regmap); + + return 0; +} + +static int fsl_sai_resume(struct device *dev) +{ + struct fsl_sai *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, false); + regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR); + regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR); + msleep(1); + regmap_write(sai->regmap, FSL_SAI_TCSR, 0); + regmap_write(sai->regmap, FSL_SAI_RCSR, 0); + return regcache_sync(sai->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_sai_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume) +}; + static struct platform_driver fsl_sai_driver = { .probe = fsl_sai_probe, .driver = { .name = "fsl-sai", + .pm = &fsl_sai_pm_ops, .of_match_table = fsl_sai_ids, }, }; -- cgit v1.1 From f9f4fa61aab9417e40898cf6706fffa94005dc44 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:11 +0800 Subject: ASoC: fsl_spdif: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, SPDIF needs to save all the values of registers before the system suspend and restore them after the system resume. The SRPC register should be volatile, LOCK bit is set by the hardware. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index ab729f2..3d59bb6 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -108,6 +108,8 @@ struct fsl_spdif_priv { struct clk *sysclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; + /* regcache for SRPC */ + u32 regcache_srpc; }; /* DPLL locked and lock loss interrupt handler */ @@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) struct regmap *regmap = spdif_priv->regmap; u32 val, cycle = 1000; + regcache_cache_bypass(regmap, true); + regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET); /* @@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) regmap_read(regmap, REG_SPDIF_SCR, &val); } while ((val & SCR_SOFT_RESET) && cycle--); + regcache_cache_bypass(regmap, false); + regcache_mark_dirty(regmap); + regcache_sync(regmap); + if (cycle) return 0; else @@ -997,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = { }; /* FSL SPDIF REGMAP */ +static const struct reg_default fsl_spdif_reg_defaults[] = { + {0x0, 0x00000400}, + {0x4, 0x00000000}, + {0xc, 0x00000000}, + {0x34, 0x00000000}, + {0x38, 0x00000000}, + {0x50, 0x00020f00}, +}; static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) { @@ -1022,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) } } +static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_SPDIF_SRPC: + case REG_SPDIF_SIS: + case REG_SPDIF_SRL: + case REG_SPDIF_SRR: + case REG_SPDIF_SRCSH: + case REG_SPDIF_SRCSL: + case REG_SPDIF_SRU: + case REG_SPDIF_SRQ: + case REG_SPDIF_STL: + case REG_SPDIF_STR: + case REG_SPDIF_SRFM: + return true; + default: + return false; + } +} + static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -1047,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = { .val_bits = 32, .max_register = REG_SPDIF_STC, + .reg_defaults = fsl_spdif_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults), .readable_reg = fsl_spdif_readable_reg, + .volatile_reg = fsl_spdif_volatile_reg, .writeable_reg = fsl_spdif_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, @@ -1271,6 +1311,38 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_PM_SLEEP +static int fsl_spdif_suspend(struct device *dev) +{ + struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + + regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC, + &spdif_priv->regcache_srpc); + + regcache_cache_only(spdif_priv->regmap, true); + regcache_mark_dirty(spdif_priv->regmap); + + return 0; +} + +static int fsl_spdif_resume(struct device *dev) +{ + struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + + regcache_cache_only(spdif_priv->regmap, false); + + regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC, + SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, + spdif_priv->regcache_srpc); + + return regcache_sync(spdif_priv->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_spdif_pm = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume) +}; + static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", }, { .compatible = "fsl,vf610-spdif", }, @@ -1282,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = { .driver = { .name = "fsl-spdif-dai", .of_match_table = fsl_spdif_dt_ids, + .pm = &fsl_spdif_pm, }, .probe = fsl_spdif_probe, }; -- cgit v1.1 From 05cf237972fe65eb537ea4f10e5627ceeb8f89b6 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:12 +0800 Subject: ASoC: fsl_ssi: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, SSI needs to save all the values of registers before the system suspend and restore them after the system resume. The register SFCSR is volatile, but some bits in it need to be recovered after suspend/resume. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8ec6fb2..7c495d3 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val { struct fsl_ssi_reg_val rx; struct fsl_ssi_reg_val tx; }; + +static const struct reg_default fsl_ssi_reg_defaults[] = { + {0x10, 0x00000000}, + {0x18, 0x00003003}, + {0x1c, 0x00000200}, + {0x20, 0x00000200}, + {0x24, 0x00040000}, + {0x28, 0x00040000}, + {0x38, 0x00000000}, + {0x48, 0x00000000}, + {0x4c, 0x00000000}, + {0x54, 0x00000000}, + {0x58, 0x00000000}, +}; + +static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_SACCEN: + case CCSR_SSI_SACCDIS: + return false; + default: + return true; + } +} + +static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_STX0: + case CCSR_SSI_STX1: + case CCSR_SSI_SRX0: + case CCSR_SSI_SRX1: + case CCSR_SSI_SISR: + case CCSR_SSI_SFCSR: + case CCSR_SSI_SACADD: + case CCSR_SSI_SACDAT: + case CCSR_SSI_SATAG: + case CCSR_SSI_SACCST: + return true; + default: + return false; + } +} + +static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_SRX0: + case CCSR_SSI_SRX1: + case CCSR_SSI_SACCST: + return false; + default: + return true; + } +} + static const struct regmap_config fsl_ssi_regconfig = { .max_register = CCSR_SSI_SACCDIS, .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .val_format_endian = REGMAP_ENDIAN_NATIVE, + .reg_defaults = fsl_ssi_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults), + .readable_reg = fsl_ssi_readable_reg, + .volatile_reg = fsl_ssi_volatile_reg, + .writeable_reg = fsl_ssi_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; struct fsl_ssi_soc_data { @@ -176,6 +239,9 @@ struct fsl_ssi_private { unsigned int baudclk_streams; unsigned int bitclk_freq; + /*regcache for SFCSR*/ + u32 regcache_sfcsr; + /* DMA params */ struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -1513,10 +1579,46 @@ static int fsl_ssi_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int fsl_ssi_suspend(struct device *dev) +{ + struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); + struct regmap *regs = ssi_private->regs; + + regmap_read(regs, CCSR_SSI_SFCSR, + &ssi_private->regcache_sfcsr); + + regcache_cache_only(regs, true); + regcache_mark_dirty(regs); + + return 0; +} + +static int fsl_ssi_resume(struct device *dev) +{ + struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); + struct regmap *regs = ssi_private->regs; + + regcache_cache_only(regs, false); + + regmap_update_bits(regs, CCSR_SSI_SFCSR, + CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK | + CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK, + ssi_private->regcache_sfcsr); + + return regcache_sync(regs); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_ssi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume) +}; + static struct platform_driver fsl_ssi_driver = { .driver = { .name = "fsl-ssi-dai", .of_match_table = fsl_ssi_ids, + .pm = &fsl_ssi_pm, }, .probe = fsl_ssi_probe, .remove = fsl_ssi_remove, -- cgit v1.1 From c64c60763b4e3c72a3520c8d51be858cd67bacb5 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:13 +0800 Subject: ASoC: fsl_esai: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, ESAI needs to save all the values of registers before the system suspend and restore them after the system resume. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 837979e..aab675a 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = { .name = "fsl-esai", }; +static const struct reg_default fsl_esai_reg_defaults[] = { + {0x8, 0x00000000}, + {0x10, 0x00000000}, + {0x18, 0x00000000}, + {0x98, 0x00000000}, + {0xd0, 0x00000000}, + {0xd4, 0x00000000}, + {0xd8, 0x00000000}, + {0xdc, 0x00000000}, + {0xe0, 0x00000000}, + {0xe4, 0x0000ffff}, + {0xe8, 0x0000ffff}, + {0xec, 0x0000ffff}, + {0xf0, 0x0000ffff}, + {0xf8, 0x00000000}, + {0xfc, 0x00000000}, +}; + static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) } } +static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ESAI_ETDR: + case REG_ESAI_ERDR: + case REG_ESAI_ESR: + case REG_ESAI_TFSR: + case REG_ESAI_RFSR: + case REG_ESAI_TX0: + case REG_ESAI_TX1: + case REG_ESAI_TX2: + case REG_ESAI_TX3: + case REG_ESAI_TX4: + case REG_ESAI_TX5: + case REG_ESAI_RX0: + case REG_ESAI_RX1: + case REG_ESAI_RX2: + case REG_ESAI_RX3: + case REG_ESAI_SAISR: + return true; + default: + return false; + } +} + static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = { .val_bits = 32, .max_register = REG_ESAI_PCRC, + .reg_defaults = fsl_esai_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults), .readable_reg = fsl_esai_readable_reg, + .volatile_reg = fsl_esai_volatile_reg, .writeable_reg = fsl_esai_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; static int fsl_esai_probe(struct platform_device *pdev) @@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); +#if CONFIG_PM_SLEEP +static int fsl_esai_suspend(struct device *dev) +{ + struct fsl_esai *esai = dev_get_drvdata(dev); + + regcache_cache_only(esai->regmap, true); + regcache_mark_dirty(esai->regmap); + + return 0; +} + +static int fsl_esai_resume(struct device *dev) +{ + struct fsl_esai *esai = dev_get_drvdata(dev); + int ret; + + regcache_cache_only(esai->regmap, false); + + /* FIFO reset for safety */ + regmap_update_bits(esai->regmap, REG_ESAI_TFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + regmap_update_bits(esai->regmap, REG_ESAI_RFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + + ret = regcache_sync(esai->regmap); + if (ret) + return ret; + + /* FIFO reset done */ + regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); + regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_esai_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume) +}; + static struct platform_driver fsl_esai_driver = { .probe = fsl_esai_probe, .driver = { .name = "fsl-esai-dai", + .pm = &fsl_esai_pm_ops, .of_match_table = fsl_esai_dt_ids, }, }; -- cgit v1.1 From e5224f58e3efd74972f7dcf46264c3ba9aa807ba Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 29 Sep 2015 16:41:43 +0200 Subject: ASoC: ad193x: add support to ad1934 The AD1934 codec has no ADC feature. Hence it register mapping is slightly different from the register mapping of other members of the AD193x family. Some ASoC controls and widgets are related to the DAC feature so are not relevant in the case of an AD1934 codec. Signed-off-by: Cyrille Pitchen Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x-i2c.c | 8 +-- sound/soc/codecs/ad193x-spi.c | 16 +++++- sound/soc/codecs/ad193x.c | 122 ++++++++++++++++++++++++++++++++---------- sound/soc/codecs/ad193x.h | 9 +++- 4 files changed, 121 insertions(+), 34 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index df3a1a4..1713136 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -15,8 +15,8 @@ #include "ad193x.h" static const struct i2c_device_id ad193x_id[] = { - { "ad1936", 0 }, - { "ad1937", 0 }, + { "ad1936", AD193X }, + { "ad1937", AD193X }, { } }; MODULE_DEVICE_TABLE(i2c, ad193x_id); @@ -30,7 +30,9 @@ static int ad193x_i2c_probe(struct i2c_client *client, config.val_bits = 8; config.reg_bits = 8; - return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config)); + return ad193x_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + (enum ad193x_type)id->driver_data); } static int ad193x_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c index 390cef9..364e8211 100644 --- a/sound/soc/codecs/ad193x-spi.c +++ b/sound/soc/codecs/ad193x-spi.c @@ -16,6 +16,7 @@ static int ad193x_spi_probe(struct spi_device *spi) { + const struct spi_device_id *id = spi_get_device_id(spi); struct regmap_config config; config = ad193x_regmap_config; @@ -24,7 +25,8 @@ static int ad193x_spi_probe(struct spi_device *spi) config.read_flag_mask = 0x09; config.write_flag_mask = 0x08; - return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); + return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config), + (enum ad193x_type)id->driver_data); } static int ad193x_spi_remove(struct spi_device *spi) @@ -33,6 +35,17 @@ static int ad193x_spi_remove(struct spi_device *spi) return 0; } +static const struct spi_device_id ad193x_spi_id[] = { + { "ad193x", AD193X }, + { "ad1933", AD1933 }, + { "ad1934", AD1934 }, + { "ad1936", AD193X }, + { "ad1937", AD193X }, + { "ad1938", AD193X }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad193x_spi_id); + static struct spi_driver ad193x_spi_driver = { .driver = { .name = "ad193x", @@ -40,6 +53,7 @@ static struct spi_driver ad193x_spi_driver = { }, .probe = ad193x_spi_probe, .remove = ad193x_spi_remove, + .id_table = ad193x_spi_id, }; module_spi_driver(ad193x_spi_driver); diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 17c9535..76d7966 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -23,6 +23,7 @@ /* codec private data */ struct ad193x_priv { struct regmap *regmap; + enum ad193x_type type; int sysclk; }; @@ -47,12 +48,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = { SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL, AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv), - /* ADC switch control */ - SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, - AD193X_ADCR1_MUTE, 1, 1), - SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, - AD193X_ADCR2_MUTE, 1, 1), - /* DAC switch control */ SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE, AD193X_DACR1_MUTE, 1, 1), @@ -63,26 +58,37 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = { SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE, AD193X_DACR4_MUTE, 1, 1), + /* DAC de-emphasis */ + SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), +}; + +static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = { + /* ADC switch control */ + SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, + AD193X_ADCR1_MUTE, 1, 1), + SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, + AD193X_ADCR2_MUTE, 1, 1), + /* ADC high-pass filter */ SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0, AD193X_ADC_HIGHPASS_FILTER, 1, 0), - - /* DAC de-emphasis */ - SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), }; static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0), SND_SOC_DAPM_VMID("VMID"), SND_SOC_DAPM_OUTPUT("DAC1OUT"), SND_SOC_DAPM_OUTPUT("DAC2OUT"), SND_SOC_DAPM_OUTPUT("DAC3OUT"), SND_SOC_DAPM_OUTPUT("DAC4OUT"), +}; + +static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_INPUT("ADC1IN"), SND_SOC_DAPM_INPUT("ADC2IN"), }; @@ -91,18 +97,33 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "DAC", NULL, "SYSCLK" }, { "DAC Output", NULL, "DAC" }, { "DAC Output", NULL, "VMID" }, - { "ADC", NULL, "SYSCLK" }, - { "DAC", NULL, "ADC_PWR" }, - { "ADC", NULL, "ADC_PWR" }, { "DAC1OUT", NULL, "DAC Output" }, { "DAC2OUT", NULL, "DAC Output" }, { "DAC3OUT", NULL, "DAC Output" }, { "DAC4OUT", NULL, "DAC Output" }, + { "SYSCLK", NULL, "PLL_PWR" }, +}; + +static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { + { "ADC", NULL, "SYSCLK" }, + { "ADC", NULL, "ADC_PWR" }, { "ADC", NULL, "ADC1IN" }, { "ADC", NULL, "ADC2IN" }, - { "SYSCLK", NULL, "PLL_PWR" }, }; +static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x) +{ + switch (ad193x->type) { + case AD1933: + case AD1934: + return false; + default: + break; + } + + return true; +} + /* * DAI ops entries */ @@ -147,8 +168,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, - AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT); + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_CHAN_MASK, + channels << AD193X_ADC_CHAN_SHFT); return 0; } @@ -172,7 +195,9 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, adc_serfmt |= AD193X_ADC_SERFMT_AUX; break; default: - return -EINVAL; + if (ad193x_has_adc(ad193x)) + return -EINVAL; + break; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -217,10 +242,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, - AD193X_ADC_SERFMT_MASK, adc_serfmt); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, - AD193X_ADC_FMT_MASK, adc_fmt); + if (ad193x_has_adc(ad193x)) { + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_SERFMT_MASK, adc_serfmt); + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_FMT_MASK, adc_fmt); + } regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_FMT_MASK, dac_fmt); @@ -287,8 +314,9 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, AD193X_DAC_WORD_LEN_MASK, word_len << AD193X_DAC_WORD_LEN_SHFT); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, - AD193X_ADC_WORD_LEN_MASK, word_len); + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_WORD_LEN_MASK, word_len); return 0; } @@ -326,6 +354,8 @@ static struct snd_soc_dai_driver ad193x_dai = { static int ad193x_codec_probe(struct snd_soc_codec *codec) { struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int num, ret; /* default setting for ad193x */ @@ -335,14 +365,46 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec) regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A); /* dac in tdm mode */ regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40); - /* high-pass filter enable */ - regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); - /* sata delay=1, adc aux mode */ - regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43); + + /* adc only */ + if (ad193x_has_adc(ad193x)) { + /* high-pass filter enable */ + regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); + /* sata delay=1, adc aux mode */ + regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43); + } + /* pll input: mclki/xi */ regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04); + /* adc only */ + if (ad193x_has_adc(ad193x)) { + /* add adc controls */ + num = ARRAY_SIZE(ad193x_adc_snd_controls); + ret = snd_soc_add_codec_controls(codec, + ad193x_adc_snd_controls, + num); + if (ret) + return ret; + + /* add adc widgets */ + num = ARRAY_SIZE(ad193x_adc_widgets); + ret = snd_soc_dapm_new_controls(dapm, + ad193x_adc_widgets, + num); + if (ret) + return ret; + + /* add adc routes */ + num = ARRAY_SIZE(ad193x_adc_audio_paths); + ret = snd_soc_dapm_add_routes(dapm, + ad193x_adc_audio_paths, + num); + if (ret) + return ret; + } + return 0; } @@ -367,7 +429,8 @@ const struct regmap_config ad193x_regmap_config = { }; EXPORT_SYMBOL_GPL(ad193x_regmap_config); -int ad193x_probe(struct device *dev, struct regmap *regmap) +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type) { struct ad193x_priv *ad193x; @@ -379,6 +442,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap) return -ENOMEM; ad193x->regmap = regmap; + ad193x->type = type; dev_set_drvdata(dev, ad193x); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index ab9a998..8b1e65f 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h @@ -13,8 +13,15 @@ struct device; +enum ad193x_type { + AD193X, + AD1933, + AD1934, +}; + extern const struct regmap_config ad193x_regmap_config; -int ad193x_probe(struct device *dev, struct regmap *regmap); +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type); #define AD193X_PLL_CLK_CTRL0 0x00 #define AD193X_PLL_POWERDOWN 0x01 -- cgit v1.1 From 698d0b59f3d91cc44a76dd2994a002d263c3606b Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Mon, 5 Oct 2015 21:03:39 +0200 Subject: ASoC: rockchip: namespace rockchip i2s module name Change the rockchip i2s object name (and thus module name) from the rather generic snd-soc-i2s to the more specific snd-soc-rockchip-i2s Signed-off-by: Sjoerd Simons Signed-off-by: Mark Brown --- sound/soc/rockchip/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 1bc1dc3..e9ba558 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,7 +1,7 @@ # ROCKCHIP Platform Support -snd-soc-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-i2s-objs := rockchip_i2s.o -obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o -- cgit v1.1 From e2600460bc3aa14ca1df86318a327cbbabedf9a8 Mon Sep 17 00:00:00 2001 From: Andreas Dannenberg Date: Mon, 5 Oct 2015 15:00:14 -0500 Subject: ASoC: tas2552: fix dBscale-min declaration The minimum volume level for the TAS2552 (control register value 0x00) is -7dB however the driver declares it as -0.07dB. Running amixer before the patch reports: dBscale-min=-0.07dB,step=1.00dB,mute=0 Running amixer with the patch applied reports: dBscale-min=-7.00dB,step=1.00dB,mute=0 Signed-off-by: Andreas Dannenberg Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/tas2552.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index e3a0bca..cc1d398 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -549,7 +549,7 @@ static struct snd_soc_dai_driver tas2552_dai[] = { /* * DAC digital volumes. From -7 to 24 dB in 1 dB steps */ -static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); +static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0); static const char * const tas2552_din_source_select[] = { "Muted", -- cgit v1.1 From d46183efe3b57d06461c9bea35e1a0262391fe77 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 6 Oct 2015 17:26:36 +0800 Subject: ASoC: ad193x-spi: Fixup ad193x_spi_id table AD1939 is missed from the table, so add it. AD1936 and AD1937 are controlled by I2C interface, so remove them. Fixes: e5224f58e3ef ("ASoC: ad193x: add support to ad1934") Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x-spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c index 364e8211..b55055a 100644 --- a/sound/soc/codecs/ad193x-spi.c +++ b/sound/soc/codecs/ad193x-spi.c @@ -39,9 +39,8 @@ static const struct spi_device_id ad193x_spi_id[] = { { "ad193x", AD193X }, { "ad1933", AD1933 }, { "ad1934", AD1934 }, - { "ad1936", AD193X }, - { "ad1937", AD193X }, { "ad1938", AD193X }, + { "ad1939", AD193X }, { } }; MODULE_DEVICE_TABLE(spi, ad193x_spi_id); -- cgit v1.1 From dc6d84c69cf8296b1e8e2fd0b1e115b7787ef4e9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 5 Oct 2015 21:22:40 +0800 Subject: ASoC: rt286: Fix run time error while modifying const data Make a copy of memory for index_cache rather than directly use the rt286_index_def to avoid run time error. Fixes: c418a84a8c8f ("ASoC: Constify reg_default tables") Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt286.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index bd93658..2088dfa 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -38,7 +38,7 @@ #define RT288_VENDOR_ID 0x10ec0288 struct rt286_priv { - const struct reg_default *index_cache; + struct reg_default *index_cache; int index_cache_size; struct regmap *regmap; struct snd_soc_codec *codec; @@ -1161,7 +1161,11 @@ static int rt286_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt286->index_cache = rt286_index_def; + rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def, + sizeof(rt286_index_def), GFP_KERNEL); + if (!rt286->index_cache) + return -ENOMEM; + rt286->index_cache_size = INDEX_CACHE_SIZE; rt286->i2c = i2c; i2c_set_clientdata(i2c, rt286); -- cgit v1.1 From b5e5a4549c8b88a880fa3866fa3803ea9396ba03 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 5 Oct 2015 14:53:50 +0300 Subject: ASoC: Intel: use dw_dmac autoconfiguration Instead of hardconding a platform data for dw_dmac let's use it's own autoconfiguration feature. Thus, remove hardcoded values. Acked-by: Liam Girdwood Cc: Mark Brown Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-firmware.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index ebcca6d..1636a1e 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -26,7 +26,6 @@ #include /* supported DMA engine drivers */ -#include #include #include @@ -169,12 +168,6 @@ err: return ret; } -static struct dw_dma_platform_data dw_pdata = { - .is_private = 1, - .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, - .chan_priority = CHAN_PRIORITY_ASCENDING, -}; - static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, int irq) { @@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, return ERR_PTR(err); chip->dev = dev; - err = dw_dma_probe(chip, &dw_pdata); + + err = dw_dma_probe(chip, NULL); if (err) return ERR_PTR(err); -- cgit v1.1 From 34ca27f34f413b4a684fc7336911799da3ac84d5 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Fri, 2 Oct 2015 09:49:14 -0700 Subject: ASoC: nau8825: Add driver for headset chip Nuvoton 8825 Sponsored-by: Google Chromium project Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/nau8825.c | 1107 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/nau8825.h | 323 +++++++++++++ 4 files changed, 1436 insertions(+) create mode 100644 sound/soc/codecs/nau8825.c create mode 100644 sound/soc/codecs/nau8825.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0142396..cc60ab9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -79,6 +79,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C + select SND_SOC_NAU8825 if I2C select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 @@ -892,6 +893,9 @@ config SND_SOC_MC13783 config SND_SOC_ML26124 tristate +config SND_SOC_NAU8825 + tristate + config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7d7cc1b..d7b0f41 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -72,6 +72,7 @@ snd-soc-max98925-objs := max98925.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o +snd-soc-nau8825-objs := nau8825.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o @@ -263,6 +264,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o +obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c new file mode 100644 index 0000000..f31a500 --- /dev/null +++ b/sound/soc/codecs/nau8825.c @@ -0,0 +1,1107 @@ +/* + * Nuvoton NAU8825 audio codec driver + * + * Copyright 2015 Google Chromium project. + * Author: Anatol Pomozov + * Copyright 2015 Nuvoton Technology Corp. + * Co-author: Meng-Huang Kuo + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include "nau8825.h" + +static const struct reg_default nau8825_reg_defaults[] = { + { NAU8825_REG_ENA_CTRL, 0x00ff }, + { NAU8825_REG_CLK_DIVIDER, 0x0050 }, + { NAU8825_REG_FLL1, 0x0 }, + { NAU8825_REG_FLL2, 0x3126 }, + { NAU8825_REG_FLL3, 0x0008 }, + { NAU8825_REG_FLL4, 0x0010 }, + { NAU8825_REG_FLL5, 0x0 }, + { NAU8825_REG_FLL6, 0x6000 }, + { NAU8825_REG_FLL_VCO_RSV, 0xf13c }, + { NAU8825_REG_HSD_CTRL, 0x000c }, + { NAU8825_REG_JACK_DET_CTRL, 0x0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0 }, + { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff }, + { NAU8825_REG_SAR_CTRL, 0x0015 }, + { NAU8825_REG_KEYDET_CTRL, 0x0110 }, + { NAU8825_REG_VDET_THRESHOLD_1, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_2, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_3, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_4, 0x0 }, + { NAU8825_REG_GPIO34_CTRL, 0x0 }, + { NAU8825_REG_GPIO12_CTRL, 0x0 }, + { NAU8825_REG_TDM_CTRL, 0x0 }, + { NAU8825_REG_I2S_PCM_CTRL1, 0x000b }, + { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 }, + { NAU8825_REG_LEFT_TIME_SLOT, 0x0 }, + { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 }, + { NAU8825_REG_BIQ_CTRL, 0x0 }, + { NAU8825_REG_BIQ_COF1, 0x0 }, + { NAU8825_REG_BIQ_COF2, 0x0 }, + { NAU8825_REG_BIQ_COF3, 0x0 }, + { NAU8825_REG_BIQ_COF4, 0x0 }, + { NAU8825_REG_BIQ_COF5, 0x0 }, + { NAU8825_REG_BIQ_COF6, 0x0 }, + { NAU8825_REG_BIQ_COF7, 0x0 }, + { NAU8825_REG_BIQ_COF8, 0x0 }, + { NAU8825_REG_BIQ_COF9, 0x0 }, + { NAU8825_REG_BIQ_COF10, 0x0 }, + { NAU8825_REG_ADC_RATE, 0x0010 }, + { NAU8825_REG_DAC_CTRL1, 0x0001 }, + { NAU8825_REG_DAC_CTRL2, 0x0 }, + { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 }, + { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf }, + { NAU8825_REG_MUTE_CTRL, 0x0 }, + { NAU8825_REG_HSVOL_CTRL, 0x0 }, + { NAU8825_REG_DACL_CTRL, 0x02cf }, + { NAU8825_REG_DACR_CTRL, 0x00cf }, + { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff }, + { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 }, + { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_IMM_MODE_CTRL, 0x0 }, + { NAU8825_REG_CLASSG_CTRL, 0x0 }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 }, + { NAU8825_REG_MISC_CTRL, 0x0 }, + { NAU8825_REG_BIAS_ADJ, 0x0 }, + { NAU8825_REG_TRIM_SETTINGS, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_1, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0 }, + { NAU8825_REG_ANALOG_ADC_1, 0x0011 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0020 }, + { NAU8825_REG_RDAC, 0x0008 }, + { NAU8825_REG_MIC_BIAS, 0x0006 }, + { NAU8825_REG_BOOST, 0x0 }, + { NAU8825_REG_FEPGA, 0x0 }, + { NAU8825_REG_POWER_UP_CONTROL, 0x0 }, + { NAU8825_REG_CHARGE_PUMP, 0x0 }, +}; + +static bool nau8825_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_ENA_CTRL: + case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS: + return true; + default: + return false; + } + +} + +static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL: + case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK: + case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET: + case NAU8825_REG_IRQ_STATUS: + case NAU8825_REG_INT_CLR_KEY_STATUS: + case NAU8825_REG_IMM_RMS_L: + case NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_I2C_DEVICE_ID: + case NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_CHARGE_PUMP_INPUT_READ: + case NAU8825_REG_GENERAL_STATUS: + return true; + default: + return false; + } +} + +static int nau8825_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Prevent startup click by letting charge pump to ramp up */ + msleep(10); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const char * const nau8825_adc_decimation[] = { + "32", "64", "128", "256" +}; + +static const struct soc_enum nau8825_adc_decimation_enum = + SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT, + ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation); + +static const char * const nau8825_dac_oversampl[] = { + "64", "256", "128", "", "32" +}; + +static const struct soc_enum nau8825_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT, + ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl); + +static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400); +static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0); +static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0); +static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600); +static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400); + +static const struct snd_kcontrol_new nau8825_controls[] = { + SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 0, 0xff, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 12, 8, 0x0f, 0, sidetone_vol_tlv), + SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL, + 6, 0, 0x3f, 1, dac_vol_tlv), + SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL, + 8, 37, 0, fepga_gain_tlv), + SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL, + 0, 8, 0xff, 0, crosstalk_vol_tlv), + + SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum), + SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum), +}; + +/* DAC Mux 0x33[9] and 0x34[9] */ +static const char * const nau8825_dac_src[] = { + "DACL", "DACR", +}; + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacl_enum, NAU8825_REG_DACL_CTRL, + NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src); + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacr_enum, NAU8825_REG_DACR_CTRL, + NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src); + +static const struct snd_kcontrol_new nau8825_dacl_mux = + SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum); + +static const struct snd_kcontrol_new nau8825_dacr_mux = + SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum); + + +static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2, + 15, 1), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), + + SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, + NULL, 0), + + SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL, + 0), + + /* ADC for button press detection */ + SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_ADC_EN_SFT, 0), + + SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0), + SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0), + SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR_SFT, 0), + SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACL_SFT, 0), + SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux), + SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux), + + SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, + 0), + + SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0, + nau8825_pump_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA("Output Driver R Stage 1", + NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver L Stage 1", + NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver R Stage 2", + NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver L Stage 2", + NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1, + NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1, + NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { + {"Frontend PGA", NULL, "MIC"}, + {"ADC", NULL, "Frontend PGA"}, + {"ADC", NULL, "ADC Clock"}, + {"ADC", NULL, "ADC Power"}, + {"AIFTX", NULL, "ADC"}, + + {"DDACL", NULL, "Playback"}, + {"DDACR", NULL, "Playback"}, + {"DDACL", NULL, "DDAC Clock"}, + {"DDACR", NULL, "DDAC Clock"}, + {"DACL Mux", "DACL", "DDACL"}, + {"DACL Mux", "DACR", "DDACR"}, + {"DACR Mux", "DACL", "DDACL"}, + {"DACR Mux", "DACR", "DDACR"}, + {"HP amp L", NULL, "DACL Mux"}, + {"HP amp R", NULL, "DACR Mux"}, + {"HP amp L", NULL, "HP amp power"}, + {"HP amp R", NULL, "HP amp power"}, + {"ADACL", NULL, "HP amp L"}, + {"ADACR", NULL, "HP amp R"}, + {"ADACL", NULL, "ADACL Clock"}, + {"ADACR", NULL, "ADACR Clock"}, + {"Output Driver L Stage 1", NULL, "ADACL"}, + {"Output Driver R Stage 1", NULL, "ADACR"}, + {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, + {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, + {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, + {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, + {"Output DACL", NULL, "Output Driver L Stage 3"}, + {"Output DACR", NULL, "Output Driver R Stage 3"}, + {"HPOL", NULL, "Output DACL"}, + {"HPOR", NULL, "Output DACR"}, + {"HPOL", NULL, "Charge Pump"}, + {"HPOR", NULL, "Charge Pump"}, +}; + +static int nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0; + + switch (params_width(params)) { + case 16: + val_len |= NAU8825_I2S_DL_16; + break; + case 20: + val_len |= NAU8825_I2S_DL_20; + break; + case 24: + val_len |= NAU8825_I2S_DL_24; + break; + case 32: + val_len |= NAU8825_I2S_DL_32; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK, val_len); + + return 0; +} + +static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8825_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8825_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8825_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8825_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8825_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + ctrl1_val |= NAU8825_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK | + NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK, + ctrl1_val); + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, ctrl2_val); + + return 0; +} + +static const struct snd_soc_dai_ops nau8825_dai_ops = { + .hw_params = nau8825_hw_params, + .set_fmt = nau8825_set_dai_fmt, +}; + +#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000 +#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8825_dai = { + .name = "nau8825-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .ops = &nau8825_dai_ops, +}; + +/** + * nau8825_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int nau8825_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap = nau8825->regmap; + + nau8825->jack = jack; + + /* Ground HP Outputs[1:0], needed for headset auto detection + * Enable Automatic Mic/Gnd switching reading on insert interrupt[6] + */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); + + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect); + + +static bool nau8825_is_jack_inserted(struct regmap *regmap) +{ + int status; + + regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status); + return !(status & NAU8825_GPIO2JD1); +} + +static void nau8825_restart_jack_detection(struct regmap *regmap) +{ + /* this will restart the entire jack detection process including MIC/GND + * switching and create interrupts. We have to go from 0 to 1 and back + * to 0 to restart. + */ + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, 0); +} + +static void nau8825_eject_jack(struct nau8825 *nau8825) +{ + struct snd_soc_dapm_context *dapm = nau8825->dapm; + struct regmap *regmap = nau8825->regmap; + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); + /* ground HPL/HPR, MICGRND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf); + + snd_soc_dapm_sync(dapm); +} + +static int nau8825_button_decode(int value) +{ + int buttons = 0; + + /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */ + if (value & BIT(0)) + buttons |= SND_JACK_BTN_0; + if (value & BIT(1)) + buttons |= SND_JACK_BTN_1; + if (value & BIT(2)) + buttons |= SND_JACK_BTN_2; + if (value & BIT(3)) + buttons |= SND_JACK_BTN_3; + if (value & BIT(4)) + buttons |= SND_JACK_BTN_4; + if (value & BIT(5)) + buttons |= SND_JACK_BTN_5; + + return buttons; +} + +static int nau8825_jack_insert(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + struct snd_soc_dapm_context *dapm = nau8825->dapm; + int jack_status_reg, mic_detected; + int type = 0; + + regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg); + mic_detected = (jack_status_reg >> 10) & 3; + + switch (mic_detected) { + case 0: + /* no mic */ + type = SND_JACK_HEADPHONE; + break; + case 1: + dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 1 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKR2); + /* Attach SARADC to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKR2); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + case 2: + case 3: + dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 2 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKSLV); + /* Attach SARADC to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKSLV); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + } + + if (type & SND_JACK_HEADPHONE) { + /* Unground HPL/R */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0); + } + + return type; +} + +#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +static irqreturn_t nau8825_interrupt(int irq, void *data) +{ + struct nau8825 *nau8825 = (struct nau8825 *)data; + struct regmap *regmap = nau8825->regmap; + int active_irq, clear_irq = 0, event = 0, event_mask = 0; + + regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq); + + if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == + NAU8825_JACK_EJECTION_DETECTED) { + + nau8825_eject_jack(nau8825); + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK; + } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) { + int key_status; + + regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, + &key_status); + + /* upper 8 bits of the register are for short pressed keys, + * lower 8 bits - for long pressed buttons + */ + nau8825->button_pressed = nau8825_button_decode( + key_status >> 8); + + event |= nau8825->button_pressed; + event_mask |= NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ; + } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) { + event_mask = NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_RELEASE_IRQ; + } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) { + if (nau8825_is_jack_inserted(regmap)) { + event |= nau8825_jack_insert(nau8825); + } else { + dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n"); + nau8825_eject_jack(nau8825); + } + + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_HEADSET_COMPLETION_IRQ; + } + + if (!clear_irq) + clear_irq = active_irq; + /* clears the rightmost interruption */ + regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq); + + if (event_mask) + snd_soc_jack_report(nau8825->jack, event, event_mask); + + return IRQ_HANDLED; +} + +static void nau8825_setup_buttons(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_TRACKING_GAIN_MASK, + nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_COMPARE_TIME_MASK, + nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_SAMPLING_TIME_MASK, + nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT); + + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_LEVELS_NR_MASK, + (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_HYSTERESIS_MASK, + nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK, + nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT); + + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1, + (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2, + (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3, + (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4, + (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]); + + /* Enable short press and release interruptions */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN, + 0); +} + +static void nau8825_init_regs(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + /* Enable Bias/Vmid */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN); + + /* VMID Tieoff */ + regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID_SEL_MASK, + nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); + /* Disable Boost Driver, Automatic Short circuit protection enable */ + regmap_update_bits(regmap, NAU8825_REG_BOOST, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | + NAU8825_SHORT_SHUTDOWN_EN, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | + NAU8825_SHORT_SHUTDOWN_EN); + + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_OUTPUT_EN, + nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_EN, + nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_UP, + nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_POLARITY, + /* jkdet_polarity - 1 is for active-low */ + nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY); + + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_INSERT_DEBOUNCE_MASK, + nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_EJECT_DEBOUNCE_MASK, + nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT); + + /* Mask unneeded IRQs: 1 - disable, 0 - enable */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff); + + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage); + + if (nau8825->sar_threshold_num) + nau8825_setup_buttons(nau8825); + + /* Default oversampling/decimations settings are unusable + * (audible hiss). Set it to something better. + */ + regmap_update_bits(regmap, NAU8825_REG_ADC_RATE, + NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128); + regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, + NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); +} + +static const struct regmap_config nau8825_regmap_config = { + .val_bits = 16, + .reg_bits = 16, + + .max_register = NAU8825_REG_MAX, + .readable_reg = nau8825_readable_reg, + .writeable_reg = nau8825_writeable_reg, + .volatile_reg = nau8825_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8825_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults), +}; + +static int nau8825_codec_probe(struct snd_soc_codec *codec) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + nau8825->dapm = dapm; + + /* The interrupt clock is gated by x1[10:8], + * one of them needs to be enabled all the time for + * interrupts to happen. + */ + snd_soc_dapm_force_enable_pin(dapm, "DDACR"); + snd_soc_dapm_sync(dapm); + + /* Unmask interruptions. Handler uses dapm object so we can enable + * interruptions only after dapm is fully initialized. + */ + regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0); + nau8825_restart_jack_detection(nau8825->regmap); + + return 0; +} + +static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, + unsigned int freq) +{ + struct regmap *regmap = nau8825->regmap; + int ret; + + switch (clk_id) { + case NAU8825_CLK_MCLK: + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); + + /* We selected MCLK source but the clock itself managed externally */ + if (!nau8825->mclk) + break; + + if (!nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + if (nau8825->mclk_freq != freq) { + nau8825->mclk_freq = freq; + + freq = clk_round_rate(nau8825->mclk, freq); + ret = clk_set_rate(nau8825->mclk, freq); + if (ret) { + dev_err(nau8825->dev, "Unable to set mclk rate\n"); + return ret; + } + } + + break; + case NAU8825_CLK_INTERNAL: + regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, + NAU8825_DCO_EN); + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + default: + dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq, + clk_id); + return 0; +} + +static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + + return nau8825_configure_sysclk(nau8825, clk_id, freq); +} + +static int nau8825_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + if (nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + ret = regcache_sync(nau8825->regmap); + if (ret) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + + break; + + case SND_SOC_BIAS_OFF: + if (nau8825->mclk_freq) + clk_disable_unprepare(nau8825->mclk); + + regcache_mark_dirty(nau8825->regmap); + break; + } + return 0; +} + +static struct snd_soc_codec_driver nau8825_codec_driver = { + .probe = nau8825_codec_probe, + .set_sysclk = nau8825_set_sysclk, + .set_bias_level = nau8825_set_bias_level, + .suspend_bias_off = true, + + .controls = nau8825_controls, + .num_controls = ARRAY_SIZE(nau8825_controls), + .dapm_widgets = nau8825_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets), + .dapm_routes = nau8825_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes), +}; + +static void nau8825_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8825_REG_RESET, 0x00); + regmap_write(regmap, NAU8825_REG_RESET, 0x00); +} + +static int nau8825_read_device_properties(struct device *dev, + struct nau8825 *nau8825) { + + nau8825->jkdet_enable = device_property_read_bool(dev, + "nuvoton,jkdet-enable"); + nau8825->jkdet_pull_enable = device_property_read_bool(dev, + "nuvoton,jkdet-pull-enable"); + nau8825->jkdet_pull_up = device_property_read_bool(dev, + "nuvoton,jkdet-pull-up"); + device_property_read_u32(dev, "nuvoton,jkdet-polarity", + &nau8825->jkdet_polarity); + device_property_read_u32(dev, "nuvoton,micbias-voltage", + &nau8825->micbias_voltage); + device_property_read_u32(dev, "nuvoton,vref-impedance", + &nau8825->vref_impedance); + device_property_read_u32(dev, "nuvoton,sar-threshold-num", + &nau8825->sar_threshold_num); + device_property_read_u32_array(dev, "nuvoton,sar-threshold", + nau8825->sar_threshold, nau8825->sar_threshold_num); + device_property_read_u32(dev, "nuvoton,sar-hysteresis", + &nau8825->sar_hysteresis); + device_property_read_u32(dev, "nuvoton,sar-voltage", + &nau8825->sar_voltage); + device_property_read_u32(dev, "nuvoton,sar-compare-time", + &nau8825->sar_compare_time); + device_property_read_u32(dev, "nuvoton,sar-sampling-time", + &nau8825->sar_sampling_time); + device_property_read_u32(dev, "nuvoton,short-key-debounce", + &nau8825->key_debounce); + device_property_read_u32(dev, "nuvoton,jack-insert-debounce", + &nau8825->jack_insert_debounce); + device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + &nau8825->jack_eject_debounce); + + nau8825->mclk = devm_clk_get(dev, "mclk"); + if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { + /* The MCLK is managed externally or not used at all */ + nau8825->mclk = NULL; + dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally"); + } else if (IS_ERR(nau8825->mclk)) { + return -EINVAL; + } + + return 0; +} + +static int nau8825_setup_irq(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + int ret; + + /* IRQ Output Enable */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN); + + /* Enable internal VCO needed for interruptions */ + nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0); + + /* Enable DDACR needed for interrupts + * It is the same as force_enable_pin("DDACR") we do later + */ + regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR); + + /* Chip needs one FSCLK cycle in order to generate interrupts, + * as we cannot guarantee one will be provided by the system. Turning + * master mode on then off enables us to generate that FSCLK cycle + * with a minimum of contention on the clock bus. + */ + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); + + ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL, + nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "nau8825", nau8825); + + if (ret) { + dev_err(nau8825->dev, "Cannot request irq %d (%d)\n", + nau8825->irq, ret); + return ret; + } + + return 0; +} + +static int nau8825_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev); + int ret, value; + + if (!nau8825) { + nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL); + if (!nau8825) + return -ENOMEM; + ret = nau8825_read_device_properties(dev, nau8825); + if (ret) + return ret; + } + + i2c_set_clientdata(i2c, nau8825); + + nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config); + if (IS_ERR(nau8825->regmap)) + return PTR_ERR(nau8825->regmap); + nau8825->dev = dev; + nau8825->irq = i2c->irq; + + nau8825_reset_chip(nau8825->regmap); + ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value); + if (ret < 0) { + dev_err(dev, "Failed to read device id from the NAU8825: %d\n", + ret); + return ret; + } + if ((value & NAU8825_SOFTWARE_ID_MASK) != + NAU8825_SOFTWARE_ID_NAU8825) { + dev_err(dev, "Not a NAU8825 chip\n"); + return -ENODEV; + } + + nau8825_init_regs(nau8825); + + if (i2c->irq) + nau8825_setup_irq(nau8825); + + return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver, + &nau8825_dai, 1); +} + +static int nau8825_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id nau8825_i2c_ids[] = { + { "nau8825", 0 }, + { } +}; + +#ifdef CONFIG_OF +static const struct of_device_id nau8825_of_ids[] = { + { .compatible = "nuvoton,nau8825", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8825_of_ids); +#endif + +static struct i2c_driver nau8825_driver = { + .driver = { + .name = "nau8825", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(nau8825_of_ids), + }, + .probe = nau8825_i2c_probe, + .remove = nau8825_i2c_remove, + .id_table = nau8825_i2c_ids, +}; +module_i2c_driver(nau8825_driver); + +MODULE_DESCRIPTION("ASoC nau8825 driver"); +MODULE_AUTHOR("Anatol Pomozov "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h new file mode 100644 index 0000000..8774923 --- /dev/null +++ b/sound/soc/codecs/nau8825.h @@ -0,0 +1,323 @@ +/* + * NAU8825 ALSA SoC audio driver + * + * Copyright 2015 Google Inc. + * Author: Anatol Pomozov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __NAU8825_H__ +#define __NAU8825_H__ + +#define NAU8825_REG_RESET 0x00 +#define NAU8825_REG_ENA_CTRL 0x01 +#define NAU8825_REG_CLK_DIVIDER 0x03 +#define NAU8825_REG_FLL1 0x04 +#define NAU8825_REG_FLL2 0x05 +#define NAU8825_REG_FLL3 0x06 +#define NAU8825_REG_FLL4 0x07 +#define NAU8825_REG_FLL5 0x08 +#define NAU8825_REG_FLL6 0x09 +#define NAU8825_REG_FLL_VCO_RSV 0x0a +#define NAU8825_REG_HSD_CTRL 0x0c +#define NAU8825_REG_JACK_DET_CTRL 0x0d +#define NAU8825_REG_INTERRUPT_MASK 0x0f +#define NAU8825_REG_IRQ_STATUS 0x10 +#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11 +#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12 +#define NAU8825_REG_SAR_CTRL 0x13 +#define NAU8825_REG_KEYDET_CTRL 0x14 +#define NAU8825_REG_VDET_THRESHOLD_1 0x15 +#define NAU8825_REG_VDET_THRESHOLD_2 0x16 +#define NAU8825_REG_VDET_THRESHOLD_3 0x17 +#define NAU8825_REG_VDET_THRESHOLD_4 0x18 +#define NAU8825_REG_GPIO34_CTRL 0x19 +#define NAU8825_REG_GPIO12_CTRL 0x1a +#define NAU8825_REG_TDM_CTRL 0x1b +#define NAU8825_REG_I2S_PCM_CTRL1 0x1c +#define NAU8825_REG_I2S_PCM_CTRL2 0x1d +#define NAU8825_REG_LEFT_TIME_SLOT 0x1e +#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f +#define NAU8825_REG_BIQ_CTRL 0x20 +#define NAU8825_REG_BIQ_COF1 0x21 +#define NAU8825_REG_BIQ_COF2 0x22 +#define NAU8825_REG_BIQ_COF3 0x23 +#define NAU8825_REG_BIQ_COF4 0x24 +#define NAU8825_REG_BIQ_COF5 0x25 +#define NAU8825_REG_BIQ_COF6 0x26 +#define NAU8825_REG_BIQ_COF7 0x27 +#define NAU8825_REG_BIQ_COF8 0x28 +#define NAU8825_REG_BIQ_COF9 0x29 +#define NAU8825_REG_BIQ_COF10 0x2a +#define NAU8825_REG_ADC_RATE 0x2b +#define NAU8825_REG_DAC_CTRL1 0x2c +#define NAU8825_REG_DAC_CTRL2 0x2d +#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f +#define NAU8825_REG_ADC_DGAIN_CTRL 0x30 +#define NAU8825_REG_MUTE_CTRL 0x31 +#define NAU8825_REG_HSVOL_CTRL 0x32 +#define NAU8825_REG_DACL_CTRL 0x33 +#define NAU8825_REG_DACR_CTRL 0x34 +#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38 +#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39 +#define NAU8825_REG_ADC_DRC_SLOPES 0x3a +#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b +#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45 +#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46 +#define NAU8825_REG_DAC_DRC_SLOPES 0x47 +#define NAU8825_REG_DAC_DRC_ATKDCY 0x48 +#define NAU8825_REG_IMM_MODE_CTRL 0x4c +#define NAU8825_REG_IMM_RMS_L 0x4d +#define NAU8825_REG_IMM_RMS_R 0x4e +#define NAU8825_REG_CLASSG_CTRL 0x50 +#define NAU8825_REG_OPT_EFUSE_CTRL 0x51 +#define NAU8825_REG_MISC_CTRL 0x55 +#define NAU8825_REG_I2C_DEVICE_ID 0x58 +#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 +#define NAU8825_REG_BIAS_ADJ 0x66 +#define NAU8825_REG_TRIM_SETTINGS 0x68 +#define NAU8825_REG_ANALOG_CONTROL_1 0x69 +#define NAU8825_REG_ANALOG_CONTROL_2 0x6a +#define NAU8825_REG_ANALOG_ADC_1 0x71 +#define NAU8825_REG_ANALOG_ADC_2 0x72 +#define NAU8825_REG_RDAC 0x73 +#define NAU8825_REG_MIC_BIAS 0x74 +#define NAU8825_REG_BOOST 0x76 +#define NAU8825_REG_FEPGA 0x77 +#define NAU8825_REG_POWER_UP_CONTROL 0x7f +#define NAU8825_REG_CHARGE_PUMP 0x80 +#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81 +#define NAU8825_REG_GENERAL_STATUS 0x82 +#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS + +/* ENA_CTRL (0x1) */ +#define NAU8825_ENABLE_DACR_SFT 10 +#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT) +#define NAU8825_ENABLE_DACL_SFT 9 +#define NAU8825_ENABLE_ADC_SFT 8 +#define NAU8825_ENABLE_SAR_SFT 1 + +/* CLK_DIVIDER (0x3) */ +#define NAU8825_CLK_SRC_SFT 15 +#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) + +/* FLL6 (0x9) */ +#define NAU8825_DCO_EN (1 << 15) + +/* HSD_CTRL (0xc) */ +#define NAU8825_HSD_AUTO_MODE (1 << 6) +/* 0 - short to GND, 1 - open */ +#define NAU8825_SPKR_DWN1R (1 << 1) +#define NAU8825_SPKR_DWN1L (1 << 0) + +/* JACK_DET_CTRL (0xd) */ +#define NAU8825_JACK_DET_RESTART (1 << 9) +#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5 +#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT) +#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2 +#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT) +#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */ + +/* INTERRUPT_MASK (0xf) */ +#define NAU8825_IRQ_OUTPUT_EN (1 << 11) +#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10) +#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5) +#define NAU8825_IRQ_EJECT_EN (1 << 2) + +/* IRQ_STATUS (0x10) */ +#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10) +#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9) +#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8) +#define NAU8825_KEY_IRQ_MASK (0x7 << 5) +#define NAU8825_KEY_RELEASE_IRQ (1 << 7) +#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6) +#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5) +#define NAU8825_MIC_DETECTION_IRQ (1 << 4) +#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2) +#define NAU8825_JACK_EJECTION_DETECTED (1 << 2) +#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0) +#define NAU8825_JACK_INSERTION_DETECTED (1 << 0) + +/* INTERRUPT_DIS_CTRL (0x12) */ +#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10) +#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5) +#define NAU8825_IRQ_EJECT_DIS (1 << 2) + +/* SAR_CTRL (0x13) */ +#define NAU8825_SAR_ADC_EN_SFT 12 +#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT) +#define NAU8825_SAR_INPUT_MASK (1 << 11) +#define NAU8825_SAR_INPUT_JKSLV (1 << 11) +#define NAU8825_SAR_INPUT_JKR2 (0 << 11) +#define NAU8825_SAR_TRACKING_GAIN_SFT 8 +#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT) +#define NAU8825_SAR_COMPARE_TIME_SFT 2 +#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2) +#define NAU8825_SAR_SAMPLING_TIME_SFT 0 +#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0) + +/* KEYDET_CTRL (0x14) */ +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12 +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT) +#define NAU8825_KEYDET_LEVELS_NR_SFT 8 +#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8) +#define NAU8825_KEYDET_HYSTERESIS_SFT 0 +#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf + +/* GPIO12_CTRL (0x1a) */ +#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */ +#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */ +#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */ + +/* I2S_PCM_CTRL1 (0x1c) */ +#define NAU8825_I2S_BP_SFT 7 +#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_PCMB_SFT 6 +#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_DL_SFT 2 +#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DF_SFT 0 +#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT) + +/* I2S_PCM_CTRL2 (0x1d) */ +#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */ +#define NAU8825_I2S_MS_SFT 3 +#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT) + +/* ADC_RATE (0x2b) */ +#define NAU8825_ADC_SYNC_DOWN_SFT 0 +#define NAU8825_ADC_SYNC_DOWN_MASK 0x3 +#define NAU8825_ADC_SYNC_DOWN_32 0 +#define NAU8825_ADC_SYNC_DOWN_64 1 +#define NAU8825_ADC_SYNC_DOWN_128 2 +#define NAU8825_ADC_SYNC_DOWN_256 3 + +/* DAC_CTRL1 (0x2c) */ +#define NAU8825_DAC_CLIP_OFF (1 << 7) +#define NAU8825_DAC_OVERSAMPLE_SFT 0 +#define NAU8825_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8825_DAC_OVERSAMPLE_64 0 +#define NAU8825_DAC_OVERSAMPLE_256 1 +#define NAU8825_DAC_OVERSAMPLE_128 2 +#define NAU8825_DAC_OVERSAMPLE_32 4 + +/* MUTE_CTRL (0x31) */ +#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9) +#define NAU8825_DAC_SOFT_MUTE (1 << 9) + +/* HSVOL_CTRL (0x32) */ +#define NAU8825_HP_MUTE (1 << 15) + +/* DACL_CTRL (0x33) */ +#define NAU8825_DACL_CH_SEL_SFT 9 + +/* DACR_CTRL (0x34) */ +#define NAU8825_DACR_CH_SEL_SFT 9 + +/* I2C_DEVICE_ID (0x58) */ +#define NAU8825_GPIO2JD1 (1 << 7) +#define NAU8825_SOFTWARE_ID_MASK 0x3 +#define NAU8825_SOFTWARE_ID_NAU8825 0x0 + +/* BIAS_ADJ (0x66) */ +#define NAU8825_BIAS_VMID (1 << 6) +#define NAU8825_BIAS_VMID_SEL_SFT 4 +#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_2 (0x6a) */ +#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12) +#define NAU8825_DAC_CAPACITOR_MSB (1 << 1) +#define NAU8825_DAC_CAPACITOR_LSB (1 << 0) + +/* ANALOG_ADC_2 (0x72) */ +#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8) +#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8) +#define NAU8825_ADC_VREFSEL_VMID (1 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) +#define NAU8825_POWERUP_ADCL (1 << 6) + +/* MIC_BIAS (0x74) */ +#define NAU8825_MICBIAS_JKSLV (1 << 14) +#define NAU8825_MICBIAS_JKR2 (1 << 12) +#define NAU8825_MICBIAS_POWERUP_SFT 8 +#define NAU8825_MICBIAS_VOLTAGE_SFT 0 +#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7 + +/* BOOST (0x76) */ +#define NAU8825_PRECHARGE_DIS (1 << 13) +#define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_HP_BOOST_G_DIS (1 << 8) +#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) + +/* POWER_UP_CONTROL (0x7f) */ +#define NAU8825_POWERUP_INTEGR_R (1 << 5) +#define NAU8825_POWERUP_INTEGR_L (1 << 4) +#define NAU8825_POWERUP_DRV_IN_R (1 << 3) +#define NAU8825_POWERUP_DRV_IN_L (1 << 2) +#define NAU8825_POWERUP_HP_DRV_R (1 << 1) +#define NAU8825_POWERUP_HP_DRV_L (1 << 0) + +/* CHARGE_PUMP (0x80) */ +#define NAU8825_JAMNODCLOW (1 << 10) +#define NAU8825_POWER_DOWN_DACR (1 << 9) +#define NAU8825_POWER_DOWN_DACL (1 << 8) +#define NAU8825_CHANRGE_PUMP_EN (1 << 5) + + +/* System Clock Source */ +enum { + NAU8825_CLK_MCLK = 0, + NAU8825_CLK_INTERNAL, +}; + +struct nau8825 { + struct device *dev; + struct regmap *regmap; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack *jack; + struct clk *mclk; + int irq; + int mclk_freq; /* 0 - mclk is disabled */ + int button_pressed; + int micbias_voltage; + int vref_impedance; + bool jkdet_enable; + bool jkdet_pull_enable; + bool jkdet_pull_up; + int jkdet_polarity; + int sar_threshold_num; + int sar_threshold[8]; + int sar_hysteresis; + int sar_voltage; + int sar_compare_time; + int sar_sampling_time; + int key_debounce; + int jack_insert_debounce; + int jack_eject_debounce; +}; + +int nau8825_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + + +#endif /* __NAU8825_H__ */ -- cgit v1.1 From cc91ef0fd4904fc130aecea8603585a9c0fe8384 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sat, 3 Oct 2015 03:37:59 +0800 Subject: ASoC: nau8825: fix platform_no_drv_owner.cocci warnings sound/soc/codecs/nau8825.c:1096:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Anatol Pomozov Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index f31a500..c44a7a1 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1093,7 +1093,6 @@ MODULE_DEVICE_TABLE(of, nau8825_of_ids); static struct i2c_driver nau8825_driver = { .driver = { .name = "nau8825", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(nau8825_of_ids), }, .probe = nau8825_i2c_probe, -- cgit v1.1 From c570b82c5e6ac78be35b4e72594c0f1b1888fce1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 7 Oct 2015 11:59:58 +0100 Subject: ASoC: sun4i-codec: Remove the routing property Most of the boards have their headphone jack directly connected to the matching pins of the SoCs. Since most of the time we will have the same routing path, it makes no sense to put that in the DTS, since it will only be some useless duplication there. It also fixes the following warning messages that were seen so far, on boards where we were using the bindings in the documentation example. sun4i-codec 1c22c00.codec: ASoC: no sink widget found for Headphone Jack sun4i-codec 1c22c00.codec: ASoC: Failed to add route HP Left -> direct -> Headphone Jack sun4i-codec 1c22c00.codec: ASoC: no sink widget found for Headphone Jack sun4i-codec 1c22c00.codec: ASoC: Failed to add route HP Right -> direct -> Headphone Jack Reported-by: Priit Laes Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 4778055..bcbf4da 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -571,7 +571,6 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) { struct snd_soc_card *card; - int ret; card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) @@ -584,12 +583,6 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) card->dev = dev; card->name = "sun4i-codec"; - ret = snd_soc_of_parse_audio_routing(card, "routing"); - if (ret) { - dev_err(dev, "Failed to create our audio routing\n"); - return NULL; - } - return card; }; -- cgit v1.1 From b7ebd78d1d142e4e47c3547b08faf51218384583 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 11:57:12 +0100 Subject: ASoC: da7219: Use of_match_ptr() when assigning match table Use of_match_ptr() to handle non-DT kernel scenario where match table should be NULL. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index c86a833..adcc079 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1932,7 +1932,7 @@ MODULE_DEVICE_TABLE(i2c, da7219_i2c_id); static struct i2c_driver da7219_i2c_driver = { .driver = { .name = "da7219", - .of_match_table = da7219_of_match, + .of_match_table = of_match_ptr(da7219_of_match), }, .probe = da7219_i2c_probe, .remove = da7219_i2c_remove, -- cgit v1.1 From ba856fbd602175d386ab5a4fc0fdd89b912546cb Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 11:57:14 +0100 Subject: ASoC: da7219: Improve error checking of mclk enable/disable Should only try to enable/disable the provided mclk, during bias level changes, if it's not NULL. Also return value of clk_prepare_enable() should be checked and dealt with accordingly. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index adcc079..abba4b3 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1494,6 +1494,7 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; switch (level) { case SND_SOC_BIAS_ON: @@ -1502,7 +1503,14 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* MCLK */ - clk_prepare_enable(da7219->mclk); + if (da7219->mclk) { + ret = clk_prepare_enable(da7219->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable mclk\n"); + return ret; + } + } /* Master bias */ snd_soc_update_bits(codec, DA7219_REFERENCES, @@ -1528,7 +1536,8 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, } /* MCLK */ - clk_disable_unprepare(da7219->mclk); + if (da7219->mclk) + clk_disable_unprepare(da7219->mclk); break; } -- cgit v1.1 From dcc448e619194098b24477a6d56af50c57f26f1d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 7 Oct 2015 12:42:22 +0200 Subject: ASoC: rsnd: Remove obsolete platform data support Since commit 3d7608e4c169af03 ("ARM: shmobile: bockw: remove legacy board file and config"), Renesas R-Car SoCs are only supported in generic DT-only ARM multi-platform builds. The driver doesn't need to use platform data anymore, hence remove platform data configuration. Move to sound/soc/sh/rcar/, as it's no longer needed by platform code. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 19 ++----- sound/soc/sh/rcar/rcar_snd.h | 117 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 3 +- sound/soc/sh/rcar/ssi.c | 3 -- 4 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 sound/soc/sh/rcar/rcar_snd.h (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index eec294d..6ef9a88 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1236,20 +1236,11 @@ static int rsnd_probe(struct platform_device *pdev) }; int ret, i; - info = NULL; - of_data = NULL; - if (of_id) { - info = devm_kzalloc(&pdev->dev, - sizeof(struct rcar_snd_info), GFP_KERNEL); - of_data = of_id->data; - } else { - info = pdev->dev.platform_data; - } - - if (!info) { - dev_err(dev, "driver needs R-Car sound information\n"); - return -ENODEV; - } + info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + of_data = of_id->data; /* * init priv data diff --git a/sound/soc/sh/rcar/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h new file mode 100644 index 0000000..d8e33d3 --- /dev/null +++ b/sound/soc/sh/rcar/rcar_snd.h @@ -0,0 +1,117 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RCAR_SND_H +#define RCAR_SND_H + + +#define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 + +#define RSND_GEN2_SCU 0 +#define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 + +#define RSND_BASE_MAX 4 + +/* + * flags + * + * 0xAB000000 + * + * A : clock sharing settings + * B : SSI direction + */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */ + +#define RSND_SSI(_dma_id, _irq, _flags) \ +{ .dma_id = _dma_id, .irq = _irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dma_id = -1, .irq = -1, .flags = 0 } + +struct rsnd_ssi_platform_info { + int dma_id; + int irq; + u32 flags; +}; + +#define RSND_SRC(rate, _dma_id) \ +{ .convert_rate = rate, .dma_id = _dma_id, } +#define RSND_SRC_UNUSED \ +{ .convert_rate = 0, .dma_id = -1, } + +struct rsnd_src_platform_info { + u32 convert_rate; /* sampling rate convert */ + int dma_id; /* for Gen2 SCU */ + int irq; +}; + +/* + * flags + */ +struct rsnd_ctu_platform_info { + u32 flags; +}; + +struct rsnd_mix_platform_info { + u32 flags; +}; + +struct rsnd_dvc_platform_info { + u32 flags; +}; + +struct rsnd_dai_path_info { + struct rsnd_ssi_platform_info *ssi; + struct rsnd_src_platform_info *src; + struct rsnd_ctu_platform_info *ctu; + struct rsnd_mix_platform_info *mix; + struct rsnd_dvc_platform_info *dvc; +}; + +struct rsnd_dai_platform_info { + struct rsnd_dai_path_info playback; + struct rsnd_dai_path_info capture; +}; + +/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN_MASK (0xF << 0) +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + +struct rcar_snd_info { + u32 flags; + struct rsnd_ssi_platform_info *ssi_info; + int ssi_info_nr; + struct rsnd_src_platform_info *src_info; + int src_info_nr; + struct rsnd_ctu_platform_info *ctu_info; + int ctu_info_nr; + struct rsnd_mix_platform_info *mix_info; + int mix_info_nr; + struct rsnd_dvc_platform_info *dvc_info; + int dvc_info_nr; + struct rsnd_dai_platform_info *dai_info; + int dai_info_nr; + int (*start)(int id); + int (*stop)(int id); +}; + +#endif diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index e4068d7..e9fef53 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -21,10 +21,11 @@ #include #include #include -#include #include #include +#include "rcar_snd.h" + /* * pseudo register * diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5e05f942..842a35b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -700,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, struct device *dev = &pdev->dev; int nr, i; - if (!of_data) - return; - node = rsnd_ssi_of_node(priv); if (!node) return; -- cgit v1.1 From e90996a3ea224fbeb459b8052ecd366d7990e1f3 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 14:27:08 +0100 Subject: ASoC: da7213: Add DT support to codec driver This patch adds support for DT bindings in the codec driver. As part of this support, the mclk data can now be provided and used to control the mclk during codec operation. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7213.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index a9c86ef..ab1486b 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1387,10 +1387,118 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, return 0; } +/* DT */ +static const struct of_device_id da7213_of_match[] = { + { .compatible = "dlg,da7213", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7213_of_match); + +static enum da7213_micbias_voltage + da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1600: + return DA7213_MICBIAS_1_6V; + case 2200: + return DA7213_MICBIAS_2_2V; + case 2500: + return DA7213_MICBIAS_2_5V; + case 3000: + return DA7213_MICBIAS_3_0V; + default: + dev_warn(codec->dev, "Invalid micbias level\n"); + return DA7213_MICBIAS_2_2V; + } +} + +static enum da7213_dmic_data_sel + da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "lrise_rfall")) { + return DA7213_DMIC_DATA_LRISE_RFALL; + } else if (!strcmp(str, "lfall_rrise")) { + return DA7213_DMIC_DATA_LFALL_RRISE; + } else { + dev_warn(codec->dev, "Invalid DMIC data select type\n"); + return DA7213_DMIC_DATA_LRISE_RFALL; + } +} + +static enum da7213_dmic_samplephase + da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "on_clkedge")) { + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } else if (!strcmp(str, "between_clkedge")) { + return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE; + } else { + dev_warn(codec->dev, "Invalid DMIC sample phase\n"); + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } +} + +static enum da7213_dmic_clk_rate + da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1500000: + return DA7213_DMIC_CLK_1_5MHZ; + case 3000000: + return DA7213_DMIC_CLK_3_0MHZ; + default: + dev_warn(codec->dev, "Invalid DMIC clock rate\n"); + return DA7213_DMIC_CLK_1_5MHZ; + } +} + +static struct da7213_platform_data + *da7213_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct da7213_platform_data *pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_warn(codec->dev, "Failed to allocate memory for pdata\n"); + return NULL; + } + + if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0) + pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32); + else + pdata->micbias1_lvl = DA7213_MICBIAS_2_2V; + + if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0) + pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32); + else + pdata->micbias2_lvl = DA7213_MICBIAS_2_2V; + + if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str)) + pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str); + else + pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL; + + if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str)) + pdata->dmic_samplephase = + da7213_of_dmic_samplephase(codec, of_str); + else + pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE; + + if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0) + pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32); + else + pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ; + + return pdata; +} + + static int da7213_probe(struct snd_soc_codec *codec) { struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); - struct da7213_platform_data *pdata = da7213->pdata; /* Default to using ALC auto offset calibration mode. */ snd_soc_update_bits(codec, DA7213_ALC_CTRL1, @@ -1450,8 +1558,15 @@ static int da7213_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, DA7213_LINE_CTRL, DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE); + /* Handle DT/Platform data */ + if (codec->dev->of_node) + da7213->pdata = da7213_of_to_pdata(codec); + else + da7213->pdata = dev_get_platdata(codec->dev); + /* Set platform data values */ if (da7213->pdata) { + struct da7213_platform_data *pdata = da7213->pdata; u8 micbias_lvl = 0, dmic_cfg = 0; /* Set Mic Bias voltages */ @@ -1507,6 +1622,7 @@ static int da7213_probe(struct snd_soc_codec *codec) /* Set MCLK squaring */ da7213->mclk_squarer_en = pdata->mclk_squaring; } + return 0; } @@ -1537,7 +1653,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da7213_priv *da7213; - struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev); int ret; da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv), @@ -1545,9 +1660,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c, if (!da7213) return -ENOMEM; - if (pdata) - da7213->pdata = pdata; - i2c_set_clientdata(i2c, da7213); da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config); @@ -1582,6 +1694,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id); static struct i2c_driver da7213_i2c_driver = { .driver = { .name = "da7213", + .of_match_table = of_match_ptr(da7213_of_match), }, .probe = da7213_i2c_probe, .remove = da7213_remove, -- cgit v1.1 From 6e7c444318699496e6e6f30c875cf67534aeccc6 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 14:27:11 +0100 Subject: ASoC: da7213: Add support to handle mclk data provided to driver Driver now can make use of mclk data, if provided, to set, enable and disable the clock source. As part of this, the choice to enable clock squaring is dealt with as part of dai_sysclk() call rather than as platform data. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7213.c | 67 +++++++++++++++++++++++++++++++++++++++-------- sound/soc/codecs/da7213.h | 8 ++++-- 2 files changed, 62 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index ab1486b..7278f93 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -12,6 +12,7 @@ * option) any later version. */ +#include #include #include #include @@ -1222,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq)) + return 0; + + if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } switch (clk_id) { case DA7213_CLKSRC_MCLK: - if ((freq == 32768) || - ((freq >= 5000000) && (freq <= 54000000))) { - da7213->mclk_rate = freq; - return 0; - } else { - dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", - freq); - return -EINVAL; - } + da7213->mclk_squarer_en = false; + break; + case DA7213_CLKSRC_MCLK_SQR: + da7213->mclk_squarer_en = true; break; default: dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); return -EINVAL; } + + da7213->clk_src = clk_id; + + if (da7213->mclk) { + freq = clk_round_rate(da7213->mclk, freq); + ret = clk_set_rate(da7213->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7213->mclk_rate = freq; + + return 0; } /* Supported PLL input frequencies are 5MHz - 54MHz. */ @@ -1366,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = { static int da7213_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* MCLK */ + if (da7213->mclk) { + ret = clk_prepare_enable(da7213->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable mclk\n"); + return ret; + } + } + /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, @@ -1382,6 +1417,10 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, /* Disable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, 0); + + /* MCLK */ + if (da7213->mclk) + clk_disable_unprepare(da7213->mclk); break; } return 0; @@ -1618,9 +1657,15 @@ static int da7213_probe(struct snd_soc_codec *codec) DA7213_DMIC_DATA_SEL_MASK | DA7213_DMIC_SAMPLEPHASE_MASK | DA7213_DMIC_CLK_RATE_MASK, dmic_cfg); + } - /* Set MCLK squaring */ - da7213->mclk_squarer_en = pdata->mclk_squaring; + /* Check if MCLK provided */ + da7213->mclk = devm_clk_get(codec->dev, "mclk"); + if (IS_ERR(da7213->mclk)) { + if (PTR_ERR(da7213->mclk) != -ENOENT) + return PTR_ERR(da7213->mclk); + else + da7213->mclk = NULL; } return 0; diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 9cb9ddd..030fd69 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -13,6 +13,7 @@ #ifndef _DA7213_H #define _DA7213_H +#include #include #include @@ -504,14 +505,17 @@ #define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 #define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 -enum clk_src { - DA7213_CLKSRC_MCLK +enum da7213_clk_src { + DA7213_CLKSRC_MCLK = 0, + DA7213_CLKSRC_MCLK_SQR, }; /* Codec private data */ struct da7213_priv { struct regmap *regmap; + struct clk *mclk; unsigned int mclk_rate; + int clk_src; bool master; bool mclk_squarer_en; bool srm_en; -- cgit v1.1 From e4e2d2f45284f620eeb9eea25570838b2a42c968 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:52 +0100 Subject: ASoC: Intel: Skylake: Add pipe and modules handlers SKL driver needs to instantiate pipelines and modules in the DSP. The topology in the DSP is modelled as DAPM graph with a PGA representing a module instance and mixer representing a pipeline for a group of modules along with the mixer itself. Here we start adding building block for handling these. We add resource checks (memory/compute) for pipelines, find the modules in a pipeline, init modules in a pipe and lastly bind/unbind modules in a pipe These will be used by pipe event handlers in subsequent patches Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/Makefile | 3 +- sound/soc/intel/skylake/skl-topology.c | 187 +++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 10 ++ sound/soc/intel/skylake/skl.h | 11 ++ 4 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/skylake/skl-topology.c (limited to 'sound') diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 27db221..914b6da 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,4 +1,5 @@ -snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o +snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ +skl-topology.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c new file mode 100644 index 0000000..b0ee1fe --- /dev/null +++ b/sound/soc/intel/skylake/skl-topology.c @@ -0,0 +1,187 @@ +/* + * skl-topology.c - Implements Platform component ALSA controls/widget + * handlers. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" +#include "skl-topology.h" +#include "skl.h" +#include "skl-tplg-interface.h" + +/* + * SKL DSP driver modelling uses only few DAPM widgets so for rest we will + * ignore. This helpers checks if the SKL driver handles this widget type + */ +static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w) +{ + switch (w->id) { + case snd_soc_dapm_dai_link: + case snd_soc_dapm_dai_in: + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_switch: + return false; + default: + return true; + } +} + +/* + * Each pipelines needs memory to be allocated. Check if we have free memory + * from available pool. Then only add this to pool + * This is freed when pipe is deleted + * Note: DSP does actual memory management we only keep track for complete + * pool + */ +static bool skl_tplg_alloc_pipe_mem(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mem + mconfig->pipe->memory_pages > + skl->resource.max_mem) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, + mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d mem %d\n", + skl->resource.max_mem, skl->resource.mem); + return false; + } + + skl->resource.mem += mconfig->pipe->memory_pages; + return true; +} + +/* + * Pipeline needs needs DSP CPU resources for computation, this is + * quantified in MCPS (Million Clocks Per Second) required for module/pipe + * + * Each pipelines needs mcps to be allocated. Check if we have mcps for this + * pipe. This adds the mcps to driver counter + * This is removed on pipeline delete + */ +static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d > mem %d\n", + skl->resource.max_mcps, skl->resource.mcps); + return false; + } + + skl->resource.mcps += mconfig->mcps; + return true; +} + +/* + * Free the mcps when tearing down + */ +static void +skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mcps -= mconfig->mcps; +} + +/* + * Free the memory when tearing down + */ +static void +skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mem -= mconfig->pipe->memory_pages; +} + +/* + * A pipe can have multiple modules, each of them will be a DAPM widget as + * well. While managing a pipeline we need to get the list of all the + * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps + * to get the SKL type widgets in that pipeline + */ +static int skl_tplg_alloc_pipe_widget(struct device *dev, + struct snd_soc_dapm_widget *w, struct skl_pipe *pipe) +{ + struct skl_module_cfg *src_module = NULL; + struct snd_soc_dapm_path *p = NULL; + struct skl_pipe_module *p_module = NULL; + + p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL); + if (!p_module) + return -ENOMEM; + + p_module->w = w; + list_add_tail(&p_module->node, &pipe->w_list); + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if ((p->sink->priv == NULL) + && (!is_skl_dsp_widget_type(w))) + continue; + + if ((p->sink->priv != NULL) && p->connect + && is_skl_dsp_widget_type(p->sink)) { + + src_module = p->sink->priv; + if (pipe->ppl_id == src_module->pipe->ppl_id) + skl_tplg_alloc_pipe_widget(dev, + p->sink, pipe); + } + } + return 0; +} + +/* + * Inside a pipe instance, we can have various modules. These modules need + * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by + * skl_init_module() routine, so invoke that for all modules in a pipeline + */ +static int +skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) +{ + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mconfig = w->priv; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -ENOMEM; + + ret = skl_init_module(ctx, mconfig, NULL); + if (ret < 0) + return ret; + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 8c7767b..73d7916 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -263,6 +263,16 @@ struct skl_module_cfg { struct skl_specific_cfg formats_config; }; +struct skl_pipeline { + struct skl_pipe *pipe; + struct list_head node; +}; + +struct skl_dapm_path_list { + struct snd_soc_dapm_path *dapm_path; + struct list_head node; +}; + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index f7fdbb0..e980d78 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -48,6 +48,13 @@ #define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 +struct skl_dsp_resource { + u32 max_mcps; + u32 max_mem; + u32 mcps; + u32 mem; +}; + struct skl { struct hdac_ext_bus ebus; struct pci_dev *pci; @@ -57,6 +64,10 @@ struct skl { void __iomem *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ + + struct skl_dsp_resource resource; + struct list_head ppl_list; + struct list_head dapm_path_list; }; #define skl_to_ebus(s) (&(s)->ebus) -- cgit v1.1 From f7590d4f1565b0d609d25113c37b9bea8a13e9f7 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:53 +0100 Subject: ASoC: Intel: Skylake: Add module configuration helpers To configure a module, driver needs to send input and output PCM params for a module in DSP. The FE PCM params come from hw_params ie from user, for a BE they also come from hw_params but from BE-link fixups. So based on PCM params required driver has to find a converter module (src/updown/format) and then do the conversion and calculate PCM params in these pipelines In this patch we add the helper modules which allow driver to do these calculations. Signed-off-by: Hardik T Shah Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 140 +++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b0ee1fe..56baf9d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -27,6 +27,10 @@ #include "skl.h" #include "skl-tplg-interface.h" +#define SKL_CH_FIXUP_MASK (1 << 0) +#define SKL_RATE_FIXUP_MASK (1 << 1) +#define SKL_FMT_FIXUP_MASK (1 << 2) + /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type @@ -119,6 +123,137 @@ skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig) skl->resource.mem -= mconfig->pipe->memory_pages; } + +static void skl_dump_mconfig(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + dev_dbg(ctx->dev, "Dumping config\n"); + dev_dbg(ctx->dev, "Input Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->in_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "Output Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->out_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg); +} + +static void skl_tplg_update_params(struct skl_module_fmt *fmt, + struct skl_pipe_params *params, int fixup) +{ + if (fixup & SKL_RATE_FIXUP_MASK) + fmt->s_freq = params->s_freq; + if (fixup & SKL_CH_FIXUP_MASK) + fmt->channels = params->ch; + if (fixup & SKL_FMT_FIXUP_MASK) + fmt->valid_bit_depth = params->s_fmt; +} + +/* + * A pipeline may have modules which impact the pcm parameters, like SRC, + * channel converter, format converter. + * We need to calculate the output params by applying the 'fixup' + * Topology will tell driver which type of fixup is to be applied by + * supplying the fixup mask, so based on that we calculate the output + * + * Now In FE the pcm hw_params is source/target format. Same is applicable + * for BE with its hw_params invoked. + * here based on FE, BE pipeline and direction we calculate the input and + * outfix and then apply that for a module + */ +static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg, + struct skl_pipe_params *params, bool is_fe) +{ + int in_fixup, out_fixup; + struct skl_module_fmt *in_fmt, *out_fmt; + + in_fmt = &m_cfg->in_fmt; + out_fmt = &m_cfg->out_fmt; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (is_fe) { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } else { + if (is_fe) { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } + + skl_tplg_update_params(in_fmt, params, in_fixup); + skl_tplg_update_params(out_fmt, params, out_fixup); +} + +/* + * A module needs input and output buffers, which are dependent upon pcm + * params, so once we have calculate params, we need buffer calculation as + * well. + */ +static void skl_tplg_update_buffer_size(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + int multiplier = 1; + + if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT) + multiplier = 5; + + mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) * + (mcfg->in_fmt.channels) * + (mcfg->in_fmt.bit_depth >> 3) * + multiplier; + + mcfg->obs = (mcfg->out_fmt.s_freq / 1000) * + (mcfg->out_fmt.channels) * + (mcfg->out_fmt.bit_depth >> 3) * + multiplier; +} + +static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx) +{ + struct skl_module_cfg *m_cfg = w->priv; + struct skl_pipe_params *params = m_cfg->pipe->p_params; + int p_conn_type = m_cfg->pipe->conn_type; + bool is_fe; + + if (!m_cfg->params_fixup) + return; + + dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); + + if (p_conn_type == SKL_PIPE_CONN_TYPE_FE) + is_fe = true; + else + is_fe = false; + + skl_tplg_update_params_fixup(m_cfg, params, is_fe); + skl_tplg_update_buffer_size(ctx, m_cfg); + + dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); +} + /* * A pipe can have multiple modules, each of them will be a DAPM widget as * well. While managing a pipeline we need to get the list of all the @@ -178,6 +313,11 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) return -ENOMEM; + /* + * apply fix/conversion to module params based on + * FE/BE params + */ + skl_tplg_update_module_params(w, ctx); ret = skl_init_module(ctx, mconfig, NULL); if (ret < 0) return ret; -- cgit v1.1 From d93f8e550f44ed1f54285f77f40f7962a3f25267 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:54 +0100 Subject: ASoC: Intel: Skylake: add DSP platform widget event handlers The Skylake driver topology model tries to model the firmware rule for pipeline and module creation. The creation rule is: - Create Pipe - Add modules to Pipe - Connect the modules (bind) - Start the pipes Similarly destroy rule is: - Stop the pipe - Disconnect it (unbind) - Delete the pipe In driver we use Mixer, as there will always be ONE mixer in a pipeline to model a pipe. The modules in pipe are modelled as PGA widgets. The DAPM sequencing rules (mixer and then PGA) are used to create the sequence DSP expects as depicted above, and then widget handlers for PMU and PMD events help in that. This patch adds widget event handlers for PRE/POST PMU and PRE/POST PMD event for mixer and pga modules. These event handlers invoke pipeline creation, destroy, module creation, module bind, unbind and pipeline bind unbind Event handler sequencing is implement to target the DSP FW sequence expectations to enable path from source to sink pipe for Playback/Capture. Signed-off-by: Jeeja KP Signed-off-by: Hardik T Shah Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 448 +++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 7 + 2 files changed, 455 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 56baf9d..6d1d29a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -325,3 +325,451 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) return 0; } + +/* + * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we + * need create the pipeline. So we do following: + * - check the resources + * - Create the pipeline + * - Initialize the modules in pipeline + * - finally bind all modules together + */ +static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret; + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_pipe *s_pipe = mconfig->pipe; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -EBUSY; + + if (!skl_tplg_alloc_pipe_mem(skl, mconfig)) + return -ENOMEM; + + /* + * Create a list of modules for pipe. + * This list contains modules from source to sink + */ + ret = skl_create_pipeline(ctx, mconfig->pipe); + if (ret < 0) + return ret; + + /* + * we create a w_list of all widgets in that pipe. This list is not + * freed on PMD event as widgets within a pipe are static. This + * saves us cycles to get widgets in pipe every time. + * + * So if we have already initialized all the widgets of a pipeline + * we skip, so check for list_empty and create the list if empty + */ + if (list_empty(&s_pipe->w_list)) { + ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe); + if (ret < 0) + return ret; + } + + /* Init all pipe modules from source to sink */ + ret = skl_tplg_init_pipe_modules(skl, s_pipe); + if (ret < 0) + return ret; + + /* Bind modules from source to sink */ + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_bind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + return 0; +} + +/* + * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA + * we need to do following: + * - Bind to sink pipeline + * Since the sink pipes can be running and we don't get mixer event on + * connect for already running mixer, we need to find the sink pipes + * here and bind to them. This way dynamic connect works. + * - Start sink pipeline, if not running + * - Then run current pipe + */ +static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct skl_dapm_path_list *path_list; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + source = w; + src_mconfig = source->priv; + + /* + * find which sink it is connected to, bind with the sink, + * if sink is not started, start sink pipe first, then start + * this pipe + */ + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name); + dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->sink->priv != NULL) && + is_skl_dsp_widget_type(p->sink)) { + + sink = p->sink; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + + /* Bind source to sink, mixin is always source */ + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + /* Start sinks pipe first */ + if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + } + + path_list = kzalloc( + sizeof(struct skl_dapm_path_list), + GFP_KERNEL); + if (path_list == NULL) + return -ENOMEM; + + /* Add connected path to one global list */ + path_list->dapm_path = p; + list_add_tail(&path_list->node, &skl->dapm_path_list); + break; + } + } + + /* Start source pipe last after starting all sinks */ + ret = skl_run_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + return 0; +} + +/* + * in the Post-PMU event of mixer we need to do following: + * - Check if this pipe is running + * - if not, then + * - bind this pipeline to its source pipeline + * if source pipe is already running, this means it is a dynamic + * connection and we need to bind only to that pipe + * - start this pipeline + */ +static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret = 0; + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int src_pipe_started = 0; + + sink = w; + sink_mconfig = sink->priv; + + /* + * If source pipe is already started, that means source is driving + * one more sink before this sink got connected, Since source is + * started, bind this sink to source and start this pipe. + */ + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "sink widget=%s\n", w->name); + dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->source->priv != NULL) && + is_skl_dsp_widget_type(p->source)) { + source = p->source; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + src_pipe_started = 1; + + /* + * check pipe state, then no need to bind or start + * the pipe + */ + if (src_mconfig->pipe->state != SKL_PIPE_STARTED) + src_pipe_started = 0; + } + } + + if (src_pipe_started) { + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + } + + return ret; +} + +/* + * in the Pre-PMD event of mixer we need to do following: + * - Stop the pipe + * - find the source connections and remove that from dapm_path_list + * - unbind with source pipelines if still connected + */ +static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_list; + struct skl_sst *ctx = skl->skl_sst; + + sink = w; + sink_mconfig = sink->priv; + + /* Stop the pipe */ + ret = skl_stop_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + + /* + * This list, dapm_path_list handling here does not need any locks + * as we are under dapm lock while handling widget events. + * List can be manipulated safely only under dapm widgets handler + * routines + */ + list_for_each_entry_safe(path_list, tmp_list, + &skl->dapm_path_list, node) { + if (path_list->dapm_path->sink == sink) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + source = path_list->dapm_path->source; + src_mconfig = source->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * If path_found == 1, that means pmd for source pipe has + * not occurred, source is connected to some other sink. + * so its responsibility of sink to unbind itself from source. + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * in the Post-PMD event of mixer we need to do following: + * - Free the mcps used + * - Free the mem used + * - Unbind the modules within the pipeline + * - Delete the pipeline (modules are not required to be explicitly + * deleted, pipeline delete is enough here + */ +static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + struct skl_pipe *s_pipe = mconfig->pipe; + int ret = 0; + + skl_tplg_free_pipe_mcps(skl, mconfig); + + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_unbind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + ret = skl_delete_pipe(ctx, mconfig->pipe); + skl_tplg_free_pipe_mem(skl, mconfig); + + return ret; +} + +/* + * in the Post-PMD event of PGA we need to do following: + * - Free the mcps used + * - Stop the pipeline + * - In source pipe is connected, unbind with source pipelines + */ +static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_path_list; + struct skl_sst *ctx = skl->skl_sst; + + source = w; + src_mconfig = source->priv; + + skl_tplg_free_pipe_mcps(skl, src_mconfig); + /* Stop the pipe since this is a mixin module */ + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) { + if (path_list->dapm_path->source == source) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + sink = path_list->dapm_path->sink; + sink_mconfig = sink->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * This is a connector and if path is found that means + * unbind between source and sink has not happened yet + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If + * mixer is not required then it is treated as static mixer aka vmixer with + * a hard path to source module + * So we don't need to check if source is started or not as hard path puts + * dependency on each other + */ +static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If a + * second one is required that is created as another pipe entity. + * The mixer is responsible for pipe management and represent a pipeline + * instance + */ +static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_mixer_dapm_post_pmu_event(w, skl); + + case SND_SOC_DAPM_PRE_PMD: + return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assumed rest of the modules in pipeline are PGA. But we + * are interested in last PGA (leaf PGA) in a pipeline to disconnect with + * the sink when it is running (two FE to one BE or one FE to two BE) + * scenarios + */ +static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_pga_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_pga_dapm_post_pmd_event(w, skl); + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 73d7916..b3a22a6 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -273,6 +273,13 @@ struct skl_dapm_path_list { struct list_head node; }; +static inline struct skl *get_skl_ctx(struct device *dev) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + + return ebus_to_skl(ebus); +} + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); -- cgit v1.1 From cfb0a87383c6c2827f108ecee3471ef981876b38 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:55 +0100 Subject: ASoC: Intel: Skylake: Add FE and BE hw_params handling For FE and BE, the PCM parameters come from FE and BE hw_params values passed. For a FE we convert the FE params to DSP expected module format and pass to DSP. For a BE we need to find the gateway settings (i2s/PDM) to be applied. These are queried from NHLT table and applied. Further for BE based on direction the settings are applied as either source or destination parameters. These helpers here allow the format to be calculated and queried as per firmware format. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 238 +++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 12 ++ 2 files changed, 250 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 6d1d29a..827f21d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -773,3 +773,241 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } + +/* + * The FE params are passed by hw_params of the DAI. + * On hw_params, the params are stored in Gateway module of the FE and we + * need to calculate the format in DSP module configuration, that + * conversion is done here + */ +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct skl_module_fmt *format = NULL; + + memcpy(pipe->p_params, params, sizeof(*params)); + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) + format = &mconfig->in_fmt; + else + format = &mconfig->out_fmt; + + /* set the hw_params */ + format->s_freq = params->s_freq; + format->channels = params->ch; + format->valid_bit_depth = skl_get_bit_depth(params->s_fmt); + + /* + * 16 bit is 16 bit container whereas 24 bit is in 32 bit + * container so update bit depth accordingly + */ + switch (format->valid_bit_depth) { + case SKL_DEPTH_16BIT: + format->bit_depth = format->valid_bit_depth; + break; + + case SKL_DEPTH_24BIT: + format->bit_depth = SKL_DEPTH_32BIT; + break; + + default: + dev_err(dev, "Invalid bit depth %x for pipe\n", + format->valid_bit_depth); + return -EINVAL; + } + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mconfig->ibs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } else { + mconfig->obs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } + + return 0; +} + +/* + * Query the module config for the FE DAI + * This is used to find the hw_params set for that DAI and apply to FE + * pipeline + */ +struct skl_module_cfg * +skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && p->sink->power && + is_skl_dsp_widget_type(p->sink)) + continue; + + if (p->sink->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->sink->name); + return p->sink->priv; + } + } + } else { + w = dai->capture_widget; + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && p->source->power && + is_skl_dsp_widget_type(p->source)) + continue; + + if (p->source->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->source->name); + return p->source->priv; + } + } + } + + return NULL; +} + +static u8 skl_tplg_be_link_type(int dev_type) +{ + int ret; + + switch (dev_type) { + case SKL_DEVICE_BT: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_DMIC: + ret = NHLT_LINK_DMIC; + break; + + case SKL_DEVICE_I2S: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_HDALINK: + ret = NHLT_LINK_HDA; + break; + + default: + ret = NHLT_LINK_INVALID; + break; + } + + return ret; +} + +/* + * Fill the BE gateway parameters + * The BE gateway expects a blob of parameters which are kept in the ACPI + * NHLT blob, so query the blob for interface type (i2s/pdm) and instance. + * The port can have multiple settings so pick based on the PCM + * parameters + */ +static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct nhlt_specific_cfg *cfg; + struct skl *skl = get_skl_ctx(dai->dev); + int link_type = skl_tplg_be_link_type(mconfig->dev_type); + + memcpy(pipe->p_params, params, sizeof(*params)); + + /* update the blob based on virtual bus_id*/ + cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + params->s_fmt, params->ch, + params->s_freq, params->stream); + if (cfg) { + mconfig->formats_config.caps_size = cfg->size; + memcpy(mconfig->formats_config.caps, &cfg->caps, cfg->size); + } else { + dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", + mconfig->vbus_id, link_type, + params->stream); + dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n", + params->ch, params->s_freq, params->s_fmt); + return -EINVAL; + } + + return 0; +} + +static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->source) && + p->source->priv) { + + if (!p->source->power) + return skl_tplg_be_fill_pipe_params( + dai, p->source->priv, + params); + else + return -EBUSY; + } else { + return skl_tplg_be_set_src_pipe_params( + dai, p->source, params); + } + } + + return -EIO; +} + +static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p = NULL; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->sink) && + p->sink->priv) { + + if (!p->sink->power) + return skl_tplg_be_fill_pipe_params( + dai, p->sink->priv, params); + else + return -EBUSY; + + } else { + return skl_tplg_be_set_sink_pipe_params( + dai, p->sink, params); + } + } + + return -EIO; +} + +/* + * BE hw_params can be a source parameters (capture) or sink parameters + * (playback). Based on sink and source we need to either find the source + * list or the sink list and set the pipeline parameters + */ +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_widget *w; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + + return skl_tplg_be_set_src_pipe_params(dai, w, params); + + } else { + w = dai->capture_widget; + + return skl_tplg_be_set_sink_pipe_params(dai, w, params); + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b3a22a6..12db94d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -280,6 +280,18 @@ static inline struct skl *get_skl_ctx(struct device *dev) return ebus_to_skl(ebus); } +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params); +void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, + struct skl_pipe_params *params, int stream); +int skl_tplg_init(struct snd_soc_platform *platform, + struct hdac_ext_bus *ebus); +struct skl_module_cfg *skl_tplg_fe_get_cpr_module( + struct snd_soc_dai *dai, int stream); +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, struct skl_pipe_params *params); + + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); -- cgit v1.1 From 3af36706ff6c4ea8695e92b1ba80e183f1919684 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:56 +0100 Subject: ASoC: Intel: Skylake: Add topology core init and handlers The SKL driver does not code DSP topology in driver. It uses the newly added ASoC topology core to parse the topology information (controls, widgets and map) from topology binary. Each topology element passed private data which contains information that driver used to identify the module instance within firmware and send IPCs for that module to DSP firmware along with parameters. This patch adds init routine to invoke topology load and callback for topology creation. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl-topology.c | 218 +++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 - sound/soc/intel/skylake/skl-tplg-interface.h | 80 ++++++++++ 4 files changed, 299 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 05fde5e6e..664df1f 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -138,4 +138,5 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE + select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 827f21d..648bbbf 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1011,3 +1011,221 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, return 0; } + +static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { + {SKL_MIXER_EVENT, skl_tplg_mixer_event}, + {SKL_VMIXER_EVENT, skl_tplg_vmixer_event}, + {SKL_PGA_EVENT, skl_tplg_pga_event}, +}; + +/* + * The topology binary passes the pin info for a module so initialize the pin + * info passed into module instance + */ +static void skl_fill_module_pin_info(struct device *dev, + struct skl_module_pin *m_pin, + int max_pin) +{ + int i; + + for (i = 0; i < max_pin; i++) { + m_pin[i].id.module_id = 0; + m_pin[i].id.instance_id = 0; + m_pin[i].in_use = false; + m_pin[i].is_dynamic = true; + m_pin[i].pin_index = i; + } +} + +/* + * Add pipeline from topology binary into driver pipeline list + * + * If already added we return that instance + * Otherwise we create a new instance and add into driver list + */ +static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, + struct skl *skl, struct skl_dfw_pipe *dfw_pipe) +{ + struct skl_pipeline *ppl; + struct skl_pipe *pipe; + struct skl_pipe_params *params; + + list_for_each_entry(ppl, &skl->ppl_list, node) { + if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) + return ppl->pipe; + } + + ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); + if (!ppl) + return NULL; + + pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); + if (!pipe) + return NULL; + + params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); + if (!params) + return NULL; + + pipe->ppl_id = dfw_pipe->pipe_id; + pipe->memory_pages = dfw_pipe->memory_pages; + pipe->pipe_priority = dfw_pipe->pipe_priority; + pipe->conn_type = dfw_pipe->conn_type; + pipe->state = SKL_PIPE_INVALID; + pipe->p_params = params; + INIT_LIST_HEAD(&pipe->w_list); + + ppl->pipe = pipe; + list_add(&ppl->node, &skl->ppl_list); + + return ppl->pipe; +} + +/* + * Topology core widget load callback + * + * This is used to save the private data for each widget which gives + * information to the driver about module and pipeline parameters which DSP + * FW expects like ids, resource values, formats etc + */ +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + int ret; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl_module_cfg *mconfig; + struct skl_pipe *pipe; + struct skl_dfw_module *dfw_config = (struct skl_dfw_module *)tplg_w->priv.data; + + if (!tplg_w->priv.size) + goto bind_event; + + mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL); + + if (!mconfig) + return -ENOMEM; + + w->priv = mconfig; + mconfig->id.module_id = dfw_config->module_id; + mconfig->id.instance_id = dfw_config->instance_id; + mconfig->mcps = dfw_config->max_mcps; + mconfig->ibs = dfw_config->ibs; + mconfig->obs = dfw_config->obs; + mconfig->core_id = dfw_config->core_id; + mconfig->max_in_queue = dfw_config->max_in_queue; + mconfig->max_out_queue = dfw_config->max_out_queue; + mconfig->is_loadable = dfw_config->is_loadable; + mconfig->in_fmt.channels = dfw_config->in_fmt.channels; + mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; + mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; + mconfig->in_fmt.valid_bit_depth = dfw_config->in_fmt.valid_bit_depth; + mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; + mconfig->out_fmt.channels = dfw_config->out_fmt.channels; + mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; + mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; + mconfig->out_fmt.valid_bit_depth = dfw_config->out_fmt.valid_bit_depth; + mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; + mconfig->params_fixup = dfw_config->params_fixup; + mconfig->converter = dfw_config->converter; + mconfig->m_type = dfw_config->module_type; + mconfig->vbus_id = dfw_config->vbus_id; + + pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe); + if (pipe) + mconfig->pipe = pipe; + + mconfig->dev_type = dfw_config->dev_type; + mconfig->hw_conn_type = dfw_config->hw_conn_type; + mconfig->time_slot = dfw_config->time_slot; + mconfig->formats_config.caps_size = dfw_config->caps.caps_size; + + mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); + if (!mconfig->m_in_pin) + return -ENOMEM; + + mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); + if (!mconfig->m_out_pin) + return -ENOMEM; + + skl_fill_module_pin_info(bus->dev, mconfig->m_in_pin, + mconfig->max_in_queue); + skl_fill_module_pin_info(bus->dev, mconfig->m_out_pin, + mconfig->max_out_queue); + + if (mconfig->formats_config.caps_size == 0) + goto bind_event; + + mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, + mconfig->formats_config.caps_size, GFP_KERNEL); + + if (mconfig->formats_config.caps == NULL) + return -ENOMEM; + + memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, + dfw_config->caps.caps_size); + +bind_event: + if (tplg_w->event_type == 0) { + dev_info(bus->dev, "ASoC: No event handler required\n"); + return 0; + } + + ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, + ARRAY_SIZE(skl_tplg_widget_ops), tplg_w->event_type); + + if (ret) { + dev_err(bus->dev, "%s: No matching event handlers found for %d\n", + __func__, tplg_w->event_type); + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_tplg_ops skl_tplg_ops = { + .widget_load = skl_tplg_widget_load, +}; + +/* This will be read from topology manifest, currently defined here */ +#define SKL_MAX_MCPS 30000000 +#define SKL_FW_MAX_MEM 1000000 + +/* + * SKL topology init routine + */ +int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) +{ + int ret; + const struct firmware *fw; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + if (ret < 0) { + dev_err(bus->dev, "config firmware %s request failed with %d\n", + "dfw_sst.bin", ret); + return ret; + } + + /* + * The complete tplg for SKL is loaded as index 0, we don't use + * any other index + */ + ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0); + if (ret < 0) { + dev_err(bus->dev, "tplg component load failed%d\n", ret); + return -EINVAL; + } + + skl->resource.max_mcps = SKL_MAX_MCPS; + skl->resource.max_mem = SKL_FW_MAX_MEM; + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 12db94d..0c83dc3 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -291,7 +291,6 @@ struct skl_module_cfg *skl_tplg_fe_get_cpr_module( int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params); - int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index a506898..d14f7fe 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -19,6 +19,29 @@ #ifndef __HDA_TPLG_INTERFACE_H__ #define __HDA_TPLG_INTERFACE_H__ +/* + * Default types range from 0~12. type can range from 0 to 0xff + * SST types start at higher to avoid any overlapping in future + */ +#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100 +#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103 + +#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ +#define MAX_IN_QUEUE 8 +#define MAX_OUT_QUEUE 8 + +/* Event types goes here */ +/* Reserve event type 0 for no event handlers */ +enum skl_event_types { + SKL_EVENT_NONE = 0, + SKL_MIXER_EVENT, + SKL_MUX_EVENT, + SKL_VMIXER_EVENT, + SKL_PGA_EVENT +}; + /** * enum skl_ch_cfg - channel configuration * @@ -85,4 +108,61 @@ enum skl_dev_type { SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_NONE }; + +struct skl_dfw_module_pin { + u16 module_id; + u16 instance_id; + u8 pin_id; + bool is_dynamic; +} __packed; + +struct skl_dfw_module_fmt { + u32 channels; + u32 freq; + u32 bit_depth; + u32 valid_bit_depth; + u32 ch_cfg; +} __packed; + +struct skl_dfw_module_caps { + u32 caps_size; + u32 caps[HDA_SST_CFG_MAX]; +}; + +struct skl_dfw_pipe { + u8 pipe_id; + u8 pipe_priority; + u16 conn_type; + u32 memory_pages; +} __packed; + +struct skl_dfw_module { + u16 module_id; + u16 instance_id; + u32 max_mcps; + u8 core_id; + u8 max_in_queue; + u8 max_out_queue; + u8 is_loadable; + u8 conn_type; + u8 dev_type; + u8 hw_conn_type; + u8 time_slot; + u32 obs; + u32 ibs; + u32 params_fixup; + u32 converter; + u32 module_type; + u32 vbus_id; + struct skl_dfw_pipe pipe; + struct skl_dfw_module_fmt in_fmt; + struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_caps caps; +} __packed; + +struct skl_dfw_algo_data { + u32 max; + char *params; +} __packed; + #endif -- cgit v1.1 From b663a8c5c9c043c47e5e0eea9f9c77ea88fbe67a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:57 +0100 Subject: ASoC: Intel: Skylake: Initialize and load DSP controls Initialize and creates DSP controls if processing pipe capability is supported by HW. Updates the dma_id, hw_params to module param to be used when DSP module has to be configured. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 141 ++++++++++++++++++++++++++------- sound/soc/intel/skylake/skl-topology.c | 39 +++++---- 2 files changed, 136 insertions(+), 44 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bea2673..a8f53da 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -24,6 +24,7 @@ #include #include #include "skl.h" +#include "skl-topology.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct skl_pipe_params p_params = {0}; + struct skl_module_cfg *m_cfg; int ret, dma_id; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); @@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.host_dma_id = dma_id; + p_params.stream = substream->stream; + + m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream); + if (m_cfg) + skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params); + return 0; } @@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, return skl_substream_free_pages(ebus_to_hbus(ebus), substream); } +static int skl_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct skl_pipe_params p_params = {0}; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + skl_tplg_be_update_params(dai, &p_params); + + return 0; +} + +static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_sst *ctx = skl->skl_sst; + struct skl_module_cfg *mconfig; + + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + if (!mconfig) + return -EIO; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + return skl_run_pipe(ctx, mconfig->pipe); + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + return skl_stop_pipe(ctx, mconfig->pipe); + + default: + return 0; + } +} + static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - int dma_id; + struct skl_pipe_params p_params = {0}; - pr_debug("%s\n", __func__); link_dev = snd_hdac_ext_stream_assign(ebus, substream, HDAC_EXT_STREAM_TYPE_LINK); if (!link_dev) @@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, if (dma_params) dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params); - dma_id = hdac_stream(link_dev)->stream_tag - 1; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; + + skl_tplg_be_update_params(dai, &p_params); return 0; } @@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, unsigned int format_val = 0; struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_pcm_hw_params *params; - struct snd_interval *channels, *rate; struct hdac_ext_link *link; - dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); if (link_dev->link_prepared) { dev_dbg(dai->dev, "already stream is prepared - returning\n"); return 0; } - params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL); - if (params == NULL) - return -ENOMEM; - - channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = substream->runtime->channels; - rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - rate->min = rate->max = substream->runtime->rate; - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - substream->runtime->format); - dma_params = (struct skl_dma_params *) snd_soc_dai_get_dma_data(codec_dai, substream); @@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, return 0; } -static int skl_hda_be_startup(struct snd_pcm_substream *substream, +static int skl_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { return pm_runtime_get_sync(dai->dev); } -static void skl_hda_be_shutdown(struct snd_pcm_substream *substream, +static void skl_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { pm_runtime_mark_last_busy(dai->dev); @@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = { .prepare = skl_pcm_prepare, .hw_params = skl_pcm_hw_params, .hw_free = skl_pcm_hw_free, + .trigger = skl_pcm_trigger, }; static struct snd_soc_dai_ops skl_dmic_dai_ops = { - .startup = skl_hda_be_startup, - .shutdown = skl_hda_be_shutdown, + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, +}; + +static struct snd_soc_dai_ops skl_be_ssp_dai_ops = { + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_ops skl_link_dai_ops = { - .startup = skl_hda_be_startup, + .startup = skl_be_startup, .prepare = skl_link_pcm_prepare, .hw_params = skl_link_hw_params, .hw_free = skl_link_hw_free, .trigger = skl_link_pcm_trigger, - .shutdown = skl_hda_be_shutdown, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_driver skl_platform_dai[] = { @@ -488,6 +540,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, /* BE CPU Dais */ { + .name = "SSP0 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp0 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp0 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ .name = "iDisp Pin", .ops = &skl_link_dai_ops, .playback = { @@ -544,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream) return 0; } -static int skl_pcm_trigger(struct snd_pcm_substream *substream, +static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -618,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -static int skl_dsp_trigger(struct snd_pcm_substream *substream, +static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -675,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = get_bus_ctx(substream); if (ebus->ppcap) - return skl_dsp_trigger(substream, cmd); + return skl_decoupled_trigger(substream, cmd); else - return skl_pcm_trigger(substream, cmd); + return skl_coupled_trigger(substream, cmd); } /* calculate runtime delay from LPIB */ @@ -844,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) return retval; } +static int skl_platform_soc_probe(struct snd_soc_platform *platform) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); + + if (ebus->ppcap) + return skl_tplg_init(platform, ebus); + + return 0; +} static struct snd_soc_platform_driver skl_platform_drv = { + .probe = skl_platform_soc_probe, .ops = &skl_platform_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, @@ -857,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = { int skl_platform_register(struct device *dev) { int ret; + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct skl *skl = ebus_to_skl(ebus); + + INIT_LIST_HEAD(&skl->ppl_list); + INIT_LIST_HEAD(&skl->dapm_path_list); ret = snd_soc_register_platform(dev, &skl_platform_drv); if (ret) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 648bbbf..b1bffa5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1089,8 +1089,8 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, * FW expects like ids, resource values, formats etc */ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, - struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) { int ret; struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); @@ -1098,7 +1098,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_module_cfg *mconfig; struct skl_pipe *pipe; - struct skl_dfw_module *dfw_config = (struct skl_dfw_module *)tplg_w->priv.data; + struct skl_dfw_module *dfw_config = + (struct skl_dfw_module *)tplg_w->priv.data; if (!tplg_w->priv.size) goto bind_event; @@ -1121,12 +1122,14 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->in_fmt.channels = dfw_config->in_fmt.channels; mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; - mconfig->in_fmt.valid_bit_depth = dfw_config->in_fmt.valid_bit_depth; + mconfig->in_fmt.valid_bit_depth = + dfw_config->in_fmt.valid_bit_depth; mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; mconfig->out_fmt.channels = dfw_config->out_fmt.channels; mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; - mconfig->out_fmt.valid_bit_depth = dfw_config->out_fmt.valid_bit_depth; + mconfig->out_fmt.valid_bit_depth = + dfw_config->out_fmt.valid_bit_depth; mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; mconfig->params_fixup = dfw_config->params_fixup; mconfig->converter = dfw_config->converter; @@ -1142,15 +1145,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_in_pin), - GFP_KERNEL); + mconfig->m_in_pin = devm_kzalloc(bus->dev, + (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); if (!mconfig->m_in_pin) return -ENOMEM; - mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); + mconfig->m_out_pin = devm_kzalloc(bus->dev, + (mconfig->max_in_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; @@ -1163,13 +1168,13 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, goto bind_event; mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, - mconfig->formats_config.caps_size, GFP_KERNEL); + mconfig->formats_config.caps_size, GFP_KERNEL); if (mconfig->formats_config.caps == NULL) return -ENOMEM; memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, - dfw_config->caps.caps_size); + dfw_config->caps.caps_size); bind_event: if (tplg_w->event_type == 0) { @@ -1178,7 +1183,8 @@ bind_event: } ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, - ARRAY_SIZE(skl_tplg_widget_ops), tplg_w->event_type); + ARRAY_SIZE(skl_tplg_widget_ops), + tplg_w->event_type); if (ret) { dev_err(bus->dev, "%s: No matching event handlers found for %d\n", @@ -1209,7 +1215,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); if (ret < 0) { - dev_err(bus->dev, "config firmware %s request failed with %d\n", + dev_err(bus->dev, "tplg fw %s load failed with %d\n", "dfw_sst.bin", ret); return ret; } @@ -1218,7 +1224,8 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) * The complete tplg for SKL is loaded as index 0, we don't use * any other index */ - ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0); + ret = snd_soc_tplg_component_load(&platform->component, + &skl_tplg_ops, fw, 0); if (ret < 0) { dev_err(bus->dev, "tplg component load failed%d\n", ret); return -EINVAL; -- cgit v1.1 From 2a29b200c6f17827ee712e719ad29366f79205ee Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:58 +0100 Subject: ASoC: Intel: Skylake: Add DSP support and enable it If processing pipe capability is supported, add DSP support. Adds initialization/free/suspend/resume DSP functionality. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 348d094..2f1890e 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -166,11 +166,16 @@ static int skl_runtime_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + int ret; dev_dbg(bus->dev, "in %s\n", __func__); /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + ret = skl_suspend_dsp(skl); + if (ret < 0) + return ret; snd_hdac_bus_stop_chip(bus); snd_hdac_bus_enter_link_reset(bus); @@ -183,7 +188,7 @@ static int skl_runtime_resume(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *hda = ebus_to_skl(ebus); + struct skl *skl = ebus_to_skl(ebus); int status; dev_dbg(bus->dev, "in %s\n", __func__); @@ -191,12 +196,12 @@ static int skl_runtime_resume(struct device *dev) /* Read STATESTS before controller reset */ status = snd_hdac_chip_readw(bus, STATESTS); - skl_init_pci(hda); + skl_init_pci(skl); snd_hdac_bus_init_chip(bus, true); /* disable controller Wake Up event */ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); - return 0; + return skl_resume_dsp(skl); } #endif /* CONFIG_PM */ @@ -457,17 +462,19 @@ static int skl_probe(struct pci_dev *pci, /* check if dsp is there */ if (ebus->ppcap) { - /* TODO register with dsp IPC */ - dev_dbg(bus->dev, "Register dsp\n"); + err = skl_init_dsp(skl); + if (err < 0) { + dev_dbg(bus->dev, "error failed to register dsp\n"); + goto out_free; + } } - if (ebus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(ebus); /* create device for soc dmic */ err = skl_dmic_device_register(skl); if (err < 0) - goto out_free; + goto out_dsp_free; /* register platform dai and controls */ err = skl_platform_register(bus->dev); @@ -491,6 +498,8 @@ out_unregister: skl_platform_unregister(bus->dev); out_dmic_free: skl_dmic_device_unregister(skl); +out_dsp_free: + skl_free_dsp(skl); out_free: skl->init_failed = 1; skl_free(ebus); @@ -507,6 +516,7 @@ static void skl_remove(struct pci_dev *pci) pm_runtime_get_noresume(&pci->dev); pci_dev_put(pci); skl_platform_unregister(&pci->dev); + skl_free_dsp(skl); skl_dmic_device_unregister(skl); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); -- cgit v1.1 From 87b2bdf02278e5623cc66e5189792fde01f829a3 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:59 +0100 Subject: ASoC: Intel: Skylake: Initialize NHLT table Load and Initialize Non HDA Link Table in Skylake driver to get platform configuration. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2f1890e..ca135b8 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -458,6 +458,11 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + skl->nhlt = skl_nhlt_init(bus->dev); + + if (skl->nhlt == NULL) + goto out_free; + pci_set_drvdata(skl->pci, ebus); /* check if dsp is there */ -- cgit v1.1 From 3373f716830f36c0042f5845201caf1d9290f95b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 16:39:38 +0100 Subject: ASoC: Intel: Skylake: Modify the log level dev_info is too noisy for tplg wiget loading, so move it to debug level Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b1bffa5..a5d8420 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1178,7 +1178,7 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, bind_event: if (tplg_w->event_type == 0) { - dev_info(bus->dev, "ASoC: No event handler required\n"); + dev_dbg(bus->dev, "ASoC: No event handler required\n"); return 0; } -- cgit v1.1 From b3681308cc7162fb08249589b5bb1b2d42e00f4b Mon Sep 17 00:00:00 2001 From: "Fang, Yang A" Date: Wed, 7 Oct 2015 14:33:57 -0700 Subject: ASoC: nau8825: add acpi match ID This patch adds the acpi match ID for nau8825 codec Signed-off-by: Fang, Yang A Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index c44a7a1..5c1badf 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1090,10 +1091,19 @@ static const struct of_device_id nau8825_of_ids[] = { MODULE_DEVICE_TABLE(of, nau8825_of_ids); #endif +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8825_acpi_match[] = { + { "10508825", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match); +#endif + static struct i2c_driver nau8825_driver = { .driver = { .name = "nau8825", .of_match_table = of_match_ptr(nau8825_of_ids), + .acpi_match_table = ACPI_PTR(nau8825_acpi_match), }, .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, -- cgit v1.1 From f874b80e1571118fcf4554878633556f06f998e6 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Thu, 8 Oct 2015 15:31:13 +0200 Subject: ASoC: rockchip: Add rockchip SPDIF transceiver driver Add a driver for the SPDIF transceiver available on RK3066, RK3188 and RK3288. Heavily based on the rockchip i2s driver. Signed-off-by: Sjoerd Simons Signed-off-by: Mark Brown --- sound/soc/rockchip/Kconfig | 8 + sound/soc/rockchip/Makefile | 2 + sound/soc/rockchip/rockchip_spdif.c | 409 ++++++++++++++++++++++++++++++++++++ sound/soc/rockchip/rockchip_spdif.h | 63 ++++++ 4 files changed, 482 insertions(+) create mode 100644 sound/soc/rockchip/rockchip_spdif.c create mode 100644 sound/soc/rockchip/rockchip_spdif.h (limited to 'sound') diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 5709057..f1e0c70 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -15,6 +15,14 @@ config SND_SOC_ROCKCHIP_I2S Rockchip I2S device. The device supports upto maximum of 8 channels each for play and record. +config SND_SOC_ROCKCHIP_SPDIF + tristate "Rockchip SPDIF Device Driver" + depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for SPDIF driver for + Rockchip SPDIF transceiver device. + config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index e9ba558..c0bf560 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,7 +1,9 @@ # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-spdif-objs := rockchip_spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c new file mode 100644 index 0000000..9d5c470 --- /dev/null +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -0,0 +1,409 @@ +/* sound/soc/rockchip/rk_spdif.c + * + * ALSA SoC Audio Layer - Rockchip I2S Controller driver + * + * Copyright (c) 2014 Rockchip Electronics Co. Ltd. + * Author: Jianqun + * Copyright (c) 2015 Collabora Ltd. + * Author: Sjoerd Simons + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rockchip_spdif.h" + +enum rk_spdif_type { + RK_SPDIF_RK3066, + RK_SPDIF_RK3188, + RK_SPDIF_RK3288, +}; + +#define RK3288_GRF_SOC_CON2 0x24c + +struct rk_spdif_dev { + struct device *dev; + + struct clk *mclk; + struct clk *hclk; + + struct snd_dmaengine_dai_dma_data playback_dma_data; + + struct regmap *regmap; +}; + +static const struct of_device_id rk_spdif_match[] = { + { .compatible = "rockchip,rk3066-spdif", + .data = (void *) RK_SPDIF_RK3066 }, + { .compatible = "rockchip,rk3188-spdif", + .data = (void *) RK_SPDIF_RK3188 }, + { .compatible = "rockchip,rk3288-spdif", + .data = (void *) RK_SPDIF_RK3288 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk_spdif_match); + +static int rk_spdif_runtime_suspend(struct device *dev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + + return 0; +} + +static int rk_spdif_runtime_resume(struct device *dev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->mclk); + if (ret) { + dev_err(spdif->dev, "mclk clock enable failed %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(spdif->hclk); + if (ret) { + dev_err(spdif->dev, "hclk clock enable failed %d\n", ret); + return ret; + } + + return 0; +} + +static int rk_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE; + int srate, mclk; + int ret; + + srate = params_rate(params); + switch (srate) { + case 32000: + case 48000: + case 96000: + mclk = 96000 * 128; /* 12288000 hz */ + break; + case 44100: + mclk = 44100 * 256; /* 11289600 hz */ + break; + case 192000: + mclk = 192000 * 128; /* 24576000 hz */ + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val |= SPDIF_CFGR_VDW_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val |= SPDIF_CFGR_VDW_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val |= SPDIF_CFGR_VDW_24; + break; + default: + return -EINVAL; + } + + /* Set clock and calculate divider */ + ret = clk_set_rate(spdif->mclk, mclk); + if (ret != 0) { + dev_err(spdif->dev, "Failed to set module clock rate: %d\n", + ret); + return ret; + } + + val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256)); + ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, + SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE | + SDPIF_CFGR_VDW_MASK, + val); + + return ret; +} + +static int rk_spdif_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, + SPDIF_DMACR_TDE_ENABLE, + SPDIF_DMACR_TDE_ENABLE); + + if (ret != 0) + return ret; + + ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, + SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_START); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, + SPDIF_DMACR_TDE_ENABLE, + SPDIF_DMACR_TDE_DISABLE); + + if (ret != 0) + return ret; + + ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, + SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_STOP); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rk_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &spdif->playback_dma_data; + + return 0; +} + +static const struct snd_soc_dai_ops rk_spdif_dai_ops = { + .hw_params = rk_spdif_hw_params, + .trigger = rk_spdif_trigger, +}; + +static struct snd_soc_dai_driver rk_spdif_dai = { + .probe = rk_spdif_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &rk_spdif_dai_ops, +}; + +static const struct snd_soc_component_driver rk_spdif_component = { + .name = "rockchip-spdif", +}; + +static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_CFGR: + case SPDIF_DMACR: + case SPDIF_INTCR: + case SPDIF_XFER: + case SPDIF_SMPDR: + return true; + default: + return false; + } +} + +static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_CFGR: + case SPDIF_SDBLR: + case SPDIF_INTCR: + case SPDIF_INTSR: + case SPDIF_XFER: + return true; + default: + return false; + } +} + +static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_INTSR: + case SPDIF_SDBLR: + return true; + default: + return false; + } +} + +static const struct regmap_config rk_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SPDIF_SMPDR, + .writeable_reg = rk_spdif_wr_reg, + .readable_reg = rk_spdif_rd_reg, + .volatile_reg = rk_spdif_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int rk_spdif_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct rk_spdif_dev *spdif; + const struct of_device_id *match; + struct resource *res; + void __iomem *regs; + int ret; + + match = of_match_node(rk_spdif_match, np); + if ((int) match->data == RK_SPDIF_RK3288) { + struct regmap *grf; + + grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, + "rockchip_spdif missing 'rockchip,grf' \n"); + return PTR_ERR(grf); + } + + /* Select the 8 channel SPDIF solution on RK3288 as + * the 2 channel one does not appear to work + */ + regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16); + } + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + spdif->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(spdif->hclk)) { + dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n"); + return PTR_ERR(spdif->hclk); + } + ret = clk_prepare_enable(spdif->hclk); + if (ret) { + dev_err(spdif->dev, "hclock enable failed %d\n", ret); + return ret; + } + + spdif->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(spdif->mclk)) { + dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n"); + return PTR_ERR(spdif->mclk); + } + + ret = clk_prepare_enable(spdif->mclk); + if (ret) { + dev_err(spdif->dev, "clock enable failed %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs, + &rk_spdif_regmap_config); + if (IS_ERR(spdif->regmap)) { + dev_err(&pdev->dev, + "Failed to initialise managed register map\n"); + return PTR_ERR(spdif->regmap); + } + + spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; + spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + spdif->playback_dma_data.maxburst = 4; + + spdif->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, spdif); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_request_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &rk_spdif_component, + &rk_spdif_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI\n"); + goto err_pm_runtime; + } + + ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM\n"); + goto err_pcm_register; + } + + return 0; + +err_pcm_register: + snd_dmaengine_pcm_unregister(&pdev->dev); +err_pm_runtime: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int rk_spdif_remove(struct platform_device *pdev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + rk_spdif_runtime_suspend(&pdev->dev); + + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + snd_dmaengine_pcm_unregister(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops rk_spdif_pm_ops = { + SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, + NULL) +}; + +static struct platform_driver rk_spdif_driver = { + .probe = rk_spdif_probe, + .remove = rk_spdif_remove, + .driver = { + .name = "rockchip-spdif", + .of_match_table = of_match_ptr(rk_spdif_match), + .pm = &rk_spdif_pm_ops, + }, +}; +module_platform_driver(rk_spdif_driver); + +MODULE_ALIAS("platform:rockchip-spdif"); +MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface"); +MODULE_AUTHOR("Sjoerd Simons "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h new file mode 100644 index 0000000..07f86a2 --- /dev/null +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -0,0 +1,63 @@ +/* + * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver + * + * Copyright (c) 2015 Collabora Ltd. + * Author: Sjoerd Simons + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ROCKCHIP_SPDIF_H +#define _ROCKCHIP_SPDIF_H + +/* + * CFGR + * transfer configuration register +*/ +#define SPDIF_CFGR_CLK_DIV_SHIFT (16) +#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT) +#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT) + +#define SPDIF_CFGR_HALFWORD_SHIFT 2 +#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT) +#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT) + +#define SPDIF_CFGR_VDW_SHIFT 0 +#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT) +#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT) + +#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x00) +#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x01) +#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x10) + +/* + * DMACR + * DMA control register +*/ +#define SPDIF_DMACR_TDE_SHIFT 5 +#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT) +#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT) + +#define SPDIF_DMACR_TDL_SHIFT 0 +#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT) +#define SPDIF_DMACR_TDL_MASK (0x1f << SDPIF_DMACR_TDL_SHIFT) + +/* + * XFER + * Transfer control register +*/ +#define SPDIF_XFER_TXS_SHIFT 0 +#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT) +#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT) + +#define SPDIF_CFGR (0x0000) +#define SPDIF_SDBLR (0x0004) +#define SPDIF_DMACR (0x0008) +#define SPDIF_INTCR (0x000c) +#define SPDIF_INTSR (0x0010) +#define SPDIF_XFER (0x0018) +#define SPDIF_SMPDR (0x0020) + +#endif /* _ROCKCHIP_SPDIF_H */ -- cgit v1.1 From dc542fb4179a104fc73760a2da0fd78f68f70d6d Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 7 Oct 2015 17:52:12 +0530 Subject: ASoC: rt5645: fix build warning We were getting build warning about "Section mismatch". dmi_platform_intel_broadwell is being referenced from the probe function rt5645_i2c_probe(), but dmi_platform_intel_broadwell was marked with __initdata. Signed-off-by: Sudip Mukherjee Reviewed-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index cd1a4ec..d9993d4 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3241,7 +3241,7 @@ static int buddy_quirk_cb(const struct dmi_system_id *id) return 1; } -static struct dmi_system_id dmi_platform_intel_broadwell[] __initdata = { +static struct dmi_system_id dmi_platform_intel_broadwell[] = { { .ident = "Chrome Buddy", .callback = buddy_quirk_cb, -- cgit v1.1 From 6ea8ba33e65d3d284de7e7373939352e2c728f10 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:48 +0100 Subject: ASoC: Intel: Skylake: Check CPA bit in DSP core power down Skylake driver will set the SPA bit to 0 to turn off the DSP core. Driver will poll the Current Power Active (CPA) bit to match the Set Power Active (SPA) bit value. When CPA bit matches the value of SPA bit, the achieved power state has reached. In case of DSP power down, register that was polled is SPA instead of CPA. This patch corrects the register to be polled in case of DSP power down. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 94875b0..194bd00 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx) /* poll with timeout to check if operation successful */ return sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_SPA_MASK, + SKL_ADSPCS_CPA_MASK, 0, SKL_DSP_PD_TO, "Power down"); -- cgit v1.1 From def656fe22abb4fbf174a982dcef1d40274ddb11 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:49 +0100 Subject: ASoC: Intel: Skylake: Verify the status bit before handling interrupt Like we have in legacy mode HDA driver, we need to check the status bit and handle interrupt only when it is not zero or all bits set. We typically see the status as all 1's when controller resumes from suspend, So add the check here as well and don't handle for these cases. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 194bd00..1bfb7f6 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); ctx->intr_status = val; + if (val == 0xffffffff) { + spin_unlock(&ctx->spinlock); + return IRQ_NONE; + } + if (val & SKL_ADSPIS_IPC) { skl_ipc_int_disable(ctx); result = IRQ_WAKE_THREAD; -- cgit v1.1 From 84c9e2836aa7c87b19a24de091c7e7cf16124645 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:50 +0100 Subject: ASoC: Intel: Skylake: Correct the runtime handler behaviour On runtime pm resume, we need to download the firmware, also on suspend we need to ensure all the interrupts from controller and DSP are disabled. Also since we download the firmware on resume, we don't need to do so on init, so remove that bit Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 12 ++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 1 + sound/soc/intel/skylake/skl-sst.c | 56 +++++++++++++++++++---------------- 4 files changed, 44 insertions(+), 26 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index cbd568e..2151652 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -314,6 +314,7 @@ struct sst_dsp { int sst_state; struct skl_cl_dev cl_dev; u32 intr_status; + const struct firmware *fw; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 937a0a3..3345ea0 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx) SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY); } +void skl_ipc_op_int_disable(struct sst_dsp *ctx) +{ + /* disable IPC DONE interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, 0); + + /* Disable IPC BUSY interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_BUSY, 0); + +} + bool skl_ipc_int_status(struct sst_dsp *ctx) { return sst_dsp_shim_read_unlocked(ctx, diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 9f5f672..f1a154e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); +void skl_ipc_op_int_disable(struct sst_dsp *ctx); void skl_ipc_int_disable(struct sst_dsp *dsp); bool skl_ipc_int_status(struct sst_dsp *dsp); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index c18ea51..3b83dc9 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx, static int skl_load_base_firmware(struct sst_dsp *ctx) { int ret = 0, i; - const struct firmware *fw = NULL; struct skl_sst *skl = ctx->thread_context; u32 reg; - ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev); + skl->boot_complete = false; + init_waitqueue_head(&skl->boot_wait); + + if (ctx->fw == NULL) { + ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + skl_dsp_disable_core(ctx); + return -EIO; + } + } + + ret = skl_dsp_boot(ctx); if (ret < 0) { - dev_err(ctx->dev, "Request firmware failed %d\n", ret); - skl_dsp_disable_core(ctx); - return -EIO; + dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); + goto skl_load_base_firmware_failed; + } + + ret = skl_cldma_prepare(ctx); + if (ret < 0) { + dev_err(ctx->dev, "CL dma prepare failed : %d", ret); + goto skl_load_base_firmware_failed; } /* enable Interrupt */ @@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto skl_load_base_firmware_failed; } - ret = skl_transfer_firmware(ctx, fw->data, fw->size); + ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); if (ret < 0) { dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); goto skl_load_base_firmware_failed; @@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); } - release_firmware(fw); - return 0; skl_load_base_firmware_failed: skl_dsp_disable_core(ctx); - release_firmware(fw); + release_firmware(ctx->fw); + ctx->fw = NULL; return ret; } @@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx) } skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + /* disable Interrupt */ + ctx->cl_dev.ops.cl_cleanup_controller(ctx); + skl_cldma_int_disable(ctx); + skl_ipc_op_int_disable(ctx); + skl_ipc_int_disable(ctx); + return ret; } @@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret; - skl->boot_complete = false; - init_waitqueue_head(&skl->boot_wait); - - ret = skl_dsp_boot(sst); - if (ret < 0) { - dev_err(skl->dev, "Boot dsp core failed ret: %d", ret); - goto free_ipc; - } - - ret = skl_cldma_prepare(sst); - if (ret < 0) { - dev_err(dev, "CL dma prepare failed : %d", ret); - goto free_ipc; - } - - ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed : %d", ret); @@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return 0; -free_ipc: skl_ipc_free(&skl->ipc); return ret; } -- cgit v1.1 From 01bb84b5001e4a6221582b163b34e90b3095f451 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:51 +0100 Subject: ASoC: Intel: Skylake: power down all link in suspend This ensures that the link is not requesting any clock and the PLL can turn off. The link is powered when controller is brought out of reset. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ca135b8..5319529 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -173,6 +173,9 @@ static int skl_runtime_suspend(struct device *dev) /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + + snd_hdac_ext_bus_link_power_down_all(ebus); + ret = skl_suspend_dsp(skl); if (ret < 0) return ret; -- cgit v1.1 From 4272975a3444f0abde1354285c5720d7819b41ed Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sat, 10 Oct 2015 14:17:58 +0900 Subject: ASoC: sh: Fit typo in Kconfig s/SUR/SRU/g Signed-off-by: Masanari Iida Signed-off-by: Mark Brown --- sound/soc/sh/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 07114b0..de668b2 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -40,7 +40,7 @@ config SND_SOC_RCAR select SND_SIMPLE_CARD select REGMAP_MMIO help - This option enables R-Car SUR/SCU/SSIU/SSI sound support + This option enables R-Car SRU/SCU/SSIU/SSI sound support config SND_SOC_RSRC_CARD tristate "Renesas Sampling Rate Convert Sound Card" -- cgit v1.1 From a5fe58fd2836987387a6ee8854c529db7f5be650 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 12 Oct 2015 21:34:59 +0800 Subject: ASoC: rt298: set register non-volatile by default It is not necessary to set registers volatile. So, return false for default case of rt298_volatile_register. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 3c2f0f8..ff126a7 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -129,7 +129,7 @@ static bool rt298_volatile_register(struct device *dev, unsigned int reg) case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0): return true; default: - return true; + return false; } -- cgit v1.1 From 42f2bb1c494543084b764e1ca253c73db910daf2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 13 Oct 2015 14:57:49 +0530 Subject: ALSA: hdac: Explicitly add io.h Compiling the hdac extended core on arm fails with below error: sound/hda/ext/hdac_ext_bus.c: In function 'hdac_ext_writel': >> sound/hda/ext/hdac_ext_bus.c:29:2: error: implicit declaration of >> function +'writel' [-Werror=implicit-function-declaration] writel(value, addr); ^ sound/hda/ext/hdac_ext_bus.c: In function 'hdac_ext_readl': >> sound/hda/ext/hdac_ext_bus.c:34:2: error: implicit declaration of >> function +'readl' [-Werror=implicit-function-declaration] return readl(addr); This is fixed by explicitly including io.h Fixes: 99463b3a3994 - ('ALSA: hda: provide default bus io ops extended hdac') Reported-by: kbuild test robot Suggested-by: Mark Brown Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/hda/ext/hdac_ext_bus.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 4449d1a9..2433f7c 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -19,6 +19,7 @@ #include #include +#include #include MODULE_DESCRIPTION("HDA extended core"); -- cgit v1.1 From e8d65a8d985271a102f07c7456da5b86c19ffe16 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 13 Oct 2015 10:10:18 +0200 Subject: ALSA: hda - Fix inverted internal mic on Lenovo G50-80 Add the appropriate quirk to indicate the Lenovo G50-80 has a stereo mic input where one channel has reverse polarity. Alsa-info available at: https://launchpadlibrarian.net/220846272/AlsaInfo.txt Cc: stable@vger.kernel.org BugLink: https://bugs.launchpad.net/bugs/1504778 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ca03c40..2f0ec7c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -819,6 +819,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD), + SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), -- cgit v1.1 From 4362495734d155e10174ace9066827780edaed0d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 14 Oct 2015 08:56:07 +0000 Subject: ASoC: rsnd: Announce for removing Gen1 SRU support Gen1 SRU support was created for preparation of Gen2 SRC support, but no-one is using this feature (sampling rate convert) on Gen1. BockW had used SRU before, but it was pass through mode. This means it is same as SSI. And BockW "platform base" code was removed from upstream code. It is now supported via DT, but it doesn't use SRU. More detail, r8a7778.dtsi has "rcar_sound,src" entry, but no-one is using this feature today. SRU probing has no relation to this removing. This means there is no effect for DT compatibility, no issues on upstream kernel. Gen2 SRC was created from Gen1 SRU, these are similar but not same IP. Keeping Gen1 SRU in current driver is a little bit difficult, and no-one is using it today. Gen1 sound is still supported via SSI. Gen1 SRU support will be removed in the next kernel version. This patch announces it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index ca7a20f..1d379e8 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -1036,8 +1036,10 @@ int rsnd_src_probe(struct platform_device *pdev, int i, nr, ret; ops = NULL; - if (rsnd_is_gen1(priv)) + if (rsnd_is_gen1(priv)) { ops = &rsnd_src_gen1_ops; + dev_warn(dev, "Gen1 support will be removed soon\n"); + } if (rsnd_is_gen2(priv)) ops = &rsnd_src_gen2_ops; if (!ops) { -- cgit v1.1 From 7ba6e4ef76c7e43101bd5e0f8987c11a8ed0d325 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 16 Oct 2015 15:21:32 +0800 Subject: ASoC: rt298: correct index default value Some of the default value on rt298_index_def are incorrect. Change them to the correct value. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 3c2f0f8..d3e30a6 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -50,24 +50,24 @@ struct rt298_priv { }; static struct reg_default rt298_index_def[] = { - { 0x01, 0xaaaa }, - { 0x02, 0x8aaa }, + { 0x01, 0xa5a8 }, + { 0x02, 0x8e95 }, { 0x03, 0x0002 }, - { 0x04, 0xaf01 }, - { 0x08, 0x000d }, - { 0x09, 0xd810 }, - { 0x0a, 0x0120 }, + { 0x04, 0xaf67 }, + { 0x08, 0x200f }, + { 0x09, 0xd010 }, + { 0x0a, 0x0100 }, { 0x0b, 0x0000 }, { 0x0d, 0x2800 }, - { 0x0f, 0x0000 }, - { 0x19, 0x0a17 }, + { 0x0f, 0x0022 }, + { 0x19, 0x0217 }, { 0x20, 0x0020 }, { 0x33, 0x0208 }, { 0x46, 0x0300 }, - { 0x49, 0x0004 }, - { 0x4f, 0x50e9 }, - { 0x50, 0x2000 }, - { 0x63, 0x2902 }, + { 0x49, 0x4004 }, + { 0x4f, 0x50c9 }, + { 0x50, 0x3000 }, + { 0x63, 0x1b02 }, { 0x67, 0x1111 }, { 0x68, 0x1016 }, { 0x69, 0x273f }, -- cgit v1.1 From 8a98b4223d0eab88bba5eb215b275da4ff6ed99c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 15 Oct 2015 03:25:28 +0000 Subject: ASoC: rsnd: Gen1 probe is not error Probing from Gen1 is not error. This patch fixup it Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 6 ++---- sound/soc/sh/rcar/dvc.c | 6 ++---- sound/soc/sh/rcar/mix.c | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index a3e7c71..f1541f46 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -118,10 +118,8 @@ int rsnd_ctu_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "CTU is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_ctu(pdev, of_data, priv); diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 8d8eee6..e36c0ac 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -333,10 +333,8 @@ int rsnd_dvc_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "CMD is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_dvc(pdev, of_data, priv); diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 8544403..ac2687d 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -151,10 +151,8 @@ int rsnd_mix_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "MIX is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_mix(pdev, of_data, priv); -- cgit v1.1 From 34198710f55b5f359f43e67d9a08fe5aadfbca1b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 14 Oct 2015 13:31:24 +0100 Subject: ASoC: Add info callback for SX_TLV controls SX_TLV controls are intended for situations where the register behind the control has some non-zero value indicating the minimum gain and then gains increasing from there and eventually overflowing through zero. Currently every CODEC implementing these controls specifies the minimum as the non-zero value for the minimum and the maximum as the number of gain settings available. This means when the info callback subtracts the minimum value from the maximum value to calculate the number of gain levels available it is actually under reporting the available levels. This patch fixes this issue by adding a new snd_soc_info_volsw_sx callback that does not subtract the minimum value. Fixes: 1d99f2436d0d ("ASoC: core: Rework SOC_DOUBLE_R_SX_TLV add SOC_SINGLE_SX_TLV") Signed-off-by: Charles Keepax Acked-by: Brian Austin Tested-by: Brian Austin Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/soc-ops.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 100d92b..05977ae 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -207,6 +207,34 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_info_volsw); /** + * snd_soc_info_volsw_sx - Mixer info callback for SX TLV controls + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a single mixer control, or a double + * mixer control that spans 2 registers of the SX TLV type. SX TLV controls + * have a range that represents both positive and negative values either side + * of zero but without a sign bit. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + snd_soc_info_volsw(kcontrol, uinfo); + /* Max represents the number of levels in an SX control not the + * maximum value, so add the minimum value back on + */ + uinfo->value.integer.max += mc->min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_sx); + +/** * snd_soc_get_volsw - single mixer get callback * @kcontrol: mixer control * @ucontrol: control element information -- cgit v1.1 From 3943b9efb381531b99bf9c545736f9e1e2715b9c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 5 Oct 2015 21:23:48 +0800 Subject: ASoC: rt298: Make rt298_index_def const The index_cache is per instance run time state but rt298_index_def is not. Make rt298_index_def const and make a copy of memory for index_cache rather than directly use the rt298_index_def. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index ff126a7..d039c99 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -49,7 +49,7 @@ struct rt298_priv { int is_hp_in; }; -static struct reg_default rt298_index_def[] = { +static const struct reg_default rt298_index_def[] = { { 0x01, 0xaaaa }, { 0x02, 0x8aaa }, { 0x03, 0x0002 }, @@ -1165,7 +1165,11 @@ static int rt298_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt298->index_cache = rt298_index_def; + rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def, + sizeof(rt298_index_def), GFP_KERNEL); + if (!rt298->index_cache) + return -ENOMEM; + rt298->index_cache_size = INDEX_CACHE_SIZE; rt298->i2c = i2c; i2c_set_clientdata(i2c, rt298); -- cgit v1.1 From c962d03be31f12fd8eea435fa59e5289ce0cc284 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 13 Oct 2015 15:07:25 +0800 Subject: ASoC: rt5645: Recheck the jack detect status after resuming from S3 The patch rechecks the jack detect status after resuming from S3. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index d9993d4..b3eff73 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2745,6 +2745,10 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_FV1 | RT5645_PWR_FV2, RT5645_PWR_FV1 | RT5645_PWR_FV2); + if (rt5645->en_button_func && + snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, msecs_to_jiffies(0)); break; case SND_SOC_BIAS_OFF: -- cgit v1.1 From 2057020db384cb971cfd51296426f447e6d66b64 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 13 Oct 2015 10:37:10 +0800 Subject: ASoC: rockchip: spdif: Convert to use devm_snd_dmaengine_pcm_register Use resource managed API then we can remove snd_dmaengine_pcm_unregister() and snd_soc_unregister_component() calls in .probe error path and .remove. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 9d5c470..a38a302 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -355,16 +355,14 @@ static int rk_spdif_probe(struct platform_device *pdev) goto err_pm_runtime; } - ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); - goto err_pcm_register; + goto err_pm_runtime; } return 0; -err_pcm_register: - snd_dmaengine_pcm_unregister(&pdev->dev); err_pm_runtime: pm_runtime_disable(&pdev->dev); @@ -381,8 +379,6 @@ static int rk_spdif_remove(struct platform_device *pdev) clk_disable_unprepare(spdif->mclk); clk_disable_unprepare(spdif->hclk); - snd_dmaengine_pcm_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); return 0; } -- cgit v1.1 From 624729fd51871bfbddb647764f180126789a29ee Mon Sep 17 00:00:00 2001 From: Omair M Abdullah Date: Wed, 14 Oct 2015 21:11:43 +0530 Subject: ASoC: Intel: Skylake - Add Skylake RT286 I2S machine driver Add the SKL I2S machine driver using Realtek ALC286S codec in I2S mode. Signed-off-by: Omair M Abdullah Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 13 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/skl_rt286.c | 266 +++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 sound/soc/intel/boards/skl_rt286.c (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 664df1f..6d67357 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -140,3 +140,16 @@ config SND_SOC_INTEL_SKYLAKE select SND_HDA_EXT_CORE select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST + +config SND_SOC_INTEL_SKL_RT286_MACH + tristate "ASoC Audio driver for SKL with RT286 I2S mode" + depends on X86 && ACPI + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT286 + select SND_SOC_DMIC + help + This adds support for ASoC machine driver for Skylake platforms + with RT286 I2S audio codec. + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index cb94895..371c456 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o +snd-soc-skl_rt286-objs := skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c new file mode 100644 index 0000000..488c98e --- /dev/null +++ b/sound/soc/intel/boards/skl_rt286.c @@ -0,0 +1,266 @@ +/* + * Intel Skylake I2S Machine Driver + * + * Copyright (C) 2014-2015, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Broadwell Wildcatpoint SST Audio + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/rt286.h" + +static struct snd_soc_jack skylake_headset; +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin skylake_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new skylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget skylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route skylake_rt286_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack deteck */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMIC AIF", NULL, "SoC DMIC"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + { "ssp0 Tx", NULL, "codec1_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "codec1_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "Capture" }, + + { "hif1", NULL, "iDisp Tx"}, + { "iDisp Tx", NULL, "iDisp_out"}, + +}; + +static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &skylake_headset, + skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins)); + + if (ret) + return ret; + + rt286_mic_detect(codec, &skylake_headset); + + return 0; +} + + +static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The output is 48KHz, stereo, 16bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static int skylake_rt286_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops skylake_rt286_ops = { + .hw_params = skylake_rt286_hw_params, +}; + +/* skylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link skylake_rt286_dais[] = { + /* Front End DAI links */ + { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + }, + { + .name = "Skl Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .be_id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt286-aif1", + .init = skylake_rt286_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp0_fixup, + .ops = &skylake_rt286_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .be_id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, +}; + +/* skylake audio machine driver for SPT + RT286S */ +static struct snd_soc_card skylake_rt286 = { + .name = "skylake-rt286", + .owner = THIS_MODULE, + .dai_link = skylake_rt286_dais, + .num_links = ARRAY_SIZE(skylake_rt286_dais), + .controls = skylake_controls, + .num_controls = ARRAY_SIZE(skylake_controls), + .dapm_widgets = skylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(skylake_widgets), + .dapm_routes = skylake_rt286_map, + .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map), + .fully_routed = true, +}; + +static int skylake_audio_probe(struct platform_device *pdev) +{ + skylake_rt286.dev = &pdev->dev; + + return snd_soc_register_card(&skylake_rt286); +} + +static int skylake_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&skylake_rt286); + return 0; +} + +static struct platform_driver skylake_audio = { + .probe = skylake_audio_probe, + .remove = skylake_audio_remove, + .driver = { + .name = "skl_alc286s_i2s", + }, +}; + +module_platform_driver(skylake_audio) + +/* Module information */ +MODULE_AUTHOR("Omair Mohammed Abdullah "); +MODULE_DESCRIPTION("Intel SST Audio for Skylake"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_alc286s_i2s"); -- cgit v1.1 From 355b27e181f667dad248a40d3d4d48150e9a593c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 18 Oct 2015 10:06:11 +0800 Subject: ASoC: ad193x-spi: Add adau1328 to ad193x_spi_id table This driver also supports adau1328, thus add adau1328 to ad193x_spi_id. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x-spi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c index b55055a..431f95d 100644 --- a/sound/soc/codecs/ad193x-spi.c +++ b/sound/soc/codecs/ad193x-spi.c @@ -41,6 +41,7 @@ static const struct spi_device_id ad193x_spi_id[] = { { "ad1934", AD1934 }, { "ad1938", AD193X }, { "ad1939", AD193X }, + { "adau1328", AD193X }, { } }; MODULE_DEVICE_TABLE(spi, ad193x_spi_id); -- cgit v1.1 From d289619a219dd01e255d7b5e30f9171b25efea48 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 20 Oct 2015 16:23:55 +0200 Subject: ALSA: hda - Fix deadlock at error in building PCM The HDA codec driver issues snd_hda_codec_reset() at the error path of PCM build. This was needed in the earlier code base, but the recent rewrite to use the standard bus binding made this a deadlock: modprobe D 0000000000000005 0 720 716 0x00000080 Call Trace: [] schedule+0x3e/0x90 [] schedule_preempt_disabled+0x15/0x20 [] __mutex_lock_slowpath+0xb5/0x120 [] mutex_lock+0x1b/0x30 [] device_release_driver+0x1b/0x30 [] bus_remove_device+0x105/0x180 [] device_del+0x139/0x260 [] snd_hdac_device_unregister+0x25/0x30 [snd_hda_core] [] snd_hda_codec_reset+0x2a/0x70 [snd_hda_codec] [] snd_hda_codec_build_pcms+0x18b/0x1b0 [snd_hda_codec] [] hda_codec_driver_probe+0xbe/0x140 [snd_hda_codec] [] driver_probe_device+0x1f4/0x460 [] __driver_attach+0x90/0xa0 [] bus_for_each_dev+0x64/0xa0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x1eb/0x280 [] driver_register+0x60/0xe0 [] __hda_codec_driver_register+0x5a/0x60 [snd_hda_codec] [] realtek_driver_init+0x1e/0x1000 [snd_hda_codec_realtek] [] do_one_initcall+0xb3/0x200 [] do_init_module+0x60/0x1f8 [] load_module+0x1653/0x1bd0 [] SYSC_finit_module+0x98/0xc0 [] SyS_finit_module+0xe/0x10 [] entry_SYSCALL_64_fastpath+0x16/0x75 The simple fix is just to remove this call, since we don't need to think about unbinding at there any longer. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=948758 Cc: # v4.1+ Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 37f43a1..a249d54 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3367,10 +3367,8 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) int dev, err; err = snd_hda_codec_parse_pcms(codec); - if (err < 0) { - snd_hda_codec_reset(codec); + if (err < 0) return err; - } /* attach a new PCM streams */ list_for_each_entry(cpcm, &codec->pcm_list_head, list) { -- cgit v1.1 From 92b822a499607c32ce12ffd798a095babedc3202 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Oct 2015 16:56:11 +0800 Subject: ASoC: ad193x: Drop .volatile_reg implementation adau193x_reg_volatile() always return false. This seems pointless because current code uses REGCACHE_NONE cache_type which is supposed to be volatile. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 76d7966..3a3f3f2 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -418,14 +418,8 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = { .num_dapm_routes = ARRAY_SIZE(audio_paths), }; -static bool adau193x_reg_volatile(struct device *dev, unsigned int reg) -{ - return false; -} - const struct regmap_config ad193x_regmap_config = { .max_register = AD193X_NUM_REGS - 1, - .volatile_reg = adau193x_reg_volatile, }; EXPORT_SYMBOL_GPL(ad193x_regmap_config); -- cgit v1.1 From ce7b8dbdded8552d1fd25cc9289da361c5cc765e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Oct 2015 16:54:46 +0800 Subject: ASoC: ssm2518: Drop .volatile_reg implementation The implementation of ssm2518_register_volatile always returns false, this behavior is the same as no .volatile_reg callback implementation when cache_type != REGCACHE_NONE. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ssm2518.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index ddb0203..86b81a6 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -723,17 +723,11 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = { .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), }; -static bool ssm2518_register_volatile(struct device *dev, unsigned int reg) -{ - return false; -} - static const struct regmap_config ssm2518_regmap_config = { .val_bits = 8, .reg_bits = 8, .max_register = SSM2518_REG_DRC_9, - .volatile_reg = ssm2518_register_volatile, .cache_type = REGCACHE_RBTREE, .reg_defaults = ssm2518_reg_defaults, -- cgit v1.1 From f8f2dc4a7127725383c93b501fcc4e47871b0a9d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 21 Oct 2015 16:18:18 +0800 Subject: ASoC: rt298: fix wrong setting of gpio2_en The register value to enable gpio2 was incorrect. So fix it. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index d3e30a6..f823eb5 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1214,7 +1214,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c, mdelay(10); if (!rt298->pdata.gpio2_en) - regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x4000); + regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x40); else regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0); -- cgit v1.1 From c5cff89b5f64b4fc7f6fd9be44b1aa7e766a94e4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 21 Oct 2015 08:34:37 +0800 Subject: ASoC: da7219: Fix da7219->alc_en state when enabling ALC Signed-off-by: Axel Lin Tested-by: Adam Thomson Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/da7219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index abba4b3..f238c1e 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -402,7 +402,7 @@ static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol, /* Force ALC offset calibration if enabling ALC */ if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) { da7219_alc_calib(codec); - da7219->alc_en = false; + da7219->alc_en = true; } else { da7219->alc_en = false; } -- cgit v1.1 From c9b9638f617871aa83c197eed8f068294c843b69 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:13:06 +0000 Subject: ASoC: rsnd: fixup print debug message after read debug meesage for rsnd_mod_read() should be prints after read Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/gen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index f04d17b..0c69f83 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -79,11 +79,11 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + dev_dbg(dev, "r %s[%d] - %4d : %08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); - regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); - return val; } -- cgit v1.1 From 9993c16d460e2965da4357575060373a5577167a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:13:27 +0000 Subject: ASoC: rsnd: fixup struct rsnd_gen::res array size struct rsnd_gen :: res array size should be RSND_BASE_MAX, not RSND_REG_MAX. This patch fixup it, and indicates whether each data array size is based on what Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/gen.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 0c69f83..76da762 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -22,13 +22,15 @@ #include "rsnd.h" struct rsnd_gen { - void __iomem *base[RSND_BASE_MAX]; - struct rsnd_gen_ops *ops; + /* RSND_BASE_MAX base */ + void __iomem *base[RSND_BASE_MAX]; + phys_addr_t res[RSND_BASE_MAX]; struct regmap *regmap[RSND_BASE_MAX]; + + /* RSND_REG_MAX base */ struct regmap_field *regs[RSND_REG_MAX]; - phys_addr_t res[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -182,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regmap)) return PTR_ERR(regmap); + /* RSND_BASE_MAX base */ gen->base[reg_id] = base; gen->regmap[reg_id] = regmap; gen->res[reg_id] = res->start; @@ -198,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regs)) return PTR_ERR(regs); + /* RSND_REG_MAX base */ gen->regs[conf[i].idx] = regs; } -- cgit v1.1 From b05ce4c0916dec0e31a12c35a3386e3ca3ed989a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:13:44 +0000 Subject: ASoC: rsnd: fixup devm_request_irq() option on ssi.c bfc0cfe("ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_ssi_xxx()") tidyuped devm_request_irq() option from ssi to mod, but devm_free_irq() on rsnd_ssi_dma_remove() didn't modified. This patch fixups this issue. Otherwise kernel will output WARNING message. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 842a35b..40e2b5d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -555,7 +555,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ - devm_free_irq(dev, irq, ssi); + devm_free_irq(dev, irq, mod); return 0; } -- cgit v1.1 From 1355720a3b3eba5604431d89d5cf69ce4ad51311 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:14:02 +0000 Subject: ASoC: rsnd: fixup rsnd_mod_call() behavior for debug Indicating each module method as debug message before executing is readable/understandable. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6ef9a88..c0182ac 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -308,16 +308,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - int called = 0; \ - if (val == __rsnd_mod_call_##func) { \ - called = 1; \ - ret = (mod)->ops->func(mod, io, param); \ - } \ + int call = (val == __rsnd_mod_call_##func); \ mod->status = (mod->status & ~mask) + \ (add << __rsnd_mod_shift_##func); \ - dev_dbg(dev, "%s[%d] 0x%08x %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \ - called ? #func : ""); \ + dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), \ + mod->status, call ? #func : ""); \ + if (call) \ + ret = (mod)->ops->func(mod, io, param); \ ret; \ }) -- cgit v1.1 From 32a96d558d02581c32b09401b8aa5eecb3965d3e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:14:21 +0000 Subject: ASoC: rsnd: fixup rsnd_dai_call() behavior for unimplemented method Current rsnd_dai_call didn't count callback-count if callback wasn't implemented. Thus, it counts can be unbalance. ex) .start : implemented .stop : not implemented This patch solve this issue Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index c0182ac..5c67144 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -300,7 +300,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) /* * rsnd_dai functions */ -#define __rsnd_mod_call(mod, io, func, param...) \ +#define rsnd_mod_call(mod, io, func, param...) \ ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ @@ -308,7 +308,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - int call = (val == __rsnd_mod_call_##func); \ + int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ mod->status = (mod->status & ~mask) + \ (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ @@ -319,11 +319,6 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ret; \ }) -#define rsnd_mod_call(mod, io, func, param...) \ - (!(mod) ? -ENODEV : \ - !((mod)->ops->func) ? 0 : \ - __rsnd_mod_call(mod, io, func, param)) - #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ -- cgit v1.1 From 89e3e2c352a523be46be5104bf18e200a8ccd444 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:14:43 +0000 Subject: ASoC: rsnd: fixup rsnd_dai_call() behavior for .stop/.quit Current rsnd_dai_call returns immediately if rsnd_mod_call return fail. Thus, each callback-count can be unbalanced for example .init was OK, start was OK, but, .stop was not OK. This case .quit should be called but isn't called. And, rsnd_dai_stream_quit() also not be called. rsnd_dai_call() should call all .stop/.quit eventhough it returns error. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 5c67144..deed48e 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -327,9 +327,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) mod = (io)->mod[i]; \ if (!mod) \ continue; \ - ret = rsnd_mod_call(mod, io, fn, param); \ - if (ret < 0) \ - break; \ + ret |= rsnd_mod_call(mod, io, fn, param); \ } \ ret; \ }) @@ -495,16 +493,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, break; case SNDRV_PCM_TRIGGER_STOP: ret = rsnd_dai_call(stop, io, priv); - if (ret < 0) - goto dai_trigger_end; - ret = rsnd_dai_call(quit, io, priv); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_dai_call(quit, io, priv); - ret = rsnd_platform_call(priv, dai, stop, ssi_id); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_platform_call(priv, dai, stop, ssi_id); rsnd_dai_stream_quit(io); break; -- cgit v1.1 From c9929345018927acaf52c14c57d78116067be6c9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:15:04 +0000 Subject: ASoC: rsnd: rename rsnd_mod_hw_start/stop to rsnd_mod_power_on/off rsnd_mod_hw_start/stop were unclear naming. It became rsnd_mod_power_on/off by this patch Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 4 ++-- sound/soc/sh/rcar/dvc.c | 4 ++-- sound/soc/sh/rcar/mix.c | 4 ++-- sound/soc/sh/rcar/rsnd.h | 4 ++-- sound/soc/sh/rcar/src.c | 4 ++-- sound/soc/sh/rcar/ssi.c | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index f1541f46..3cb214a 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -35,7 +35,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_ctu_initialize_lock(mod); @@ -50,7 +50,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index e36c0ac..58f6909 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -153,7 +153,7 @@ static int rsnd_dvc_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_dvc_soft_reset(mod); @@ -175,7 +175,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index ac2687d..953dd0b 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -58,7 +58,7 @@ static int rsnd_mix_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_mix_soft_reset(mod); @@ -83,7 +83,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index e9fef53..38c16d7 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -330,8 +330,8 @@ struct rsnd_mod { #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) -#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) -#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) +#define rsnd_mod_power_on(mod) clk_enable((mod)->clk) +#define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) int rsnd_mod_init(struct rsnd_priv *priv, diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 1d379e8..37d41f0 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -352,7 +352,7 @@ static int rsnd_src_init(struct rsnd_mod *mod, { struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_src_soft_reset(mod); @@ -373,7 +373,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); if (src->err) dev_warn(dev, "%s[%d] under/over flow err = %d\n", diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 40e2b5d..d70720a 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -184,7 +184,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, u32 cr; if (0 == ssi->usrcnt) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); if (rsnd_rdai_is_clk_master(rdai)) { struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); @@ -265,7 +265,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) rsnd_ssi_master_clk_stop(ssi); } - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); ssi->chan = 0; } -- cgit v1.1 From 69819f527afba7a909a2aba32521a3bea8e3b60b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:15:26 +0000 Subject: ASoC: rsnd: remove unused rsnd_dma_to_ssi() macro rsnd_dma_to_ssi() is no longer used, let's remove it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d70720a..e550621 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -79,7 +79,6 @@ struct rsnd_ssi { #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) #define rsnd_ssi_parent(ssi) ((ssi)->parent) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) -- cgit v1.1 From b415b4d3122a466f3a73d86a1dd2dcdc13de7ef3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:15:46 +0000 Subject: ASoC: rsnd: remove duplicate parameter from rsnd_ssi_xxx() rsnd_ssi_use_busif() and rsnd_ssi_is_pin_sharing() are the function which returns current SSI status. But these requests duplicated parameter. This patch removes duplicated parameter. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 3 +-- sound/soc/sh/rcar/dma.c | 2 +- sound/soc/sh/rcar/rsnd.h | 7 +++++-- sound/soc/sh/rcar/src.c | 2 +- sound/soc/sh/rcar/ssi.c | 11 ++++++----- 5 files changed, 14 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index c4ebbb7..2a5b3a2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -69,11 +69,10 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div) static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); int id = rsnd_mod_id(mod); int ws = id; - if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { + if (rsnd_ssi_is_pin_sharing(io)) { switch (id) { case 1: case 2: diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index bfbb8a5..5d084d0 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -470,7 +470,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, dev_err(dev, "DVC is selected without SRC\n"); /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(io, mod)) + if (is_ssi && rsnd_ssi_use_busif(io)) is_ssi++; return (is_from) ? diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 38c16d7..0853298 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -572,9 +572,12 @@ int rsnd_ssi_probe(struct platform_device *pdev, void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); + +#define rsnd_ssi_is_pin_sharing(io) \ + __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); /* * R-Car SRC diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 37d41f0..261b502 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -159,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, /* * SSI_MODE1 */ - if (rsnd_ssi_is_pin_sharing(ssi_mod)) { + if (rsnd_ssi_is_pin_sharing(io)) { int shift = -1; switch (ssi_id) { case 1: diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index e550621..1427ec2 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -86,8 +86,9 @@ struct rsnd_ssi { #define rsnd_ssi_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int use_busif = 0; @@ -394,7 +395,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod)); + rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io)); rsnd_ssi_hw_start(ssi, io); @@ -613,7 +614,7 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, int is_play = rsnd_io_is_play(io); char *name; - if (rsnd_ssi_use_busif(io, mod)) + if (rsnd_ssi_use_busif(io)) name = is_play ? "rxu" : "txu"; else name = is_play ? "rx" : "tx"; @@ -659,7 +660,7 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); } -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -670,7 +671,7 @@ static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { struct rsnd_mod *mod = rsnd_mod_get(ssi); - if (!rsnd_ssi_is_pin_sharing(mod)) + if (!__rsnd_ssi_is_pin_sharing(mod)) return; switch (rsnd_mod_id(mod)) { -- cgit v1.1 From 4eb0f7abcefad2d4c127aa7502d3122635eddab0 Mon Sep 17 00:00:00 2001 From: Jiada Wang Date: Tue, 20 Oct 2015 11:47:11 +0900 Subject: ASoC: wm8962: mark cache_dirty flag after software reset in pm_resume By doing software reset of wm8962 in pm_resume, all registers which have already been set will be reset to default value without regmap interface be involved, thus driver need to mark cache_dirty flag, to let regcache can be updated by regcache_sync(). Signed-off-by: Jiada Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b4eb975..45f06d5 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3804,6 +3804,8 @@ static int wm8962_runtime_resume(struct device *dev) wm8962_reset(wm8962); + regcache_mark_dirty(wm8962->regmap); + /* SYSCLK defaults to on; make sure it is off so we can safely * write to registers if the device is declocked. */ -- cgit v1.1 From 16566e47098211e30b3d8a0bc6a3576871ada8e8 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 21 Oct 2015 09:46:05 +0800 Subject: ASoC: rt5640: Fill up the IN3's support Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e1ceeb8..f2beb1a 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -405,11 +405,14 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv), - /* IN1/IN2 Control */ + /* IN1/IN2/IN3 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2, RT5640_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4, RT5640_BST_SFT2, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2, + RT5640_BST_SFT2, 8, 0, bst_tlv), + /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL, RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT, @@ -598,6 +601,8 @@ static const struct snd_kcontrol_new rt5640_rec_l_mix[] = { RT5640_M_HP_L_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER, RT5640_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST2_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER, RT5640_M_BST4_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER, @@ -611,6 +616,8 @@ static const struct snd_kcontrol_new rt5640_rec_r_mix[] = { RT5640_M_HP_R_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER, RT5640_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST2_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER, RT5640_M_BST4_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER, @@ -1065,6 +1072,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN1N"), SND_SOC_DAPM_INPUT("IN2P"), SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1081,6 +1090,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { RT5640_PWR_BST1_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2, RT5640_PWR_BST4_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2, + RT5640_PWR_BST2_BIT, 0, NULL, 0), /* Input Volume */ SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL, RT5640_PWR_IN_L_BIT, 0, NULL, 0), @@ -1310,6 +1321,7 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = { static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"IN1P", NULL, "LDO2"}, {"IN2P", NULL, "LDO2"}, + {"IN3P", NULL, "LDO2"}, {"DMIC L1", NULL, "DMIC1"}, {"DMIC R1", NULL, "DMIC1"}, @@ -1320,18 +1332,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"BST1", NULL, "IN1N"}, {"BST2", NULL, "IN2P"}, {"BST2", NULL, "IN2N"}, + {"BST3", NULL, "IN3P"}, + {"BST3", NULL, "IN3N"}, {"INL VOL", NULL, "IN2P"}, {"INR VOL", NULL, "IN2N"}, {"RECMIXL", "HPOL Switch", "HPOL"}, {"RECMIXL", "INL Switch", "INL VOL"}, + {"RECMIXL", "BST3 Switch", "BST3"}, {"RECMIXL", "BST2 Switch", "BST2"}, {"RECMIXL", "BST1 Switch", "BST1"}, {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"}, {"RECMIXR", "HPOR Switch", "HPOR"}, {"RECMIXR", "INR Switch", "INR VOL"}, + {"RECMIXR", "BST3 Switch", "BST3"}, {"RECMIXR", "BST2 Switch", "BST2"}, {"RECMIXR", "BST1 Switch", "BST1"}, {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"}, @@ -2260,6 +2276,10 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, RT5640_IN_DF2, RT5640_IN_DF2); + if (rt5640->pdata.in3_diff) + regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, + RT5640_IN_DF2, RT5640_IN_DF2); + rt5640->hp_mute = 1; return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, -- cgit v1.1 From 26d9ca3462df8f7e83fc372b23c8da5ed2b1c4f3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 17:04:33 +0200 Subject: ASoC: Let snd_soc_limit_volume() take a snd_soc_card snd_soc_limit_volume() operates on a card and the CODEC that is passed in is only used to look up the card. Let it directly take the card instead. This makes it possible to use it when no snd_soc_codec is available. Signed-off-by: Lars-Peter Clausen Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/rx51.c | 2 +- sound/soc/soc-ops.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 3bebfb1..9953890 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -297,7 +297,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) dev_err(card->dev, "Failed to add TPA6130A2 controls\n"); return err; } - snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42); + snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42); err = omap_mcbsp_st_add_controls(rtd, 2); if (err < 0) { diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 100d92b..20f702a 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -560,16 +560,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); /** * snd_soc_limit_volume - Set new limit to an existing volume control. * - * @codec: where to look for the control + * @card: where to look for the control * @name: Name of the control * @max: new maximum limit * * Return 0 for success, else error. */ -int snd_soc_limit_volume(struct snd_soc_codec *codec, +int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max) { - struct snd_card *card = codec->component.card->snd_card; + struct snd_card *snd_card = card->snd_card; struct snd_kcontrol *kctl; struct soc_mixer_control *mc; int found = 0; @@ -579,7 +579,7 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec, if (unlikely(!name || max <= 0)) return -EINVAL; - list_for_each_entry(kctl, &card->controls, list) { + list_for_each_entry(kctl, &snd_card->controls, list) { if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { found = 1; break; -- cgit v1.1 From 6f0c42269f000b1e346c84d9a589f17aa94c96d8 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 13 Oct 2015 23:41:00 +0800 Subject: ASoC: compress: add config item for soc-compress to make it compiled only when needed We don't always need soc-compress in soc, here add a config item SND_SOC_COMPRESS, when nobody select it, the soc-compress will not be compiled. Here also change Kconfig to 'select SND_SOC_COMPRESS' for drivers that needed soc-compress. Signed-off-by: Jie Yang Signed-off-by: Mark Brown --- sound/soc/Kconfig | 5 ++++- sound/soc/Makefile | 3 ++- sound/soc/intel/Kconfig | 1 + sound/soc/intel/atom/sst-mfld-platform-pcm.c | 2 +- sound/soc/soc-compress.c | 12 ++++++++++-- sound/soc/soc-core.c | 4 ++-- 6 files changed, 20 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 225bfda..3e4f444 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -9,7 +9,6 @@ menuconfig SND_SOC select SND_JACK if INPUT=y || INPUT=SND select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER - select SND_COMPRESS_OFFLOAD ---help--- If you want ASoC support, you should say Y here and also to the @@ -30,6 +29,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM bool select SND_DMAENGINE_PCM +config SND_SOC_COMPRESS + bool + select SND_COMPRESS_OFFLOAD + config SND_SOC_TOPOLOGY bool diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 134aca1..280ac82 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,5 +1,6 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o -snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o +snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o +snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o ifneq ($(CONFIG_SND_SOC_TOPOLOGY),) snd-soc-core-objs += soc-topology.o diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 05fde5e6e..221e3bd 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -12,6 +12,7 @@ config SND_MFLD_MACHINE config SND_SST_MFLD_PLATFORM tristate + select SND_SOC_COMPRESS config SND_SST_IPC tristate diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 683e501..4c734f5 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -529,7 +529,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, { .name = "compress-cpu-dai", - .compress_dai = 1, + .compress_new = snd_soc_new_compress, .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 025c38f..12a9820 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -612,8 +612,15 @@ static struct snd_compr_ops soc_compr_dyn_ops = { .get_codec_caps = soc_compr_get_codec_caps }; -/* create a new compress */ -int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) +/** + * snd_soc_new_compress - create a new compress. + * + * @rtd: The runtime for which we will create compress + * @num: the device index number (zero based - shared with normal PCMs) + * + * Return: 0 for success, else error. + */ +int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; @@ -703,3 +710,4 @@ compr_err: kfree(compr); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_new_compress); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6173d15..6e543e1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1370,9 +1370,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) soc_dpcm_debugfs_add(rtd); #endif - if (cpu_dai->driver->compress_dai) { + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = soc_new_compress(rtd, num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); -- cgit v1.1 From 931dfa6931448ccfd12d4ccd8d1d9289ceb92439 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:08:18 +0300 Subject: ASoC: wm8904: harmless underflow in wm8904_put_deemph() We try to check that "deemph" is zero or one, but because of an underflow bug, it can also be negative. It's fine, negative values are handled as non-zero. But it's messy and static checkers complain so let's fix it. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8904.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index b783743..2aa23f1 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -534,7 +534,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; -- cgit v1.1 From 523bade22428d4c6742dd7bfb4c0259a23bb9ec3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:08:57 +0300 Subject: ASoC: wm8731: harmless underflow in wm8731_put_deemph() The code tries to verify that "deemph" is zero or one, but it fails to account for that it can be negative. It's harmless because negatives are treated the same as one, but we should fix it just to silence the static checker warning. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index ace8645..07cf1bd 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -132,7 +132,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) -- cgit v1.1 From c1fe81f2cd30c18d7df444697bac8051fdf2aee6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:09:19 +0300 Subject: ASoC: wm8960: harmless underflow in wm8960_put_deemph() We should only accept "deemph" values of zero and one, but by mistake we accept negatives as well. It's harmless but let's clean it up anyway. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index e3b7d0c..dc47c2f 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -201,7 +201,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; -- cgit v1.1 From 4d0197a5cad29ee58593eceac85cbb712430cd89 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:10:18 +0300 Subject: ASoC: wm8903: harmless underflow in wm8903_put_deemph() Valid values for "deemph" are zero and one, but we accidentally allow negative values as well. It's harmless, but static checkers complain and we may as well clean it up. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8903.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index b011253..e4cc41e 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -452,7 +452,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) -- cgit v1.1 From 3e2a71b232d6b6270eda72ff659c31c97899c00d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:10:48 +0300 Subject: ASoC: wm8955: harmless underflow in wm8955_put_deemph() Valid values for "deemph" are zero and one, but we mistakenly allow negative values as well. It's harmless but we may as well clean it up. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8955.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 12e4435..9db00d5 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -402,7 +402,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; -- cgit v1.1 From 7ab8a54ea7914574c04c0ac441f9ab65ff21abd9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:11:09 +0300 Subject: ASoC: es8328: harmless underflow in es8328_put_deemph() Valid values for "deemph" are zero and one but we accidentally allow negative values as well. It's harmless but it causes static checker warnings and we may as well clean it up. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 6a09101..969e337 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret; if (deemph > 1) -- cgit v1.1 From 8444f59fd7bd8aa079609e575c0688669b85bfcc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:12:45 +0300 Subject: ASoC: wm2000: a couple harmless underflows We want these to be zero or one, but by mistake we also accept negative values. It's harmless but we should still clean it up. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm2000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 786abd0..a67ea10 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -620,7 +620,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int anc_active = ucontrol->value.integer.value[0]; + unsigned int anc_active = ucontrol->value.integer.value[0]; int ret; if (anc_active > 1) @@ -653,7 +653,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int val = ucontrol->value.integer.value[0]; + unsigned int val = ucontrol->value.integer.value[0]; int ret; if (val > 1) -- cgit v1.1 From ef3e199a49c8e605e326ae60c5e156bfb1ca7e3d Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 22 Oct 2015 13:55:07 +0800 Subject: ASoC: Intel: sst: only use sst-firmware when DW DMAC is available Currentlly, we use Synopsys DesignWare DMA Controller for baytrail/haswell/broadwell ADSP firmware loading, but for skylake, we don't use it, compiling sst-firmware.c may introduce error when CONFIG_DW_DMAC_CORE is not enabled: sound/built-in.o: In function `sst_dma_new': (.text+0xd7b38): undefined reference to `dw_dma_probe' sound/built-in.o: In function `sst_dma_free': (.text+0xd7c0a): undefined reference to `dw_dma_remove' Here we only compile sst-firmware when CONFIG_DW_DMAC_CORE is selected, to fix the linking error issue. Reported-by: Randy Dunlap Signed-off-by: Jie Yang Signed-off-by: Mark Brown --- sound/soc/intel/common/Makefile | 6 +++++- sound/soc/intel/common/sst-dsp.c | 2 ++ sound/soc/intel/common/sst-dsp.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index f24154c..d910558 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,7 +1,11 @@ -snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o +ifneq ($(CONFIG_DW_DMAC_CORE),) +snd-soc-sst-dsp-objs += sst-firmware.o +endif + obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index a627236..c9452e0 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) } EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) { @@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst) sst_dma_free(sst->dma); } EXPORT_SYMBOL_GPL(sst_dsp_free); +#endif /* Module information */ MODULE_AUTHOR("Liam Girdwood"); diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 1f45f18..859f0de 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -216,10 +216,12 @@ struct sst_pdata { void *dsp; }; +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) /* Initialization */ struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); void sst_dsp_free(struct sst_dsp *sst); +#endif /* SHIM Read / Write */ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); -- cgit v1.1 From ba40a854ea4f9d81368dc023bd25ac4073058039 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 9 Oct 2015 18:16:36 -0400 Subject: ASoC: Intel: switch from ioremap_cache to memremap In preparation for deprecating ioremap_cache() convert its usage in skl-nhlt to memremap. Signed-off-by: Dan Williams Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 10 +++++----- sound/soc/intel/skylake/skl.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 13036b1..b0c7bd1 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" -void __iomem *skl_nhlt_init(struct device *dev) +void *skl_nhlt_init(struct device *dev) { acpi_handle handle; union acpi_object *obj; @@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev) if (obj && obj->type == ACPI_TYPE_BUFFER) { nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length); + return memremap(nhlt_ptr->min_addr, nhlt_ptr->length, + MEMREMAP_WB); } dev_err(dev, "device specific method to extract NHLT blob failed\n"); return NULL; } -void skl_nhlt_free(void __iomem *addr) +void skl_nhlt_free(void *addr) { - iounmap(addr); - addr = NULL; + memunmap(addr); } static struct nhlt_specific_cfg *skl_get_specific_cfg( diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index e980d78..dd2e79a 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -62,7 +62,7 @@ struct skl { unsigned int init_failed:1; /* delayed init failed */ struct platform_device *dmic_dev; - void __iomem *nhlt; /* nhlt ptr */ + void *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ struct skl_dsp_resource resource; @@ -83,8 +83,8 @@ struct skl_dma_params { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -void __iomem *skl_nhlt_init(struct device *dev); -void skl_nhlt_free(void __iomem *addr); +void *skl_nhlt_init(struct device *dev); +void skl_nhlt_free(void *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); -- cgit v1.1 From 19a2557b76d64f26c761925cb4fecefb5d72c099 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 20 Oct 2015 22:30:06 +0530 Subject: ASoC: dapm: Add kcontrol support for PGAs For DSPs we can define processing blocks as DAPM PGA widgets. Some of these proceesing blocks can be configured by usermode like EQ etc. So we need to add support of kcontrol for PGA widgets. Signed-off-by: Jeeja KP Signed-off-by: Mythri P K Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f4bf21a..9762ac4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -779,7 +779,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, * Determine if a kcontrol is shared. If it is, look it up. If it isn't, * create it. Either way, add the widget into the control's widget list */ -static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, +static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, int kci) { struct snd_soc_dapm_context *dapm = w->dapm; @@ -810,6 +810,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, switch (w->id) { case snd_soc_dapm_switch: case snd_soc_dapm_mixer: + case snd_soc_dapm_pga: wname_in_long_name = true; kcname_in_long_name = true; break; @@ -899,7 +900,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue; if (!w->kcontrols[i]) { - ret = dapm_create_or_share_mixmux_kcontrol(w, i); + ret = dapm_create_or_share_kcontrol(w, i); if (ret < 0) return ret; } @@ -952,7 +953,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return -EINVAL; } - ret = dapm_create_or_share_mixmux_kcontrol(w, 0); + ret = dapm_create_or_share_kcontrol(w, 0); if (ret < 0) return ret; @@ -967,9 +968,13 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) /* create new dapm volume control */ static int dapm_new_pga(struct snd_soc_dapm_widget *w) { - if (w->num_kcontrols) - dev_err(w->dapm->dev, - "ASoC: PGA controls not supported: '%s'\n", w->name); + int i, ret; + + for (i = 0; i < w->num_kcontrols; i++) { + ret = dapm_create_or_share_kcontrol(w, i); + if (ret < 0) + return ret; + } return 0; } -- cgit v1.1 From 9b8ef9f6b3fcccc2b6ce4bb59d8ab55b36a8b8f0 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 20 Oct 2015 22:30:07 +0530 Subject: ASoC: dapm: Add startup & shutdown for dai_links For DAI link events, DSPs would like to get notified for startup and shutdown event as well apart for existing hw_params. This helps managing DSP resource allocation and freeup on these events So add support for startup and shutdown for snd_soc_dai_link_event() Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9762ac4..a28d6a1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3478,11 +3478,29 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: substream.stream = SNDRV_PCM_STREAM_CAPTURE; + if (source->driver->ops && source->driver->ops->startup) { + ret = source->driver->ops->startup(&substream, source); + if (ret < 0) { + dev_err(source->dev, + "ASoC: startup() failed: %d\n", ret); + goto out; + } + source->active++; + } ret = soc_dai_hw_params(&substream, params, source); if (ret < 0) goto out; substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + if (sink->driver->ops && sink->driver->ops->startup) { + ret = sink->driver->ops->startup(&substream, sink); + if (ret < 0) { + dev_err(sink->dev, + "ASoC: startup() failed: %d\n", ret); + goto out; + } + sink->active++; + } ret = soc_dai_hw_params(&substream, params, sink); if (ret < 0) goto out; @@ -3502,6 +3520,18 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret); ret = 0; + + source->active--; + if (source->driver->ops && source->driver->ops->shutdown) { + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + source->driver->ops->shutdown(&substream, source); + } + + sink->active--; + if (sink->driver->ops && sink->driver->ops->shutdown) { + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + sink->driver->ops->shutdown(&substream, sink); + } break; default: -- cgit v1.1 From 93e39a11520c000c9086215460bf27b35b09c724 Mon Sep 17 00:00:00 2001 From: Mythri P K Date: Tue, 20 Oct 2015 22:30:08 +0530 Subject: ASoC: dapm: Add snd_soc_dapm_kcontrol_widget() Given a kcontrol, we may want to access the parent widget and it's associated data. So export function to return it. Signed-off-by: Mythri P K Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a28d6a1..38281c2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -509,6 +509,18 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, } /** + * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a + * kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( + struct snd_kcontrol *kcontrol) +{ + return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget); + +/** * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a * kcontrol * @kcontrol: The kcontrol -- cgit v1.1 From c86ba612bdba4d267ce5aad30ff10b4d994e2b33 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 19 Oct 2015 16:49:05 -0700 Subject: ASoC: nau8825: Add FLL configuration snd_soc_codec_driver.set_pll is implemented to configure the FLL. The codec internal SYSCLK can be from either the MCLK pin directly, or the FLL. This is configured by snd_soc_codec_driver.set_pll. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 163 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/nau8825.h | 28 ++++++-- 2 files changed, 186 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 5c1badf..bd58b96 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,58 @@ #include "nau8825.h" +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MIN 90000000 + +struct nau8825_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_int; + int clk_ref_div; +}; + +struct nau8825_fll_attr { + unsigned int param; + unsigned int val; +}; + +/* scaling for mclk from sysclk_src output */ +static const struct nau8825_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, + { 48, 0xd }, + { 96, 0xe }, + { 5, 0xf }, +}; + +/* ratio for input clk freq */ +static const struct nau8825_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8825_fll_attr fll_pre_scalar[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 4, 0x2 }, + { 8, 0x3 }, +}; + static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_ENA_CTRL, 0x00ff }, { NAU8825_REG_CLK_DIVIDER, 0x0050 }, @@ -808,6 +861,115 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec) return 0; } +/** + * nau8825_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, + struct nau8825_fll *fll_param) +{ + u64 fvco; + unsigned int fref, i; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in / fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 100MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX) + break; + } + if (i == ARRAY_SIZE(mclk_src_scaling)) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[i].val; + + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco << 16, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> 16) & 0x3FF; + fll_param->fll_frac = fvco & 0xFFFF; + return 0; +} + +static void nau8825_fll_apply(struct nau8825 *nau8825, + struct nau8825_fll *fll_param) +{ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, + NAU8825_FLL_RATIO_MASK, fll_param->ratio); + /* FLL 16-bit fractional input */ + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); + /* FLL 10-bit integer input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, + NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4, + NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div); + /* select divided VCO input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_FILTER_SW_MASK, 0x0000); + /* FLL sigma delta modulator enable */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN_MASK, NAU8825_SDM_EN); +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct nau8825_fll fll_param; + int ret, fs; + + fs = freq_out / 256; + ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); + if (ret < 0) { + dev_err(codec->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, + fll_param.fll_int, fll_param.clk_ref_div); + + nau8825_fll_apply(nau8825, &fll_param); + mdelay(2); + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + return 0; +} + static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, unsigned int freq) { @@ -920,6 +1082,7 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec, static struct snd_soc_codec_driver nau8825_codec_driver = { .probe = nau8825_codec_probe, .set_sysclk = nau8825_set_sysclk, + .set_pll = nau8825_set_pll, .set_bias_level = nau8825_set_bias_level, .suspend_bias_off = true, diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 8774923..dff8edb 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -101,13 +101,31 @@ #define NAU8825_ENABLE_SAR_SFT 1 /* CLK_DIVIDER (0x3) */ -#define NAU8825_CLK_SRC_SFT 15 -#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) -#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) -#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_SFT 15 +#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0) + +/* FLL1 (0x04) */ +#define NAU8825_FLL_RATIO_MASK (0x7f << 0) + +/* FLL3 (0x06) */ +#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) + +/* FLL4 (0x07) */ +#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10) + +/* FLL5 (0x08) */ +#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14) /* FLL6 (0x9) */ -#define NAU8825_DCO_EN (1 << 15) +#define NAU8825_DCO_EN_MASK (0x1 << 15) +#define NAU8825_DCO_EN (0x1 << 15) +#define NAU8825_DCO_DIS (0x0 << 15) +#define NAU8825_SDM_EN_MASK (0x1 << 14) +#define NAU8825_SDM_EN (0x1 << 14) +#define NAU8825_SDM_DIS (0x0 << 14) /* HSD_CTRL (0xc) */ #define NAU8825_HSD_AUTO_MODE (1 << 6) -- cgit v1.1 From 218d2ce2036f50d259dbcdd37a4db72cad6fc0d1 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 19 Oct 2015 16:49:06 -0700 Subject: ASoC: nau8825: Show device properties The codec device properties are printed for debugging. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index bd58b96..7fc7b4e 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1100,6 +1100,34 @@ static void nau8825_reset_chip(struct regmap *regmap) regmap_write(regmap, NAU8825_REG_RESET, 0x00); } +static void nau8825_print_device_properties(struct nau8825 *nau8825) +{ + int i; + struct device *dev = nau8825->dev; + + dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable); + dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable); + dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up); + dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity); + dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage); + dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance); + + dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num); + for (i = 0; i < nau8825->sar_threshold_num; i++) + dev_dbg(dev, "sar-threshold[%d]=%d\n", i, + nau8825->sar_threshold[i]); + + dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis); + dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage); + dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time); + dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time); + dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce); + dev_dbg(dev, "jack-insert-debounce: %d\n", + nau8825->jack_insert_debounce); + dev_dbg(dev, "jack-eject-debounce: %d\n", + nau8825->jack_eject_debounce); +} + static int nau8825_read_device_properties(struct device *dev, struct nau8825 *nau8825) { @@ -1213,6 +1241,8 @@ static int nau8825_i2c_probe(struct i2c_client *i2c, nau8825->dev = dev; nau8825->irq = i2c->irq; + nau8825_print_device_properties(nau8825); + nau8825_reset_chip(nau8825->regmap); ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value); if (ret < 0) { -- cgit v1.1 From 50760cad9de969fe85b24465afe6396b8bbc6a3f Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 19 Sep 2015 02:00:25 +0200 Subject: ASoC: fsl-asoc-card: add AC'97 support Add AC'97 support to fsl-asoc-card using generic ASoC AC'97 CODEC. The SSI controller will silently enable any TX AC'97 slots that have their bits set in SLOTREQ received from CODEC and then will redirect some of playback samples there. That's why it is important to make sure that any of CODEC playback slots that can pull samples are set to slots 3/4 (standard PCM playback slots). Currently, this applies to S/PDIF slots as they were seen to pull samples sometimes even with S/PDIF output being disabled. Signed-off-by: Maciej Szmigiero Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 140 +++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 30 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5aeb6ed..33628a0 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -14,6 +14,9 @@ #include #include #include +#if IS_ENABLED(CONFIG_SND_AC97_CODEC) +#include +#endif #include #include @@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; +static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv) +{ + return priv->dai_fmt == SND_SOC_DAIFMT_AC97; +} + static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, * set_bias_level(), bypass the remaining settings in hw_params(). * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS. */ - if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) + if ((priv->card.set_bias_level && + priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) || + fsl_asoc_card_is_ac97(priv)) return 0; /* Specific configurations of DAIs starts from here */ @@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, ext_port--; /* - * Use asynchronous mode (6 wires) for all cases. + * Use asynchronous mode (6 wires) for all cases except AC97. * If only 4 wires are needed, just set SSI into * synchronous mode and enable 4 PADs in IOMUX. */ @@ -346,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_TCLKDIR; break; default: - return -EINVAL; + if (!fsl_asoc_card_is_ac97(priv)) + return -EINVAL; + } + + if (fsl_asoc_card_is_ac97(priv)) { + int_ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCLKDIR; + ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | + IMX_AUDMUX_V2_PTCR_TFSDIR; } /* Asynchronous mode can not be set along with RCLKDIR */ - ret = imx_audmux_v2_configure_port(int_port, 0, - IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); - if (ret) { - dev_err(dev, "audmux internal port setup failed\n"); - return ret; + if (!fsl_asoc_card_is_ac97(priv)) { + unsigned int pdcr = + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port); + + ret = imx_audmux_v2_configure_port(int_port, 0, + pdcr); + if (ret) { + dev_err(dev, "audmux internal port setup failed\n"); + return ret; + } } ret = imx_audmux_v2_configure_port(int_port, int_ptcr, @@ -364,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, return ret; } - ret = imx_audmux_v2_configure_port(ext_port, 0, - IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); - if (ret) { - dev_err(dev, "audmux external port setup failed\n"); - return ret; + if (!fsl_asoc_card_is_ac97(priv)) { + unsigned int pdcr = + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port); + + ret = imx_audmux_v2_configure_port(ext_port, 0, + pdcr); + if (ret) { + dev_err(dev, "audmux external port setup failed\n"); + return ret; + } } ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, @@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct device *dev = card->dev; int ret; + if (fsl_asoc_card_is_ac97(priv)) { +#if IS_ENABLED(CONFIG_SND_AC97_CODEC) + struct snd_soc_codec *codec = card->rtd[0].codec; + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + /* + * Use slots 3/4 for S/PDIF so SSI won't try to enable + * other slots and send some samples there + * due to SLOTREQ bits for S/PDIF received from codec + */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, + AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4); +#endif + + return 0; + } + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); if (ret) { @@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; struct i2c_client *codec_dev; - struct clk *codec_clk; const char *codec_dai_name; u32 width; int ret; @@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Give a chance to old DT binding */ if (!cpu_np) cpu_np = of_parse_phandle(np, "ssi-controller", 0); - codec_np = of_parse_phandle(np, "audio-codec", 0); - if (!cpu_np || !codec_np) { - dev_err(&pdev->dev, "phandle missing or invalid\n"); + if (!cpu_np) { + dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); ret = -EINVAL; goto fail; } @@ -434,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } - codec_dev = of_find_i2c_device_by_node(codec_np); - if (!codec_dev) { - dev_err(&pdev->dev, "failed to find codec platform device\n"); - ret = -EINVAL; - goto fail; - } + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (codec_np) + codec_dev = of_find_i2c_device_by_node(codec_np); + else + codec_dev = NULL; asrc_np = of_parse_phandle(np, "audio-asrc", 0); if (asrc_np) asrc_pdev = of_find_device_by_node(asrc_np); /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ - codec_clk = clk_get(&codec_dev->dev, NULL); - if (!IS_ERR(codec_clk)) { - priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); - clk_put(codec_clk); + if (codec_dev) { + struct clk *codec_clk = clk_get(&codec_dev->dev, NULL); + + if (!IS_ERR(codec_clk)) { + priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } } /* Default sample rate and format, will be updated in hw_params() */ @@ -486,11 +533,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { + codec_dai_name = "ac97-hifi"; + priv->card.set_bias_level = NULL; + priv->dai_fmt = SND_SOC_DAIFMT_AC97; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); return -EINVAL; } + if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { + dev_err(&pdev->dev, "failed to find codec device\n"); + ret = -EINVAL; + goto asrc_fail; + } + /* Common settings for corresponding Freescale CPU DAI driver */ if (strstr(cpu_np->name, "ssi")) { /* Only SSI needs to configure AUDMUX */ @@ -507,7 +564,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } - sprintf(priv->name, "%s-audio", codec_dev->name); + snprintf(priv->name, sizeof(priv->name), "%s-audio", + fsl_asoc_card_is_ac97(priv) ? "ac97" : + codec_dev->name); /* Initialize sound card */ priv->pdev = pdev; @@ -531,8 +590,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; - priv->dai_link[0].codec_of_node = codec_np; priv->dai_link[0].codec_dai_name = codec_dai_name; + + if (!fsl_asoc_card_is_ac97(priv)) + priv->dai_link[0].codec_of_node = codec_np; + else { + u32 idx; + + ret = of_property_read_u32(cpu_np, "cell-index", &idx); + if (ret) { + dev_err(&pdev->dev, + "cannot get CPU index property\n"); + goto asrc_fail; + } + + priv->dai_link[0].codec_name = + devm_kasprintf(&pdev->dev, GFP_KERNEL, + "ac97-codec.%u", + (unsigned int)idx); + } + priv->dai_link[0].platform_of_node = cpu_np; priv->dai_link[0].dai_fmt = priv->dai_fmt; priv->card.num_links = 1; @@ -543,6 +620,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_link[1].platform_of_node = asrc_np; priv->dai_link[2].codec_dai_name = codec_dai_name; priv->dai_link[2].codec_of_node = codec_np; + priv->dai_link[2].codec_name = + priv->dai_link[0].codec_name; priv->dai_link[2].cpu_of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; priv->card.num_links = 3; @@ -578,14 +657,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); -fail: of_node_put(codec_np); +fail: of_node_put(cpu_np); return ret; } static const struct of_device_id fsl_asoc_card_dt_ids[] = { + { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, -- cgit v1.1 From 942e4a30eb87a7565c1cd28a08825f58b0794711 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 21 Sep 2015 18:38:19 +0800 Subject: ASoC: rt5645: Add dmi "Google Reks" for chrome Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index b3eff73..65ea712 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3228,6 +3228,13 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"), }, }, + { + .ident = "Google Reks", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Reks"), + }, + }, { } }; -- cgit v1.1 From e0a25b6d18624140905d79775f9e1b05c12502f5 Mon Sep 17 00:00:00 2001 From: Songjun Wu Date: Thu, 8 Oct 2015 18:13:31 +0800 Subject: ASoC: atmel-classd: add the Audio Class D Amplifier Add driver for the digital imput to PWM output stereo class D amplifier. It comes with filter, digitally controlled gain, an equalizer and a dmphase filter. Signed-off-by: Songjun Wu Signed-off-by: Mark Brown --- sound/soc/atmel/Kconfig | 9 + sound/soc/atmel/Makefile | 2 + sound/soc/atmel/atmel-classd.c | 679 +++++++++++++++++++++++++++++++++++++++++ sound/soc/atmel/atmel-classd.h | 120 ++++++++ 4 files changed, 810 insertions(+) create mode 100644 sound/soc/atmel/atmel-classd.c create mode 100644 sound/soc/atmel/atmel-classd.h (limited to 'sound') diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 1489cd4..2d30464 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -59,4 +59,13 @@ config SND_AT91_SOC_SAM9X5_WM8731 help Say Y if you want to add support for audio SoC on an at91sam9x5 based board that is using WM8731 codec. + +config SND_ATMEL_SOC_CLASSD + tristate "Atmel ASoC driver for boards using CLASSD" + depends on ARCH_AT91 || COMPILE_TEST + select SND_ATMEL_SOC_DMA + select REGMAP_MMIO + help + Say Y if you want to add support for Atmel ASoC driver for boards using + CLASSD. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index b327e5c..f6f7db4 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o snd-atmel-soc-wm8904-objs := atmel_wm8904.o snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o +snd-atmel-soc-classd-objs := atmel-classd.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o +obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c new file mode 100644 index 0000000..8276675 --- /dev/null +++ b/sound/soc/atmel/atmel-classd.c @@ -0,0 +1,679 @@ +/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver + * + * Copyright (C) 2015 Atmel + * + * Author: Songjun Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or later + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atmel-classd.h" + +struct atmel_classd_pdata { + bool non_overlap_enable; + int non_overlap_time; + int pwm_type; + const char *card_name; +}; + +struct atmel_classd { + dma_addr_t phy_base; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + struct clk *aclk; + int irq; + const struct atmel_classd_pdata *pdata; +}; + +#ifdef CONFIG_OF +static const struct of_device_id atmel_classd_of_match[] = { + { + .compatible = "atmel,sama5d2-classd", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, atmel_classd_of_match); + +static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct atmel_classd_pdata *pdata; + const char *pwm_type; + int ret; + + if (!np) { + dev_err(dev, "device node not found\n"); + return ERR_PTR(-EINVAL); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type); + if ((ret == 0) && (strcmp(pwm_type, "diff") == 0)) + pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF; + else + pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE; + + ret = of_property_read_u32(np, + "atmel,non-overlap-time", &pdata->non_overlap_time); + if (ret) + pdata->non_overlap_enable = false; + else + pdata->non_overlap_enable = true; + + ret = of_property_read_string(np, "atmel,model", &pdata->card_name); + if (ret) + pdata->card_name = "CLASSD"; + + return pdata; +} +#else +static inline struct atmel_classd_pdata * +atmel_classd_dt_init(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + +#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \ + | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \ + | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \ + | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \ + | SNDRV_PCM_RATE_96000) + +static const struct snd_pcm_hardware atmel_classd_hw = { + .info = SNDRV_PCM_INFO_MMAP + | SNDRV_PCM_INFO_MMAP_VALID + | SNDRV_PCM_INFO_INTERLEAVED + | SNDRV_PCM_INFO_RESUME + | SNDRV_PCM_INFO_PAUSE, + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = ATMEL_CLASSD_RATES, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 32 * 1024, + .periods_min = 2, + .periods_max = 256, +}; + +#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024) + +/* cpu dai component */ +static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + regmap_write(dd->regmap, CLASSD_THR, 0x0); + + return clk_prepare_enable(dd->pclk); +} + +static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(dd->pclk); +} + +static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { + .startup = atmel_classd_cpu_dai_startup, + .shutdown = atmel_classd_cpu_dai_shutdown, +}; + +static struct snd_soc_dai_driver atmel_classd_cpu_dai = { + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = ATMEL_CLASSD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &atmel_classd_cpu_dai_ops, +}; + +static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = { + .name = "atmel-classd", +}; + +/* platform */ +static int +atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + if (params_physical_width(params) != 16) { + dev_err(rtd->platform->dev, + "only supports 16-bit audio data\n"); + return -EINVAL; + } + + slave_config->direction = DMA_MEM_TO_DEV; + slave_config->dst_addr = dd->phy_base + CLASSD_THR; + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config->dst_maxburst = 1; + slave_config->src_maxburst = 1; + slave_config->device_fc = false; + + return 0; +} + +static const struct snd_dmaengine_pcm_config +atmel_classd_dmaengine_pcm_config = { + .prepare_slave_config = atmel_classd_platform_configure_dma, + .pcm_hardware = &atmel_classd_hw, + .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE, +}; + +/* codec */ +static const char * const mono_mode_text[] = { + "mix", "sat", "left", "right" +}; + +static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum, + CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT, + mono_mode_text); + +static const char * const eqcfg_text[] = { + "Treble-12dB", "Treble-6dB", + "Medium-8dB", "Medium-3dB", + "Bass-12dB", "Bass-6dB", + "0 dB", + "Bass+6dB", "Bass+12dB", + "Medium+3dB", "Medium+8dB", + "Treble+6dB", "Treble+12dB", +}; + +static const unsigned int eqcfg_value[] = { + CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6, + CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3, + CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6, + CLASSD_INTPMR_EQCFG_FLAT, + CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12, + CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8, + CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum, + CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf, + eqcfg_text, eqcfg_value); + +static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1); + +static const struct snd_kcontrol_new atmel_classd_snd_controls[] = { +SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR, + CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT, + 78, 1, classd_digital_tlv), + +SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR, + CLASSD_INTPMR_DEEMP_SHIFT, 1, 0), + +SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0), + +SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0), + +SOC_ENUM("Mono Mode", classd_mono_mode_enum), + +SOC_ENUM("EQ", classd_eqcfg_enum), +}; + +static const char * const pwm_type[] = { + "Single ended", "Differential" +}; + +static int atmel_classd_codec_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec); + struct atmel_classd *dd = snd_soc_card_get_drvdata(card); + const struct atmel_classd_pdata *pdata = dd->pdata; + u32 mask, val; + + mask = CLASSD_MR_PWMTYP_MASK; + val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT; + + mask |= CLASSD_MR_NON_OVERLAP_MASK; + if (pdata->non_overlap_enable) { + val |= (CLASSD_MR_NON_OVERLAP_EN + << CLASSD_MR_NON_OVERLAP_SHIFT); + + mask |= CLASSD_MR_NOVR_VAL_MASK; + switch (pdata->non_overlap_time) { + case 5: + val |= (CLASSD_MR_NOVR_VAL_5NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 10: + val |= (CLASSD_MR_NOVR_VAL_10NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 15: + val |= (CLASSD_MR_NOVR_VAL_15NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 20: + val |= (CLASSD_MR_NOVR_VAL_20NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + default: + val |= (CLASSD_MR_NOVR_VAL_10NS + << CLASSD_MR_NOVR_VAL_SHIFT); + dev_warn(codec->dev, + "non-overlapping value %d is invalid, the default value 10 is specified\n", + pdata->non_overlap_time); + break; + } + } + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + dev_info(codec->dev, + "PWM modulation type is %s, non-overlapping is %s\n", + pwm_type[pdata->pwm_type], + pdata->non_overlap_enable?"enabled":"disabled"); + + return 0; +} + +static struct regmap *atmel_classd_codec_get_remap(struct device *dev) +{ + return dev_get_regmap(dev, NULL); +} + +static struct snd_soc_codec_driver soc_codec_dev_classd = { + .probe = atmel_classd_codec_probe, + .controls = atmel_classd_snd_controls, + .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), + .get_regmap = atmel_classd_codec_get_remap, +}; + +/* codec dai component */ +static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + int ret; + + ret = clk_prepare_enable(dd->aclk); + if (ret) + return ret; + + return clk_prepare_enable(dd->gclk); +} + +static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai, + int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 mask, val; + + mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK; + + if (mute) + val = mask; + else + val = 0; + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + return 0; +} + +#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8) +#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8) + +static struct { + int rate; + int sample_rate; + int dsp_clk; + unsigned long aclk_rate; +} const sample_rates[] = { + { 8000, CLASSD_INTPMR_FRAME_8K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 16000, CLASSD_INTPMR_FRAME_16K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 32000, CLASSD_INTPMR_FRAME_32K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 48000, CLASSD_INTPMR_FRAME_48K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 96000, CLASSD_INTPMR_FRAME_96K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 22050, CLASSD_INTPMR_FRAME_22K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + { 44100, CLASSD_INTPMR_FRAME_44K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + { 88200, CLASSD_INTPMR_FRAME_88K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, +}; + +static int +atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = codec_dai->codec; + int fs; + int i, best, best_val, cur_val, ret; + u32 mask, val; + + fs = params_rate(params); + + best = 0; + best_val = abs(fs - sample_rates[0].rate); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + /* Closest match */ + cur_val = abs(fs - sample_rates[i].rate); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + + dev_dbg(codec->dev, + "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n", + sample_rates[best].rate, sample_rates[best].aclk_rate); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->aclk); + + ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate); + if (ret) + return ret; + + mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK; + val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT) + | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT); + + snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val); + + ret = clk_prepare_enable(dd->aclk); + if (ret) + return ret; + + return clk_prepare_enable(dd->gclk); +} + +static void +atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->aclk); +} + +static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + snd_soc_update_bits(codec, CLASSD_MR, + CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK, + (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) + |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT)); + + return 0; +} + +static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 mask, val; + + mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = mask; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) + | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT); + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + return 0; +} + +static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = { + .digital_mute = atmel_classd_codec_dai_digital_mute, + .startup = atmel_classd_codec_dai_startup, + .shutdown = atmel_classd_codec_dai_shutdown, + .hw_params = atmel_classd_codec_dai_hw_params, + .prepare = atmel_classd_codec_dai_prepare, + .trigger = atmel_classd_codec_dai_trigger, +}; + +#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi" + +static struct snd_soc_dai_driver atmel_classd_codec_dai = { + .name = ATMEL_CLASSD_CODEC_DAI_NAME, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ATMEL_CLASSD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &atmel_classd_codec_dai_ops, +}; + +/* ASoC sound card */ +static int atmel_classd_asoc_card_init(struct device *dev, + struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + struct atmel_classd *dd = snd_soc_card_get_drvdata(card); + + dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL); + if (!dai_link) + return -ENOMEM; + + dai_link->name = "CLASSD"; + dai_link->stream_name = "CLASSD PCM"; + dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME; + dai_link->cpu_dai_name = dev_name(dev); + dai_link->codec_name = dev_name(dev); + dai_link->platform_name = dev_name(dev); + + card->dai_link = dai_link; + card->num_links = 1; + card->name = dd->pdata->card_name; + card->dev = dev; + + return 0; +}; + +/* regmap configuration */ +static const struct reg_default atmel_classd_reg_defaults[] = { + { CLASSD_INTPMR, 0x00301212 }, +}; + +#define ATMEL_CLASSD_REG_MAX 0xE4 +static const struct regmap_config atmel_classd_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_CLASSD_REG_MAX, + + .cache_type = REGCACHE_FLAT, + .reg_defaults = atmel_classd_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults), +}; + +static int atmel_classd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atmel_classd *dd; + struct resource *res; + void __iomem *io_base; + const struct atmel_classd_pdata *pdata; + struct snd_soc_card *card; + int ret; + + pdata = dev_get_platdata(dev); + if (!pdata) { + pdata = atmel_classd_dt_init(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + dd->pdata = pdata; + + dd->irq = platform_get_irq(pdev, 0); + if (dd->irq < 0) { + ret = dd->irq; + dev_err(dev, "failed to could not get irq: %d\n", ret); + return ret; + } + + dd->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dd->pclk)) { + ret = PTR_ERR(dd->pclk); + dev_err(dev, "failed to get peripheral clock: %d\n", ret); + return ret; + } + + dd->gclk = devm_clk_get(dev, "gclk"); + if (IS_ERR(dd->gclk)) { + ret = PTR_ERR(dd->gclk); + dev_err(dev, "failed to get GCK clock: %d\n", ret); + return ret; + } + + dd->aclk = devm_clk_get(dev, "aclk"); + if (IS_ERR(dd->aclk)) { + ret = PTR_ERR(dd->aclk); + dev_err(dev, "failed to get audio clock: %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no memory resource\n"); + return -ENXIO; + } + + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) { + ret = PTR_ERR(io_base); + dev_err(dev, "failed to remap register memory: %d\n", ret); + return ret; + } + + dd->phy_base = res->start; + + dd->regmap = devm_regmap_init_mmio(dev, io_base, + &atmel_classd_regmap_config); + if (IS_ERR(dd->regmap)) { + ret = PTR_ERR(dd->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, + &atmel_classd_cpu_dai_component, + &atmel_classd_cpu_dai, 1); + if (ret) { + dev_err(dev, "could not register CPU DAI: %d\n", ret); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(dev, + &atmel_classd_dmaengine_pcm_config, + 0); + if (ret) { + dev_err(dev, "could not register platform: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(dev, &soc_codec_dev_classd, + &atmel_classd_codec_dai, 1); + if (ret) { + dev_err(dev, "could not register codec: %d\n", ret); + return ret; + } + + /* register sound card */ + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, dd); + platform_set_drvdata(pdev, card); + + ret = atmel_classd_asoc_card_init(dev, card); + if (ret) { + dev_err(dev, "failed to init sound card\n"); + return ret; + } + + ret = devm_snd_soc_register_card(dev, card); + if (ret) { + dev_err(dev, "failed to register sound card: %d\n", ret); + return ret; + } + + return 0; +} + +static int atmel_classd_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver atmel_classd_driver = { + .driver = { + .name = "atmel-classd", + .of_match_table = of_match_ptr(atmel_classd_of_match), + .pm = &snd_soc_pm_ops, + }, + .probe = atmel_classd_probe, + .remove = atmel_classd_remove, +}; +module_platform_driver(atmel_classd_driver); + +MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture"); +MODULE_AUTHOR("Songjun Wu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h new file mode 100644 index 0000000..73f8fdd --- /dev/null +++ b/sound/soc/atmel/atmel-classd.h @@ -0,0 +1,120 @@ +#ifndef __ATMEL_CLASSD_H_ +#define __ATMEL_CLASSD_H_ + +#define CLASSD_CR 0x00000000 +#define CLASSD_CR_RESET 0x1 + +#define CLASSD_MR 0x00000004 + +#define CLASSD_MR_LEN_DIS 0x0 +#define CLASSD_MR_LEN_EN 0x1 +#define CLASSD_MR_LEN_MASK (0x1 << 0) +#define CLASSD_MR_LEN_SHIFT (0) + +#define CLASSD_MR_LMUTE_DIS 0x0 +#define CLASSD_MR_LMUTE_EN 0x1 +#define CLASSD_MR_LMUTE_SHIFT (0x1) +#define CLASSD_MR_LMUTE_MASK (0x1 << 1) + +#define CLASSD_MR_REN_DIS 0x0 +#define CLASSD_MR_REN_EN 0x1 +#define CLASSD_MR_REN_MASK (0x1 << 4) +#define CLASSD_MR_REN_SHIFT (4) + +#define CLASSD_MR_RMUTE_DIS 0x0 +#define CLASSD_MR_RMUTE_EN 0x1 +#define CLASSD_MR_RMUTE_SHIFT (0x5) +#define CLASSD_MR_RMUTE_MASK (0x1 << 5) + +#define CLASSD_MR_PWMTYP_SINGLE 0x0 +#define CLASSD_MR_PWMTYP_DIFF 0x1 +#define CLASSD_MR_PWMTYP_MASK (0x1 << 8) +#define CLASSD_MR_PWMTYP_SHIFT (8) + +#define CLASSD_MR_NON_OVERLAP_DIS 0x0 +#define CLASSD_MR_NON_OVERLAP_EN 0x1 +#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16) +#define CLASSD_MR_NON_OVERLAP_SHIFT (16) + +#define CLASSD_MR_NOVR_VAL_5NS 0x0 +#define CLASSD_MR_NOVR_VAL_10NS 0x1 +#define CLASSD_MR_NOVR_VAL_15NS 0x2 +#define CLASSD_MR_NOVR_VAL_20NS 0x3 +#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20) +#define CLASSD_MR_NOVR_VAL_SHIFT (20) + +#define CLASSD_INTPMR 0x00000008 + +#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0) +#define CLASSD_INTPMR_ATTL_SHIFT (0) +#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8) +#define CLASSD_INTPMR_ATTR_SHIFT (8) + +#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0 +#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1 +#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16) +#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16) + +#define CLASSD_INTPMR_DEEMP_DIS 0x0 +#define CLASSD_INTPMR_DEEMP_EN 0x1 +#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18) +#define CLASSD_INTPMR_DEEMP_SHIFT (18) + +#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0 +#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1 +#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19) +#define CLASSD_INTPMR_SWAP_SHIFT (19) + +#define CLASSD_INTPMR_FRAME_8K 0x0 +#define CLASSD_INTPMR_FRAME_16K 0x1 +#define CLASSD_INTPMR_FRAME_32K 0x2 +#define CLASSD_INTPMR_FRAME_48K 0x3 +#define CLASSD_INTPMR_FRAME_96K 0x4 +#define CLASSD_INTPMR_FRAME_22K 0x5 +#define CLASSD_INTPMR_FRAME_44K 0x6 +#define CLASSD_INTPMR_FRAME_88K 0x7 +#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20) +#define CLASSD_INTPMR_FRAME_SHIFT (20) + +#define CLASSD_INTPMR_EQCFG_FLAT 0x0 +#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1 +#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2 +#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3 +#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4 +#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5 +#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6 +#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7 +#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8 +#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9 +#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa +#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb +#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc +#define CLASSD_INTPMR_EQCFG_SHIFT (24) + +#define CLASSD_INTPMR_MONO_DIS 0x0 +#define CLASSD_INTPMR_MONO_EN 0x1 +#define CLASSD_INTPMR_MONO_MASK (0x1 << 28) +#define CLASSD_INTPMR_MONO_SHIFT (28) + +#define CLASSD_INTPMR_MONO_MODE_MIX 0x0 +#define CLASSD_INTPMR_MONO_MODE_SAT 0x1 +#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2 +#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3 +#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29) +#define CLASSD_INTPMR_MONO_MODE_SHIFT (29) + +#define CLASSD_INTSR 0x0000000c + +#define CLASSD_THR 0x00000010 + +#define CLASSD_IER 0x00000014 + +#define CLASSD_IDR 0x00000018 + +#define CLASSD_IMR 0x0000001c + +#define CLASSD_ISR 0x00000020 + +#define CLASSD_WPMR 0x000000e4 + +#endif -- cgit v1.1 From 4c9c018b2ac72e6ffaeae472723023dc4fd99a88 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Thu, 8 Oct 2015 20:40:07 +0800 Subject: ASoC: rockchip: i2s: add 8 channels capture support support max 8 channels capture, please add property 'rockchip,capture-channels' in dts to enable this, if not, support 2 channels capture default. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 40 +++++++++++++++++++++++++++++++++++++-- sound/soc/rockchip/rockchip_i2s.h | 10 ++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b936102..f07833b 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -245,8 +245,34 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val); - regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val); + switch (params_channels(params)) { + case 8: + val |= I2S_CHN_8; + break; + case 6: + val |= I2S_CHN_6; + break; + case 4: + val |= I2S_CHN_4; + break; + case 2: + val |= I2S_CHN_2; + break; + default: + dev_err(i2s->dev, "invalid channel: %d\n", + params_channels(params)); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + regmap_update_bits(i2s->regmap, I2S_RXCR, + I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, + val); + else + regmap_update_bits(i2s->regmap, I2S_TXCR, + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, + val); + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, I2S_DMACR_TDL(16)); regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, @@ -415,10 +441,12 @@ static const struct regmap_config rockchip_i2s_regmap_config = { static int rockchip_i2s_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; struct rk_i2s_dev *i2s; struct resource *res; void __iomem *regs; int ret; + int val; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) { @@ -475,6 +503,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_pm_disable; } + /* refine capture channels */ + if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) { + if (val >= 2 && val <= 8) + rockchip_i2s_dai.capture.channels_max = val; + else + rockchip_i2s_dai.capture.channels_max = 2; + } + ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_i2s_component, &rockchip_i2s_dai, 1); diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index 93f456f..a54ee35 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -49,6 +49,9 @@ * RXCR * receive operation control register */ +#define I2S_RXCR_CSR_SHIFT 15 +#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT) +#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT) #define I2S_RXCR_HWT BIT(14) #define I2S_RXCR_SJM_SHIFT 12 #define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT) @@ -207,6 +210,13 @@ enum { ROCKCHIP_DIV_BCLK, }; +/* channel select */ +#define I2S_CSR_SHIFT 15 +#define I2S_CHN_2 (0 << I2S_CSR_SHIFT) +#define I2S_CHN_4 (1 << I2S_CSR_SHIFT) +#define I2S_CHN_6 (2 << I2S_CSR_SHIFT) +#define I2S_CHN_8 (3 << I2S_CSR_SHIFT) + /* I2S REGS */ #define I2S_TXCR (0x0000) #define I2S_RXCR (0x0004) -- cgit v1.1 From b3f2dcddd576a2a6e59c407109610206c4062c8f Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Thu, 8 Oct 2015 20:40:09 +0800 Subject: ASoC: rockchip: i2s: share tx/rx lrck when symmetric_rates enabled share lrck_tx to lrck_rx when symmetric_rates enabled. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 8 ++++++++ sound/soc/rockchip/rockchip_i2s.h | 6 ++++++ 2 files changed, 14 insertions(+) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index f07833b..58ee645 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -226,6 +226,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rk_i2s_dev *i2s = to_info(dai); + struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned int val = 0; switch (params_format(params)) { @@ -278,6 +279,13 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, I2S_DMACR_RDL(16)); + val = I2S_CKR_TRCM_TXRX; + if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates) + val = I2S_CKR_TRCM_TXSHARE; + + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_TRCM_MASK, + val); return 0; } diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index a54ee35..dc6e2c7 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -78,6 +78,12 @@ * CKR * clock generation register */ +#define I2S_CKR_TRCM_SHIFT 28 +#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT) #define I2S_CKR_MSS_SHIFT 27 #define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT) #define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT) -- cgit v1.1 From 4901aa065bced55be5ae55b58fc032bfc727fdda Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Oct 2015 19:13:45 +0800 Subject: ASoC: Intel: Skylake: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_rt286.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 488c98e..a73a431 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -240,18 +240,11 @@ static int skylake_audio_probe(struct platform_device *pdev) { skylake_rt286.dev = &pdev->dev; - return snd_soc_register_card(&skylake_rt286); -} - -static int skylake_audio_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&skylake_rt286); - return 0; + return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286); } static struct platform_driver skylake_audio = { .probe = skylake_audio_probe, - .remove = skylake_audio_remove, .driver = { .name = "skl_alc286s_i2s", }, -- cgit v1.1 From de687fb133f7ede18f026feefbeab3106081148d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 22 Oct 2015 10:43:21 +0200 Subject: ASoC: h1940_uda1380: Constify rate constraints The rate constraints in this board driver are not modified at runtime, so make them const. While we are at it also remove the unnecessary 0 initializer for the mask field. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/samsung/h1940_uda1380.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index c72e9fb..5f5825f 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -26,16 +26,15 @@ #include #include "s3c24xx-i2s.h" -static unsigned int rates[] = { +static const unsigned int rates[] = { 11025, 22050, 44100, }; -static struct snd_pcm_hw_constraint_list hw_rates = { +static const struct snd_pcm_hw_constraint_list hw_rates = { .count = ARRAY_SIZE(rates), .list = rates, - .mask = 0, }; static struct snd_soc_jack hp_jack; -- cgit v1.1 From 7a5b8ba412b6217de8badaa52283588e50eeae08 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 22 Oct 2015 10:43:22 +0200 Subject: ASoC: rx1950_uda1380: Constify rate constraints The rate constraints in this board driver are not modified at runtime, so make them const. While we are at it also remove the unnecessary 0 initializer for the mask field. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/samsung/rx1950_uda1380.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 35e37c4..fa096ab 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -38,16 +38,15 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, static int rx1950_spk_power(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -static unsigned int rates[] = { +static const unsigned int rates[] = { 16000, 44100, 48000, }; -static struct snd_pcm_hw_constraint_list hw_rates = { +static const struct snd_pcm_hw_constraint_list hw_rates = { .count = ARRAY_SIZE(rates), .list = rates, - .mask = 0, }; static struct snd_soc_jack hp_jack; -- cgit v1.1 From 444c37aeee46db70ce01199863dc046fa68e88b7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 22 Oct 2015 10:43:23 +0200 Subject: ASoC: fsl_sai: Constify rate constraints The rate constraints in this driver are shared between all device instances. It should not be (and is not) modified at runtime, so make them const. While we are at it also change the type of the rates array from u32 to unsigned int. While both resolve to the same type, u32 is usually used to empathize that the value is for a 32-bit hardware register or similar which makes it slightly confusing in this context. Signed-off-by: Lars-Peter Clausen Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 9366b5a..2150f5a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -27,13 +27,13 @@ #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ FSL_SAI_CSR_FEIE) -static u32 fsl_sai_rates[] = { +static const unsigned int fsl_sai_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 }; -static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { +static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { .count = ARRAY_SIZE(fsl_sai_rates), .list = fsl_sai_rates, }; -- cgit v1.1 From 9270b7b92615324ecda2b3fa0be98b4ab6c3e361 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:34 +0530 Subject: ASoC: Intel: Skylake: Fix return for pm_runtime_get_sync() pm_runtime_get_sync() will return a negative value in case of error and can return postive value for success. The return check for pm_runtime_get_sync() must be less than 0, so fix it Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a8f53da..a2f94ce 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -116,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); ret = pm_runtime_get_sync(dai->dev); - if (ret) + if (ret < 0) return ret; stream = snd_hdac_ext_stream_assign(ebus, substream, -- cgit v1.1 From bc03281a5c706f6372dfc2100b04aa4055a15c88 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:35 +0530 Subject: ASoC: Intel: Skylake: Remove BE copier blob memcpy The BE copier private data allowed endpoint configuration blobs, now these are queried from BIOS, we don't need to copy the blob, but only capability. Removing the blob from private data will not allocate memory for module specific config in which case memcpy will fail. Fix is to assign the ptr queried from the NHLT table for the endpoint configuration. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a5d8420..7c92085 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -926,7 +926,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, params->s_freq, params->stream); if (cfg) { mconfig->formats_config.caps_size = cfg->size; - memcpy(mconfig->formats_config.caps, &cfg->caps, cfg->size); + mconfig->formats_config.caps = (u32 *) &cfg->caps; } else { dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", mconfig->vbus_id, link_type, -- cgit v1.1 From f0900eb213e0f6f37e1567531f9604b9bed6771d Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 22 Oct 2015 23:22:36 +0530 Subject: ASoC: Intel: Skylake: Fix to use correct macros for the path iteration In case of playback, for the BE dai source path should be iterated to find the pipe params. With sink path iterated, this resulted in a loop and kernel panic with page request failure. Similar are the cases for Capture and FE dais. Using correct macros to fix the panic Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 7c92085..0fba39d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -844,7 +844,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { w = dai->playback_widget; - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && p->sink->power && is_skl_dsp_widget_type(p->sink)) continue; @@ -857,7 +857,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) } } else { w = dai->capture_widget; - snd_soc_dapm_widget_for_each_sink_path(w, p) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && p->source->power && is_skl_dsp_widget_type(p->source)) continue; @@ -945,7 +945,7 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, { struct snd_soc_dapm_path *p; - snd_soc_dapm_widget_for_each_sink_path(w, p) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { @@ -969,7 +969,7 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, { struct snd_soc_dapm_path *p = NULL; - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { -- cgit v1.1 From 4d8adccb220ca270cfcdd80752618095fdc9990c Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 22 Oct 2015 23:22:37 +0530 Subject: ASoC: Intel: Skylake: Fix to fill all sink/source pipe params Currently params only for first copier widget identified in the source/sink path is queried from NHLT. In the dapm route the playback/capture widget may be connected to more than one copier widget. This patch adds return check to return only for any error case. Signed-off-by: Subhransu S. Prusty Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 0fba39d..68e1b00 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -944,48 +944,60 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, struct skl_pipe_params *params) { struct snd_soc_dapm_path *p; + int ret = -EIO; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { - if (!p->source->power) - return skl_tplg_be_fill_pipe_params( + if (!p->source->power) { + ret = skl_tplg_be_fill_pipe_params( dai, p->source->priv, params); - else + if (ret < 0) + return ret; + } else { return -EBUSY; + } } else { - return skl_tplg_be_set_src_pipe_params( + ret = skl_tplg_be_set_src_pipe_params( dai, p->source, params); + if (ret < 0) + return ret; } } - return -EIO; + return ret; } static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) { struct snd_soc_dapm_path *p = NULL; + int ret = -EIO; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { - if (!p->sink->power) - return skl_tplg_be_fill_pipe_params( + if (!p->sink->power) { + ret = skl_tplg_be_fill_pipe_params( dai, p->sink->priv, params); - else + if (ret < 0) + return ret; + } else { return -EBUSY; + } } else { - return skl_tplg_be_set_sink_pipe_params( + ret = skl_tplg_be_set_sink_pipe_params( dai, p->sink, params); + if (ret < 0) + return ret; } } - return -EIO; + return ret; } /* -- cgit v1.1 From d7b188131c4823a85d17b992083bf8a8c32a2be3 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:38 +0530 Subject: ASoC: Intel: Skylake: Update for ssp node index in copier cfg DSP firmware has interface change for SSP node index structure. New FW interface removes the dual_mono field and adds 4 bits for TDM slot group index. This patch updates the ssp dma to align with the DSP firmware structure. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 7 ++++--- sound/soc/intel/skylake/skl-topology.h | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 826d4fd..77c0736 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -303,6 +303,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, struct skl_cpr_cfg *cpr_mconfig) { union skl_connector_node_id node_id = {0}; + union skl_ssp_dma_node ssp_node = {0}; struct skl_pipe_params *params = mconfig->pipe->p_params; switch (mconfig->dev_type) { @@ -320,9 +321,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_I2S_LINK_OUTPUT_CLASS : SKL_DMA_I2S_LINK_INPUT_CLASS; - node_id.node.vindex = params->host_dma_id + - (mconfig->time_slot << 1) + - (mconfig->vbus_id << 3); + ssp_node.dma_node.time_slot_index = mconfig->time_slot; + ssp_node.dma_node.i2s_instance = mconfig->vbus_id; + node_id.node.vindex = ssp_node.val; break; case SKL_DEVICE_DMIC: diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 0c83dc3..2399535 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -153,8 +153,7 @@ enum skl_dma_type { union skl_ssp_dma_node { u8 val; struct { - u8 dual_mono:1; - u8 time_slot:3; + u8 time_slot_index:4; u8 i2s_instance:4; } dma_node; }; -- cgit v1.1 From 4e10996ba810031a493d903539f595e053826103 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:39 +0530 Subject: ASoC: Intel: Skylake: Add support to disable module notifications Each FW modules can report underrun/overrun notification from all modules. This patch disables underrun/overrun notification after firmware is loaded. This will be supportted for debug mode only thru debugfs Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 28 ++++++++++++++++++++++++++-- sound/soc/intel/skylake/skl-topology.h | 5 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 77c0736..ca7bbee 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) return 0; } +#define NOTIFICATION_PARAM_ID 3 +#define NOTIFICATION_MASK 0xf + +/* disable notfication for underruns/overruns from firmware module */ +static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) +{ + struct notification_mask mask; + struct skl_ipc_large_config_msg msg = {0}; + + mask.notify = NOTIFICATION_MASK; + mask.enable = enable; + + msg.large_param_id = NOTIFICATION_PARAM_ID; + msg.param_data_size = sizeof(mask); + + skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); +} + int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; @@ -79,7 +97,7 @@ int skl_init_dsp(struct skl *skl) ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, loader_ops, &skl->skl_sst); - + skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); return ret; @@ -122,6 +140,7 @@ int skl_suspend_dsp(struct skl *skl) int skl_resume_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + int ret; /* if ppcap is not supported return 0 */ if (!skl->ebus.ppcap) @@ -131,7 +150,12 @@ int skl_resume_dsp(struct skl *skl) snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); - return skl_dsp_wake(ctx->dsp); + ret = skl_dsp_wake(ctx->dsp); + if (ret < 0) + return ret; + + skl_dsp_enable_notification(skl->skl_sst, false); + return ret; } enum skl_bitdepth skl_get_bit_depth(int params) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 2399535..76053a8 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -129,6 +129,11 @@ struct skl_src_module_cfg { enum skl_s_freq src_cfg; } __packed; +struct notification_mask { + u32 notify; + u32 enable; +} __packed; + struct skl_up_down_mixer_cfg { struct skl_base_cfg base_cfg; enum skl_ch_cfg out_ch_cfg; -- cgit v1.1 From 2ac454ffe6c2cb923711edc8027bbc0e782be457 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:40 +0530 Subject: ASoC: Intel: Skylake: Fix to check return value of dsp init If DSP initialization fails, ipc to disable notification will cause NULL ptr exception as ipc is not initialized. This patch returns error if dsp init fails before sending disable notification ipc. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ca7bbee..a1001a6 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -97,6 +97,9 @@ int skl_init_dsp(struct skl *skl) ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, loader_ops, &skl->skl_sst); + if (ret < 0) + return ret; + skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); -- cgit v1.1 From bfa764accdea017b1c557f43fa5f2c77a0dc3b15 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:41 +0530 Subject: ASoC: Intel: Skylake: Fix to set Non gateway copier cfg If copier instance is connected to a DMA gateway then driver needs to configure the gateway configuration otherwise set it to invalid. This patch sets the non gateway copier node value to invalid. Signed-off-by: Dharageswari.R Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 8 +++++++- sound/soc/intel/skylake/skl-tplg-interface.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index a1001a6..50a1095 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -321,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, (mconfig->formats_config.caps_size) / 4; } +#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF /* * Calculate the gatewat settings required for copier module, type of * gateway and index of gateway to use @@ -367,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, node_id.node.vindex = params->link_dma_id; break; - default: + case SKL_DEVICE_HDAHOST: node_id.node.dma_type = (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_HDA_HOST_OUTPUT_CLASS : SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; + + default: + cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID; + cpr_mconfig->cpr_feature_mask = 0; + return; } cpr_mconfig->gtw_cfg.node_id = node_id.val; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index d14f7fe..718d3d5 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -106,6 +106,7 @@ enum skl_dev_type { SKL_DEVICE_I2S = 0x2, SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, + SKL_DEVICE_HDAHOST = 0x5, SKL_DEVICE_NONE }; -- cgit v1.1 From 6abca1d71b62090821240ee2a4507c94ddd144e9 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:42 +0530 Subject: ASoC: Intel: Skylake: Add support to topology for module static pin Some module pin connection are static and defined by the topology. This patch adds support for static pin definitions in topology widget private data Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 32 +++++++++++++++------------- sound/soc/intel/skylake/skl-tplg-interface.h | 6 ++++-- 2 files changed, 21 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 68e1b00..a7854c8 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1034,18 +1034,17 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { * The topology binary passes the pin info for a module so initialize the pin * info passed into module instance */ -static void skl_fill_module_pin_info(struct device *dev, - struct skl_module_pin *m_pin, - int max_pin) +static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, + struct skl_module_pin *m_pin, + bool is_dynamic, int max_pin) { int i; for (i = 0; i < max_pin; i++) { - m_pin[i].id.module_id = 0; - m_pin[i].id.instance_id = 0; + m_pin[i].id.module_id = dfw_pin[i].module_id; + m_pin[i].id.instance_id = dfw_pin[i].instance_id; m_pin[i].in_use = false; - m_pin[i].is_dynamic = true; - m_pin[i].pin_index = i; + m_pin[i].is_dynamic = is_dynamic; } } @@ -1164,17 +1163,20 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, if (!mconfig->m_in_pin) return -ENOMEM; - mconfig->m_out_pin = devm_kzalloc(bus->dev, - (mconfig->max_in_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); + mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; - skl_fill_module_pin_info(bus->dev, mconfig->m_in_pin, - mconfig->max_in_queue); - skl_fill_module_pin_info(bus->dev, mconfig->m_out_pin, - mconfig->max_out_queue); + skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin, + dfw_config->is_dynamic_in_pin, + mconfig->max_in_queue); + + skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin, + dfw_config->is_dynamic_out_pin, + mconfig->max_out_queue); + if (mconfig->formats_config.caps_size == 0) goto bind_event; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 718d3d5..2bc396d 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -113,8 +113,6 @@ enum skl_dev_type { struct skl_dfw_module_pin { u16 module_id; u16 instance_id; - u8 pin_id; - bool is_dynamic; } __packed; struct skl_dfw_module_fmt { @@ -155,9 +153,13 @@ struct skl_dfw_module { u32 converter; u32 module_type; u32 vbus_id; + u8 is_dynamic_in_pin; + u8 is_dynamic_out_pin; struct skl_dfw_pipe pipe; struct skl_dfw_module_fmt in_fmt; struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; + struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; struct skl_dfw_module_caps caps; } __packed; -- cgit v1.1 From be77b38a3668306bdc0b0c8dff4660c2a8b38d8e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Fri, 23 Oct 2015 14:05:53 +0800 Subject: ASoC: rt5645: Add the HWEQ for the speaker output The patch adds the HWEQ function for the speaker output. User can set the HWEQ parameters using the ALSA binary control byte-by-byte. We use the following struct array to store the HWEQ parameters for implementing as simple as possible. struct rt5645_eq_param_s { unsigned short reg; unsigned short val; }; It supports the variant length of the HWEQ parameters that are required. We add the validating function in the function "rt5645_hweq_put" of the ALSA binary control to avoid the user that puts the invalid parameters. In the HWEQ enable function of speaker event, we also add the validating function to prevent that the invalid parameters are applied to codec. The HWEQ parameters should be controlled by DAPM for a specific sequence, so the parameters will be applied to the codec in the speaker power up event of DAPM, and will be disabled in the speaker power down event of DAPM. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 65ea712..f046bb8 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -42,6 +42,8 @@ #define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING)) +#define RT5645_HWEQ_NUM 57 + static const struct regmap_range_cfg rt5645_ranges[] = { { .name = "PR", @@ -224,6 +226,11 @@ static const struct reg_default rt5645_reg[] = { { 0xff, 0x6308 }, }; +struct rt5645_eq_param_s { + unsigned short reg; + unsigned short val; +}; + static const char *const rt5645_supply_names[] = { "avdd", "cpvdd", @@ -240,6 +247,7 @@ struct rt5645_priv { struct snd_soc_jack *btn_jack; struct delayed_work jack_detect_work; struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)]; + struct rt5645_eq_param_s *eq_param; int codec_type; int sysclk; @@ -477,6 +485,86 @@ static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv, 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0) ); +static int rt5645_hweq_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s); + + return 0; +} + +static int rt5645_hweq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s *eq_param = + (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg); + eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val); + } + + return 0; +} + +static bool rt5645_validate_hweq(unsigned short reg) +{ + if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) | + (reg == RT5645_EQ_CTRL2)) + return true; + + return false; +} + +static int rt5645_hweq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s *eq_param = + (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + eq_param[i].reg = be16_to_cpu(eq_param[i].reg); + eq_param[i].val = be16_to_cpu(eq_param[i].val); + } + + /* The final setting of the table should be RT5645_EQ_CTRL2 */ + for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) { + if (eq_param[i].reg == 0) + continue; + else if (eq_param[i].reg != RT5645_EQ_CTRL2) + return 0; + else + break; + } + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (!rt5645_validate_hweq(eq_param[i].reg) && + eq_param[i].reg != 0) + return 0; + else if (eq_param[i].reg == 0) + break; + } + + memcpy(rt5645->eq_param, eq_param, + RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s)); + + return 0; +} + +#define RT5645_HWEQ(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rt5645_hweq_info, \ + .get = rt5645_hweq_get, \ + .put = rt5645_hweq_put \ +} + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, @@ -541,6 +629,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* I2S2 function select */ SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT, 1, 1), + RT5645_HWEQ("Speaker HWEQ"), }; /** @@ -631,6 +720,22 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source, } +static int rt5645_enable_hweq(struct snd_soc_codec *codec) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (rt5645_validate_hweq(rt5645->eq_param[i].reg)) + regmap_write(rt5645->regmap, rt5645->eq_param[i].reg, + rt5645->eq_param[i].val); + else + break; + } + + return 0; +} + /** * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters * @codec: SoC audio codec device. @@ -1535,6 +1640,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: + rt5645_enable_hweq(codec); snd_soc_update_bits(codec, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L, @@ -1543,6 +1649,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, RT5645_EQ_CTRL2, 0); snd_soc_update_bits(codec, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L, 0); @@ -3057,6 +3164,9 @@ static int rt5645_probe(struct snd_soc_codec *codec) snd_soc_dapm_sync(dapm); } + rt5645->eq_param = devm_kzalloc(codec->dev, + RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL); + return 0; } -- cgit v1.1 From ab57b8e90c658a72944c48a81a1352e61e84db02 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Fri, 23 Oct 2015 17:15:41 -0400 Subject: ASoC: dwc: add check for master/slave format DW i2s controller's master/slave config can be read from a read-only register. Machine driver can try to set a master/slave format on cpu-dai using 'set_fmt' of dai ops. A check is added to verify codec is master when dwc is slave and vice-versa. Signed-off-by: Maruthi Bayyavarapu Signed-off-by: Alex Deucher Signed-off-by: Mark Brown --- sound/soc/dwc/designware_i2s.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'sound') diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 3a52f82..f427325 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -341,12 +341,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream, return ret; } +static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + if (dev->capability & DW_I2S_SLAVE) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBS_CFS: + if (dev->capability & DW_I2S_MASTER) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + dev_dbg(dev->dev, "dwc : Invalid master/slave format\n"); + ret = -EINVAL; + break; + } + return ret; +} + static struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, + .set_fmt = dw_i2s_set_fmt, }; static const struct snd_soc_component_driver dw_i2s_component = { -- cgit v1.1 From bc86e53a0ae9bb26c1af04034e010d8f22b1b0da Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 24 Oct 2015 14:55:41 +0800 Subject: ASoC: rt5645: Sort the order for register bit defines So we have consistent order for register bit defines. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index d0e0ce5..7cea282 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -619,14 +619,14 @@ #define RT5645_G_OM_L_SM_L_SFT 6 #define RT5645_M_BST1_L_SM_L (0x1 << 5) #define RT5645_M_BST1_L_SM_L_SFT 5 +#define RT5645_M_BST3_L_SM_L (0x1 << 4) +#define RT5645_M_BST3_L_SM_L_SFT 4 #define RT5645_M_IN_L_SM_L (0x1 << 3) #define RT5645_M_IN_L_SM_L_SFT 3 -#define RT5645_M_DAC_L1_SM_L (0x1 << 1) -#define RT5645_M_DAC_L1_SM_L_SFT 1 #define RT5645_M_DAC_L2_SM_L (0x1 << 2) #define RT5645_M_DAC_L2_SM_L_SFT 2 -#define RT5645_M_BST3_L_SM_L (0x1 << 4) -#define RT5645_M_BST3_L_SM_L_SFT 4 +#define RT5645_M_DAC_L1_SM_L (0x1 << 1) +#define RT5645_M_DAC_L1_SM_L_SFT 1 /* SPK Right Mixer Control (0x47) */ #define RT5645_G_RM_R_SM_R_MASK (0x3 << 14) @@ -641,14 +641,14 @@ #define RT5645_G_OM_R_SM_R_SFT 6 #define RT5645_M_BST2_R_SM_R (0x1 << 5) #define RT5645_M_BST2_R_SM_R_SFT 5 +#define RT5645_M_BST3_R_SM_R (0x1 << 4) +#define RT5645_M_BST3_R_SM_R_SFT 4 #define RT5645_M_IN_R_SM_R (0x1 << 3) #define RT5645_M_IN_R_SM_R_SFT 3 -#define RT5645_M_DAC_R1_SM_R (0x1 << 1) -#define RT5645_M_DAC_R1_SM_R_SFT 1 #define RT5645_M_DAC_R2_SM_R (0x1 << 2) #define RT5645_M_DAC_R2_SM_R_SFT 2 -#define RT5645_M_BST3_R_SM_R (0x1 << 4) -#define RT5645_M_BST3_R_SM_R_SFT 4 +#define RT5645_M_DAC_R1_SM_R (0x1 << 1) +#define RT5645_M_DAC_R1_SM_R_SFT 1 /* SPOLMIX Control (0x48) */ #define RT5645_M_DAC_L1_SPM_L (0x1 << 15) @@ -673,12 +673,12 @@ #define RT5645_SPK_G_CLSD_SFT 0 /* Mono Output Mixer Control (0x4c) */ +#define RT5645_G_MONOMIX_MASK (0x1 << 10) +#define RT5645_G_MONOMIX_SFT 10 #define RT5645_M_OV_L_MM (0x1 << 9) #define RT5645_M_OV_L_MM_SFT 9 #define RT5645_M_DAC_L2_MA (0x1 << 8) #define RT5645_M_DAC_L2_MA_SFT 8 -#define RT5645_G_MONOMIX_MASK (0x1 << 10) -#define RT5645_G_MONOMIX_SFT 10 #define RT5645_M_BST2_MM (0x1 << 4) #define RT5645_M_BST2_MM_SFT 4 #define RT5645_M_DAC_R1_MM (0x1 << 3) -- cgit v1.1 From 739146b6144a6541cf94a668d6e7227b7882af30 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 22 Oct 2015 15:56:40 -0700 Subject: ASoC: fsl: Use #ifdef instead of #if for CONFIG_PM_SLEEP Change them to #ifdef as CONFIG_PM_SLEEP might not be defined at all. Reported-by: kbuild test robot Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 2 +- sound/soc/fsl/fsl_sai.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index aab675a..59f234e 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -900,7 +900,7 @@ static const struct of_device_id fsl_esai_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int fsl_esai_suspend(struct device *dev) { struct fsl_esai *esai = dev_get_drvdata(dev); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 38505f3..a4435f5 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -806,7 +806,7 @@ static const struct of_device_id fsl_sai_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int fsl_sai_suspend(struct device *dev) { struct fsl_sai *sai = dev_get_drvdata(dev); -- cgit v1.1