diff options
Diffstat (limited to 'sound')
177 files changed, 13334 insertions, 5082 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 9070f277f8db..09ee8c6b9f75 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -153,7 +153,9 @@ static int snd_pcm_control_ioctl(struct snd_card *card, err = -ENXIO; goto _error; } + mutex_lock(&pcm->open_mutex); err = snd_pcm_info_user(substream, info); + mutex_unlock(&pcm->open_mutex); _error: mutex_unlock(®ister_mutex); return err; diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 9be81025372f..c4eb561d2008 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -163,13 +163,30 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { .width = 32, .phys = 32, .le = 0, .signd = 0, .silence = { 0x69, 0x69, 0x69, 0x69 }, }, - /* FIXME: the following three formats are not defined properly yet */ + /* FIXME: the following two formats are not defined properly yet */ [SNDRV_PCM_FORMAT_MPEG] = { .le = -1, .signd = -1, }, [SNDRV_PCM_FORMAT_GSM] = { .le = -1, .signd = -1, }, + [SNDRV_PCM_FORMAT_S20_LE] = { + .width = 20, .phys = 32, .le = 1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_S20_BE] = { + .width = 20, .phys = 32, .le = 0, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U20_LE] = { + .width = 20, .phys = 32, .le = 1, .signd = 0, + .silence = { 0x00, 0x00, 0x08, 0x00 }, + }, + [SNDRV_PCM_FORMAT_U20_BE] = { + .width = 20, .phys = 32, .le = 0, .signd = 0, + .silence = { 0x00, 0x08, 0x00, 0x00 }, + }, + /* FIXME: the following format is not defined properly yet */ [SNDRV_PCM_FORMAT_SPECIAL] = { .le = -1, .signd = -1, }, diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b3b353d72527..f055ca10bbc1 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -579,15 +579,14 @@ static int snd_rawmidi_info_user(struct snd_rawmidi_substream *substream, return 0; } -int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info) +static int __snd_rawmidi_info_select(struct snd_card *card, + struct snd_rawmidi_info *info) { struct snd_rawmidi *rmidi; struct snd_rawmidi_str *pstr; struct snd_rawmidi_substream *substream; - mutex_lock(®ister_mutex); rmidi = snd_rawmidi_search(card, info->device); - mutex_unlock(®ister_mutex); if (!rmidi) return -ENXIO; if (info->stream < 0 || info->stream > 1) @@ -603,6 +602,16 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info } return -ENXIO; } + +int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info) +{ + int ret; + + mutex_lock(®ister_mutex); + ret = __snd_rawmidi_info_select(card, info); + mutex_unlock(®ister_mutex); + return ret; +} EXPORT_SYMBOL(snd_rawmidi_info_select); static int snd_rawmidi_info_select_user(struct snd_card *card, diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 37d9cfbc29f9..b80985fbc334 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -355,7 +355,7 @@ static int initialize_timer(struct snd_seq_timer *tmr) unsigned long freq; t = tmr->timeri->timer; - if (snd_BUG_ON(!t)) + if (!t) return -EINVAL; freq = tmr->preferred_resolution; diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 31b510c5ca0b..0daf31383084 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -146,7 +146,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) edev = kzalloc(sizeof(*edev), GFP_KERNEL); if (!edev) return -ENOMEM; - hdev = &edev->hdac; + hdev = &edev->hdev; edev->ebus = ebus; snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 038a180d3f81..cbe818eda336 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -325,7 +325,7 @@ static int hdac_component_master_match(struct device *dev, void *data) */ int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops) { - if (WARN_ON(!hdac_acomp)) + if (!hdac_acomp) return -ENODEV; hdac_acomp->audio_ops = aops; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index a81aacf684b2..37e1cf8218ff 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -271,6 +271,8 @@ enum { CXT_FIXUP_HP_SPECTRE, CXT_FIXUP_HP_GATE_MIC, CXT_FIXUP_MUTE_LED_GPIO, + CXT_FIXUP_HEADSET_MIC, + CXT_FIXUP_HP_MIC_NO_PRESENCE, }; /* for hda_fixup_thinkpad_acpi() */ @@ -350,6 +352,18 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec, } } +static void cxt_fixup_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + break; + } +} + /* OPLC XO 1.5 fixup */ /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors) @@ -880,6 +894,19 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_mute_led_gpio, }, + [CXT_FIXUP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_headset_mic, + }, + [CXT_FIXUP_HP_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x02a1113c }, + { } + }, + .chained = true, + .chain_id = CXT_FIXUP_HEADSET_MIC, + }, }; static const struct snd_pci_quirk cxt5045_fixups[] = { @@ -934,6 +961,8 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC), SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index c19c81d230bd..b4f1b6e88305 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -55,10 +55,11 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); #define is_kabylake(codec) ((codec)->core.vendor_id == 0x8086280b) #define is_geminilake(codec) (((codec)->core.vendor_id == 0x8086280d) || \ ((codec)->core.vendor_id == 0x80862800)) +#define is_cannonlake(codec) ((codec)->core.vendor_id == 0x8086280c) #define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \ || is_skylake(codec) || is_broxton(codec) \ - || is_kabylake(codec)) || is_geminilake(codec) - + || is_kabylake(codec)) || is_geminilake(codec) \ + || is_cannonlake(codec) #define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882) #define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883) #define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec)) @@ -3841,6 +3842,7 @@ HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 921a10eff43a..8fd2d9c62c96 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -324,21 +324,24 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0292: alc_update_coef_idx(codec, 0x4, 1<<15, 0); break; - case 0x10ec0215: case 0x10ec0225: + case 0x10ec0295: + case 0x10ec0299: + alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); + /* fallthrough */ + case 0x10ec0215: case 0x10ec0233: case 0x10ec0236: case 0x10ec0255: case 0x10ec0256: + case 0x10ec0257: case 0x10ec0282: case 0x10ec0283: case 0x10ec0286: case 0x10ec0288: case 0x10ec0285: - case 0x10ec0295: case 0x10ec0298: case 0x10ec0289: - case 0x10ec0299: alc_update_coef_idx(codec, 0x10, 1<<9, 0); break; case 0x10ec0275: @@ -2772,6 +2775,7 @@ enum { ALC269_TYPE_ALC298, ALC269_TYPE_ALC255, ALC269_TYPE_ALC256, + ALC269_TYPE_ALC257, ALC269_TYPE_ALC215, ALC269_TYPE_ALC225, ALC269_TYPE_ALC294, @@ -2805,6 +2809,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC298: case ALC269_TYPE_ALC255: case ALC269_TYPE_ALC256: + case ALC269_TYPE_ALC257: case ALC269_TYPE_ALC215: case ALC269_TYPE_ALC225: case ALC269_TYPE_ALC294: @@ -5182,6 +5187,22 @@ static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, } } +/* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */ +static void alc274_fixup_bind_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static hda_nid_t preferred_pairs[] = { + 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02, + 0 + }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + spec->gen.preferred_dacs = preferred_pairs; +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -5299,6 +5320,8 @@ enum { ALC233_FIXUP_LENOVO_MULTI_CODECS, ALC294_FIXUP_LENOVO_MIC_LOCATION, ALC700_FIXUP_INTEL_REFERENCE, + ALC274_FIXUP_DELL_BIND_DACS, + ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, }; static const struct hda_fixup alc269_fixups[] = { @@ -6109,6 +6132,21 @@ static const struct hda_fixup alc269_fixups[] = { {} } }, + [ALC274_FIXUP_DELL_BIND_DACS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_bind_dacs, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC274_FIXUP_DELL_AIO_LINEOUT_VERB] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x0401102f }, + { } + }, + .chained = true, + .chain_id = ALC274_FIXUP_DELL_BIND_DACS + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -6292,6 +6330,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), @@ -6550,6 +6589,11 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1b, 0x01011020}, {0x21, 0x02211010}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x14, 0x90170110}, + {0x1b, 0x01011020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, {0x14, 0x90170120}, {0x21, 0x02211030}), @@ -6575,7 +6619,7 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x14, 0x90170110}, {0x1b, 0x90a70130}, {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, {0x12, 0xb7a60130}, {0x13, 0xb8a61140}, {0x16, 0x90170110}, @@ -6867,6 +6911,10 @@ static int patch_alc269(struct hda_codec *codec) spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/ break; + case 0x10ec0257: + spec->codec_variant = ALC269_TYPE_ALC257; + spec->gen.mixer_nid = 0; + break; case 0x10ec0215: case 0x10ec0285: case 0x10ec0289: @@ -7914,6 +7962,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269), HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269), HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260), HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262), HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index d22758165496..84c3582f3982 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -71,6 +71,7 @@ source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" +source "sound/soc/uniphier/Kconfig" source "sound/soc/ux500/Kconfig" source "sound/soc/xtensa/Kconfig" source "sound/soc/zte/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5327f4d6c668..74cd1858d38b 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ +obj-$(CONFIG_SND_SOC) += uniphier/ obj-$(CONFIG_SND_SOC) += ux500/ obj-$(CONFIG_SND_SOC) += xtensa/ obj-$(CONFIG_SND_SOC) += zte/ diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 9f521a55d610..c33a512283a4 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -850,6 +850,9 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; + if (!rtd) + return -EINVAL; + buffersize = frames_to_bytes(runtime, runtime->buffer_size); bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream); @@ -875,6 +878,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; + if (!rtd) + return -EINVAL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, PLAYBACK_START_DMA_DESCR_CH12, @@ -1051,6 +1056,11 @@ static int acp_audio_probe(struct platform_device *pdev) struct resource *res; const u32 *pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data), GFP_KERNEL); if (audio_drv_data == NULL) @@ -1058,6 +1068,8 @@ static int acp_audio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); audio_drv_data->acp_mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(audio_drv_data->acp_mmio)) + return PTR_ERR(audio_drv_data->acp_mmio); /* The following members gets populated in device 'open' * function. Till then interrupts are disabled in 'acp_init' @@ -1084,7 +1096,11 @@ static int acp_audio_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, audio_drv_data); /* Initialize the ACP */ - acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type); + status = acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type); + if (status) { + dev_err(&pdev->dev, "ACP Init failed status:%d\n", status); + return status; + } status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform); if (status != 0) { @@ -1101,9 +1117,12 @@ static int acp_audio_probe(struct platform_device *pdev) static int acp_audio_remove(struct platform_device *pdev) { + int status; struct audio_drv_data *adata = dev_get_drvdata(&pdev->dev); - acp_deinit(adata->acp_mmio); + status = acp_deinit(adata->acp_mmio); + if (status) + dev_err(&pdev->dev, "ACP Deinit failed status:%d\n", status); snd_soc_unregister_platform(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1113,9 +1132,14 @@ static int acp_audio_remove(struct platform_device *pdev) static int acp_pcm_resume(struct device *dev) { u16 bank; + int status; struct audio_drv_data *adata = dev_get_drvdata(dev); - acp_init(adata->acp_mmio, adata->asic_type); + status = acp_init(adata->acp_mmio, adata->asic_type); + if (status) { + dev_err(dev, "ACP Init failed status:%d\n", status); + return status; + } if (adata->play_stream && adata->play_stream->runtime) { /* For Stoney, Memory gating is disabled,i.e SRAM Banks @@ -1147,18 +1171,26 @@ static int acp_pcm_resume(struct device *dev) static int acp_pcm_runtime_suspend(struct device *dev) { + int status; struct audio_drv_data *adata = dev_get_drvdata(dev); - acp_deinit(adata->acp_mmio); + status = acp_deinit(adata->acp_mmio); + if (status) + dev_err(dev, "ACP Deinit failed status:%d\n", status); acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); return 0; } static int acp_pcm_runtime_resume(struct device *dev) { + int status; struct audio_drv_data *adata = dev_get_drvdata(dev); - acp_init(adata->acp_mmio, adata->asic_type); + status = acp_init(adata->acp_mmio, adata->asic_type); + if (status) { + dev_err(dev, "ACP Init failed status:%d\n", status); + return status; + } acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); return 0; } diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 4a56f3dfba51..dcee145dd179 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -64,7 +64,7 @@ config SND_AT91_SOC_SAM9X5_WM8731 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 SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO help Say Y if you want to add support for Atmel ASoC driver for boards using diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 8445edd06737..ebabed69f0e6 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -308,15 +308,9 @@ static int atmel_classd_codec_resume(struct snd_soc_codec *codec) return regcache_sync(dd->regmap); } -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, .resume = atmel_classd_codec_resume, - .get_regmap = atmel_classd_codec_get_remap, .component_driver = { .controls = atmel_classd_snd_controls, .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index d7469cdd90dc..98f93e79c654 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -110,16 +110,15 @@ static const struct snd_soc_dapm_route intercon[] = { static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct device *dev = rtd->dev; int ret; - printk(KERN_DEBUG - "at91sam9g20ek_wm8731 " - ": at91sam9g20ek_wm8731_init() called\n"); + dev_dbg(dev, "%s called\n", __func__); ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK, - MCLK_RATE, SND_SOC_CLOCK_IN); + MCLK_RATE, SND_SOC_CLOCK_IN); if (ret < 0) { - printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); + dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret); return ret; } @@ -179,21 +178,21 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) */ mclk = clk_get(NULL, "pck0"); if (IS_ERR(mclk)) { - printk(KERN_ERR "ASoC: Failed to get MCLK\n"); + dev_err(&pdev->dev, "Failed to get MCLK\n"); ret = PTR_ERR(mclk); goto err; } pllb = clk_get(NULL, "pllb"); if (IS_ERR(pllb)) { - printk(KERN_ERR "ASoC: Failed to get PLLB\n"); + dev_err(&pdev->dev, "Failed to get PLLB\n"); ret = PTR_ERR(pllb); goto err_mclk; } ret = clk_set_parent(mclk, pllb); clk_put(pllb); if (ret != 0) { - printk(KERN_ERR "ASoC: Failed to set MCLK parent\n"); + dev_err(&pdev->dev, "Failed to set MCLK parent\n"); goto err_mclk; } @@ -236,7 +235,7 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) ret = snd_soc_register_card(card); if (ret) { - printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n"); + dev_err(&pdev->dev, "snd_soc_register_card() failed\n"); } return ret; diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index bbf7a9266a99..cd5a939ad608 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -365,7 +365,7 @@ static int ep93xx_ac97_probe(struct platform_device *pdev) { struct ep93xx_ac97_info *info; struct resource *res; - unsigned int irq; + int irq; int ret; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); @@ -378,8 +378,8 @@ static int ep93xx_ac97_probe(struct platform_device *pdev) return PTR_ERR(info->regs); irq = platform_get_irq(pdev, 0); - if (!irq) - return -ENODEV; + if (irq <= 0) + return irq < 0 ? irq : -ENODEV; ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt, IRQF_TRIGGER_HIGH, pdev->name, info); diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 848c5fe49bc7..be8ea723dff9 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1319,6 +1319,7 @@ static int pm860x_probe(struct snd_soc_codec *codec) int i, ret; pm860x->codec = codec; + snd_soc_codec_init_regmap(codec, pm860x->regmap); for (i = 0; i < 4; i++) { ret = request_threaded_irq(pm860x->irq[i], NULL, @@ -1348,18 +1349,10 @@ static int pm860x_remove(struct snd_soc_codec *codec) return 0; } -static struct regmap *pm860x_get_regmap(struct device *dev) -{ - struct pm860x_priv *pm860x = dev_get_drvdata(dev); - - return pm860x->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_pm860x = { .probe = pm860x_probe, .remove = pm860x_remove, .set_bias_level = pm860x_set_bias_level, - .get_regmap = pm860x_get_regmap, .component_driver = { .controls = pm860x_snd_controls, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a42ddbc93f3d..2b331f7266ab 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -95,6 +95,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98926 if I2C select SND_SOC_MAX98927 if I2C + select SND_SOC_MAX98373 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9860 if I2C select SND_SOC_MAX9768 if I2C @@ -109,6 +110,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM1681 if I2C select SND_SOC_PCM179X_I2C if I2C select SND_SOC_PCM179X_SPI if SPI_MASTER + select SND_SOC_PCM186X_I2C if I2C + select SND_SOC_PCM186X_SPI if SPI_MASTER select SND_SOC_PCM3008 select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_SPI if SPI_MASTER @@ -133,7 +136,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SIRF_AUDIO_CODEC - select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF select SND_SOC_SSM2518 if I2C select SND_SOC_SSM2602_SPI if SPI_MASTER @@ -148,6 +150,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5086 if I2C select SND_SOC_TAS571X if I2C select SND_SOC_TAS5720 if I2C + select SND_SOC_TAS6424 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -158,6 +161,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C + select SND_SOC_TSCS42XX if I2C select SND_SOC_TS3A227E if I2C select SND_SOC_TWL4030 if TWL4030_CORE select SND_SOC_TWL6040 if TWL6040_CORE @@ -623,6 +627,10 @@ config SND_SOC_MAX98927 tristate "Maxim Integrated MAX98927 Speaker Amplifier" depends on I2C +config SND_SOC_MAX98373 + tristate "Maxim Integrated MAX98373 Speaker Amplifier" + depends on I2C + config SND_SOC_MAX9850 tristate @@ -661,6 +669,21 @@ config SND_SOC_PCM179X_SPI Enable support for Texas Instruments PCM179x CODEC. Select this if your PCM179x is connected via an SPI bus. +config SND_SOC_PCM186X + tristate + +config SND_SOC_PCM186X_I2C + tristate "Texas Instruments PCM186x CODECs - I2C" + depends on I2C + select SND_SOC_PCM186X + select REGMAP_I2C + +config SND_SOC_PCM186X_SPI + tristate "Texas Instruments PCM186x CODECs - SPI" + depends on SPI_MASTER + select SND_SOC_PCM186X + select REGMAP_SPI + config SND_SOC_PCM3008 tristate @@ -818,9 +841,6 @@ config SND_SOC_SIRF_AUDIO_CODEC tristate "SiRF SoC internal audio codec" select REGMAP_MMIO -config SND_SOC_SN95031 - tristate - config SND_SOC_SPDIF tristate "S/PDIF CODEC" @@ -883,6 +903,13 @@ config SND_SOC_TAS5720 Enable support for Texas Instruments TAS5720L/M high-efficiency mono Class-D audio power amplifiers. +config SND_SOC_TAS6424 + tristate "Texas Instruments TAS6424 Quad-Channel Audio amplifier" + depends on I2C + help + Enable support for Texas Instruments TAS6424 high-efficiency + digital input quad-channel Class-D audio power amplifiers. + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C @@ -913,12 +940,12 @@ config SND_SOC_TLV320AIC32X4 tristate config SND_SOC_TLV320AIC32X4_I2C - tristate + tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C" depends on I2C select SND_SOC_TLV320AIC32X4 config SND_SOC_TLV320AIC32X4_SPI - tristate + tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI" depends on SPI_MASTER select SND_SOC_TLV320AIC32X4 @@ -933,6 +960,13 @@ config SND_SOC_TS3A227E tristate "TI Headset/Mic detect and keypress chip" depends on I2C +config SND_SOC_TSCS42XX + tristate "Tempo Semiconductor TSCS42xx CODEC" + depends on I2C + select REGMAP_I2C + help + Add support for Tempo Semiconductor's TSCS42xx audio CODEC. + config SND_SOC_TWL4030 select MFD_TWL4030_AUDIO tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0001069ce2a7..da1571336f1e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -90,6 +90,7 @@ snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o snd-soc-max98926-objs := max98926.o snd-soc-max98927-objs := max98927.o +snd-soc-max98373-objs := max98373.o snd-soc-max9850-objs := max9850.o snd-soc-max9860-objs := max9860.o snd-soc-mc13783-objs := mc13783.o @@ -105,6 +106,9 @@ snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm179x-codec-objs := pcm179x.o snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o snd-soc-pcm179x-spi-objs := pcm179x-spi.o +snd-soc-pcm186x-objs := pcm186x.o +snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o +snd-soc-pcm186x-spi-objs := pcm186x-spi.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o @@ -140,7 +144,6 @@ snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o snd-soc-si476x-objs := si476x.o snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o -snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-tx-objs := spdif_transmitter.o snd-soc-spdif-rx-objs := spdif_receiver.o snd-soc-ssm2518-objs := ssm2518.o @@ -156,6 +159,7 @@ snd-soc-sti-sas-objs := sti-sas.o snd-soc-tas5086-objs := tas5086.o snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o +snd-soc-tas6424-objs := tas6424.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -167,6 +171,7 @@ snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320dac33-objs := tlv320dac33.o +snd-soc-tscs42xx-objs := tscs42xx.o snd-soc-ts3a227e-objs := ts3a227e.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -330,6 +335,7 @@ obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o +obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o @@ -345,6 +351,9 @@ obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o +obj-$(CONFIG_SND_SOC_PCM186X) += snd-soc-pcm186x.o +obj-$(CONFIG_SND_SOC_PCM186X_I2C) += snd-soc-pcm186x-i2c.o +obj-$(CONFIG_SND_SOC_PCM186X_SPI) += snd-soc-pcm186x-spi.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o @@ -395,6 +404,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o +obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o @@ -406,6 +416,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o +obj-$(CONFIG_SND_SOC_TSCS42XX) += snd-soc-tscs42xx.o obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index b95bb8b52e51..3d1cf4784e87 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -15,6 +15,7 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/of_device.h> @@ -95,6 +96,9 @@ struct ak4613_priv { struct mutex lock; const struct ak4613_interface *iface; struct snd_pcm_hw_constraint_list constraint; + struct work_struct dummy_write_work; + struct snd_soc_component *component; + unsigned int rate; unsigned int sysclk; unsigned int fmt; @@ -392,6 +396,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + priv->rate = rate; /* * FIXME @@ -467,11 +472,83 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec, return 0; } +static void ak4613_dummy_write(struct work_struct *work) +{ + struct ak4613_priv *priv = container_of(work, + struct ak4613_priv, + dummy_write_work); + struct snd_soc_component *component = priv->component; + unsigned int mgmt1; + unsigned int mgmt3; + + /* + * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks + * + * Note + * + * To avoid extra delay, we want to avoid preemption here, + * but we can't. Because it uses I2C access which is using IRQ + * and sleep. Thus, delay might be more than 5 LR clocks + * see also + * ak4613_dai_trigger() + */ + udelay(5000000 / priv->rate); + + snd_soc_component_read(component, PW_MGMT1, &mgmt1); + snd_soc_component_read(component, PW_MGMT3, &mgmt3); + + snd_soc_component_write(component, PW_MGMT1, mgmt1); + snd_soc_component_write(component, PW_MGMT3, mgmt3); +} + +static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + /* + * FIXME + * + * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks + * from Power Down Release. Otherwise, Playback volume will be 0dB. + * To avoid complex multiple delay/dummy_write method from + * ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...), + * call it once here. + * + * But, unfortunately, we can't "write" here because here is atomic + * context (It uses I2C access for writing). + * Thus, use schedule_work() to switching to normal context + * immediately. + * + * Note + * + * Calling ak4613_dummy_write() function might be delayed. + * In such case, ak4613 volume might be temporarily 0dB when + * beggining of playback. + * see also + * ak4613_dummy_write() + */ + + if ((cmd != SNDRV_PCM_TRIGGER_START) && + (cmd != SNDRV_PCM_TRIGGER_RESUME)) + return 0; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + priv->component = &codec->component; + schedule_work(&priv->dummy_write_work); + + return 0; +} + static const struct snd_soc_dai_ops ak4613_dai_ops = { .startup = ak4613_dai_startup, .shutdown = ak4613_dai_shutdown, .set_sysclk = ak4613_dai_set_sysclk, .set_fmt = ak4613_dai_set_fmt, + .trigger = ak4613_dai_trigger, .hw_params = ak4613_dai_hw_params, }; @@ -590,6 +667,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, priv->iface = NULL; priv->cnt = 0; priv->sysclk = 0; + INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write); mutex_init(&priv->lock); diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 6ed2cc374768..3bf93652bb31 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -121,17 +121,19 @@ static struct snd_soc_dai_driver cq93vc_dai = { .ops = &cq93vc_dai_ops, }; -static struct regmap *cq93vc_get_regmap(struct device *dev) +static int cq93vc_probe(struct snd_soc_component *component) { - struct davinci_vc *davinci_vc = dev->platform_data; + struct davinci_vc *davinci_vc = component->dev->platform_data; - return davinci_vc->regmap; + snd_soc_component_init_regmap(component, davinci_vc->regmap); + + return 0; } static const struct snd_soc_codec_driver soc_codec_dev_cq93vc = { .set_bias_level = cq93vc_set_bias_level, - .get_regmap = cq93vc_get_regmap, .component_driver = { + .probe = cq93vc_probe, .controls = cq93vc_snd_controls, .num_controls = ARRAY_SIZE(cq93vc_snd_controls), }, diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 7e9806206648..bc3a72e4c4ed 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -355,13 +355,9 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client, unsigned int devid = 0; unsigned int reg; - - cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private), - GFP_KERNEL); - if (!cs35l32) { - dev_err(&i2c_client->dev, "could not allocate codec\n"); + cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL); + if (!cs35l32) return -ENOMEM; - } i2c_set_clientdata(i2c_client, cs35l32); @@ -375,13 +371,11 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client, if (pdata) { cs35l32->pdata = *pdata; } else { - pdata = devm_kzalloc(&i2c_client->dev, - sizeof(struct cs35l32_platform_data), - GFP_KERNEL); - if (!pdata) { - dev_err(&i2c_client->dev, "could not allocate pdata\n"); + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) return -ENOMEM; - } + if (i2c_client->dev.of_node) { ret = cs35l32_handle_of_data(i2c_client, &cs35l32->pdata); diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index 1e05026bedca..0600d5264c4c 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -1004,13 +1004,9 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client, unsigned int devid = 0; unsigned int reg; - cs35l34 = devm_kzalloc(&i2c_client->dev, - sizeof(struct cs35l34_private), - GFP_KERNEL); - if (!cs35l34) { - dev_err(&i2c_client->dev, "could not allocate codec\n"); + cs35l34 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l34), GFP_KERNEL); + if (!cs35l34) return -ENOMEM; - } i2c_set_clientdata(i2c_client, cs35l34); cs35l34->regmap = devm_regmap_init_i2c(i2c_client, &cs35l34_regmap); @@ -1044,14 +1040,11 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client, if (pdata) { cs35l34->pdata = *pdata; } else { - pdata = devm_kzalloc(&i2c_client->dev, - sizeof(struct cs35l34_platform_data), - GFP_KERNEL); - if (!pdata) { - dev_err(&i2c_client->dev, - "could not allocate pdata\n"); + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) return -ENOMEM; - } + if (i2c_client->dev.of_node) { ret = cs35l34_handle_of_data(i2c_client, pdata); if (ret != 0) diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 0d9c4a57301b..9731e5dff291 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1100,8 +1100,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, unsigned int reg; u32 val32; - cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private), - GFP_KERNEL); + cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l52), GFP_KERNEL); if (cs42l52 == NULL) return -ENOMEM; cs42l52->dev = &i2c_client->dev; @@ -1115,13 +1114,11 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, if (pdata) { cs42l52->pdata = *pdata; } else { - pdata = devm_kzalloc(&i2c_client->dev, - sizeof(struct cs42l52_platform_data), - GFP_KERNEL); - if (!pdata) { - dev_err(&i2c_client->dev, "could not allocate pdata\n"); + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) return -ENOMEM; - } + if (i2c_client->dev.of_node) { if (of_property_read_bool(i2c_client->dev.of_node, "cirrus,mica-differential-cfg")) diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index cb6ca85f1536..fd7b8d32c2b2 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1190,9 +1190,7 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client, unsigned int alpha_rev, metal_rev; unsigned int reg; - cs42l56 = devm_kzalloc(&i2c_client->dev, - sizeof(struct cs42l56_private), - GFP_KERNEL); + cs42l56 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l56), GFP_KERNEL); if (cs42l56 == NULL) return -ENOMEM; cs42l56->dev = &i2c_client->dev; @@ -1207,14 +1205,11 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client, if (pdata) { cs42l56->pdata = *pdata; } else { - pdata = devm_kzalloc(&i2c_client->dev, - sizeof(struct cs42l56_platform_data), + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&i2c_client->dev, - "could not allocate pdata\n"); + if (!pdata) return -ENOMEM; - } + if (i2c_client->dev.of_node) { ret = cs42l56_handle_of_data(i2c_client, &cs42l56->pdata); diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 3df2c473ab88..dde37e569ade 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1289,8 +1289,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, unsigned int reg; u32 val32; - cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l73_private), - GFP_KERNEL); + cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l73), GFP_KERNEL); if (!cs42l73) return -ENOMEM; @@ -1304,13 +1303,11 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, if (pdata) { cs42l73->pdata = *pdata; } else { - pdata = devm_kzalloc(&i2c_client->dev, - sizeof(struct cs42l73_platform_data), - GFP_KERNEL); - if (!pdata) { - dev_err(&i2c_client->dev, "could not allocate pdata\n"); + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) return -ENOMEM; - } + if (i2c_client->dev.of_node) { if (of_property_read_u32(i2c_client->dev.of_node, "chgfreq", &val32) >= 0) diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 94c0209977d0..be2750680838 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1120,9 +1120,11 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int ret; - priv->core.arizona->dapm = dapm; + arizona->dapm = dapm; + snd_soc_codec_init_regmap(codec, arizona->regmap); ret = arizona_init_spk(codec); if (ret < 0) @@ -1175,17 +1177,9 @@ static unsigned int cs47l24_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_4L, }; -static struct regmap *cs47l24_get_regmap(struct device *dev) -{ - struct cs47l24_priv *priv = dev_get_drvdata(dev); - - return priv->core.arizona->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_cs47l24 = { .probe = cs47l24_codec_probe, .remove = cs47l24_codec_remove, - .get_regmap = cs47l24_get_regmap, .idle_bias_off = true, diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 46b1fbb66eba..6b6f8e44369b 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -26,7 +26,7 @@ struct cx20442_priv { - void *control_data; + struct tty_struct *tty; struct regulator *por; }; @@ -88,17 +88,6 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = { {"ADC", NULL, "Input Mixer"}, }; -static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 *reg_cache = codec->reg_cache; - - if (reg >= codec->driver->reg_cache_size) - return -EINVAL; - - return reg_cache[reg]; -} - enum v253_vls { V253_VLS_NONE = 0, V253_VLS_T, @@ -123,6 +112,8 @@ enum v253_vls { V253_VLS_TEST, }; +#if 0 +/* FIXME : these function will be re-used */ static int cx20442_pm_to_v253_vls(u8 value) { switch (value & ~(1 << CX20442_AGC)) { @@ -163,9 +154,9 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg, if (reg >= codec->driver->reg_cache_size) return -EINVAL; - /* hw_write and control_data pointers required for talking to the modem + /* tty and write pointers required for talking to the modem * are expected to be set by the line discipline initialization code */ - if (!codec->hw_write || !cx20442->control_data) + if (!cx20442->tty || !cx20442->tty->ops->write) return -EIO; old = reg_cache[reg]; @@ -194,12 +185,12 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg, return -ENOMEM; dev_dbg(codec->dev, "%s: %s\n", __func__, buf); - if (codec->hw_write(cx20442->control_data, buf, len) != len) + if (cx20442->tty->ops->write(cx20442->tty, buf, len) != len) return -EIO; return 0; } - +#endif /* * Line discpline related code @@ -252,8 +243,7 @@ static void v253_close(struct tty_struct *tty) cx20442 = snd_soc_codec_get_drvdata(codec); /* Prevent the codec driver from further accessing the modem */ - codec->hw_write = NULL; - cx20442->control_data = NULL; + cx20442->tty = NULL; codec->component.card->pop_time = 0; } @@ -276,12 +266,11 @@ static void v253_receive(struct tty_struct *tty, cx20442 = snd_soc_codec_get_drvdata(codec); - if (!cx20442->control_data) { + if (!cx20442->tty) { /* First modem response, complete setup procedure */ /* Set up codec driver access to modem controls */ - cx20442->control_data = tty; - codec->hw_write = (hw_write_t)tty->ops->write; + cx20442->tty = tty; codec->component.card->pop_time = 1; } } @@ -367,10 +356,9 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec) cx20442->por = regulator_get(codec->dev, "POR"); if (IS_ERR(cx20442->por)) dev_warn(codec->dev, "failed to get the regulator"); - cx20442->control_data = NULL; + cx20442->tty = NULL; snd_soc_codec_set_drvdata(codec, cx20442); - codec->hw_write = NULL; codec->component.card->pop_time = 0; return 0; @@ -381,8 +369,8 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec) { struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec); - if (cx20442->control_data) { - struct tty_struct *tty = cx20442->control_data; + if (cx20442->tty) { + struct tty_struct *tty = cx20442->tty; tty_hangup(tty); } @@ -402,11 +390,7 @@ static const struct snd_soc_codec_driver cx20442_codec_dev = { .probe = cx20442_codec_probe, .remove = cx20442_codec_remove, .set_bias_level = cx20442_set_bias_level, - .reg_cache_default = &cx20442_reg, - .reg_cache_size = 1, - .reg_word_size = sizeof(u8), - .read = cx20442_read_reg_cache, - .write = cx20442_write, + .component_driver = { .dapm_widgets = cx20442_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets), diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 41d9b1da27c2..b2b4e90fc02a 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1654,10 +1654,8 @@ static struct da7213_platform_data u32 fw_val32; pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_warn(codec->dev, "Failed to allocate memory for pdata\n"); + if (!pdata) return NULL; - } if (device_property_read_u32(dev, "dlg,micbias1-lvl", &fw_val32) >= 0) pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, fw_val32); @@ -1855,8 +1853,7 @@ static int da7213_i2c_probe(struct i2c_client *i2c, struct da7213_priv *da7213; int ret; - da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv), - GFP_KERNEL); + da7213 = devm_kzalloc(&i2c->dev, sizeof(*da7213), GFP_KERNEL); if (!da7213) return -ENOMEM; diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index b2d42ec1dcd9..96c644a15b11 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -2455,10 +2455,8 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec) 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"); + if (!pdata) return NULL; - } if (of_property_read_u32(np, "dlg,micbias1-lvl-millivolt", &of_val32) >= 0) pdata->micbias1_lvl = da7218_of_micbias_lvl(codec, of_val32); @@ -2520,15 +2518,13 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec) } if (da7218->dev_id == DA7218_DEV_ID) { - hpldet_np = of_find_node_by_name(np, "da7218_hpldet"); + hpldet_np = of_get_child_by_name(np, "da7218_hpldet"); if (!hpldet_np) return pdata; hpldet_pdata = devm_kzalloc(codec->dev, sizeof(*hpldet_pdata), GFP_KERNEL); if (!hpldet_pdata) { - dev_warn(codec->dev, - "Failed to allocate memory for hpldet pdata\n"); of_node_put(hpldet_np); return pdata; } @@ -3273,8 +3269,7 @@ static int da7218_i2c_probe(struct i2c_client *i2c, struct da7218_priv *da7218; int ret; - da7218 = devm_kzalloc(&i2c->dev, sizeof(struct da7218_priv), - GFP_KERNEL); + da7218 = devm_kzalloc(&i2c->dev, sizeof(*da7218), GFP_KERNEL); if (!da7218) return -ENOMEM; diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index b88a1ee66f80..cf83c423394d 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -107,8 +107,30 @@ static const struct snd_soc_codec_driver soc_dmic = { static int dmic_dev_probe(struct platform_device *pdev) { + int err; + u32 chans; + struct snd_soc_dai_driver *dai_drv = &dmic_dai; + + if (pdev->dev.of_node) { + err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans); + if (err && (err != -EINVAL)) + return err; + + if (!err) { + if (chans < 1 || chans > 8) + return -EINVAL; + + dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + + memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv)); + dai_drv->capture.channels_max = chans; + } + } + return snd_soc_register_codec(&pdev->dev, - &soc_dmic, &dmic_dai, 1); + &soc_dmic, dai_drv, 1); } static int dmic_dev_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index f3b4f4dfae6a..dba6f4c5074a 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -136,8 +136,11 @@ struct hdac_hdmi_priv { struct mutex pin_mutex; struct hdac_chmap chmap; struct hdac_hdmi_drv_data *drv_data; + struct snd_soc_dai_driver *dai_drv; }; +#define hdev_to_hdmi_priv(_hdev) ((to_ehdac_device(_hdev))->private_data) + static struct hdac_hdmi_pcm * hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, struct hdac_hdmi_cvt *cvt) @@ -169,7 +172,7 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, * ports. */ if (pcm->jack_event == 0) { - dev_dbg(&edev->hdac.dev, + dev_dbg(&edev->hdev.dev, "jack report for pcm=%d\n", pcm->pcm_id); snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT, @@ -195,18 +198,18 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, /* * Get the no devices that can be connected to a port on the Pin widget. */ -static int hdac_hdmi_get_port_len(struct hdac_ext_device *hdac, hda_nid_t nid) +static int hdac_hdmi_get_port_len(struct hdac_ext_device *edev, hda_nid_t nid) { unsigned int caps; unsigned int type, param; - caps = get_wcaps(&hdac->hdac, nid); + caps = get_wcaps(&edev->hdev, nid); type = get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) return 0; - param = snd_hdac_read_parm_uncached(&hdac->hdac, nid, + param = snd_hdac_read_parm_uncached(&edev->hdev, nid, AC_PAR_DEVLIST_LEN); if (param == -1) return param; @@ -219,10 +222,10 @@ static int hdac_hdmi_get_port_len(struct hdac_ext_device *hdac, hda_nid_t nid) * id selected on the pin. Return 0 means the first port entry * is selected or MST is not supported. */ -static int hdac_hdmi_port_select_get(struct hdac_ext_device *hdac, +static int hdac_hdmi_port_select_get(struct hdac_ext_device *edev, struct hdac_hdmi_port *port) { - return snd_hdac_codec_read(&hdac->hdac, port->pin->nid, + return snd_hdac_codec_read(&edev->hdev, port->pin->nid, 0, AC_VERB_GET_DEVICE_SEL, 0); } @@ -230,7 +233,7 @@ static int hdac_hdmi_port_select_get(struct hdac_ext_device *hdac, * Sets the selected port entry for the configuring Pin widget verb. * returns error if port set is not equal to port get otherwise success */ -static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac, +static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev, struct hdac_hdmi_port *port) { int num_ports; @@ -239,7 +242,7 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac, return 0; /* AC_PAR_DEVLIST_LEN is 0 based. */ - num_ports = hdac_hdmi_get_port_len(hdac, port->pin->nid); + num_ports = hdac_hdmi_get_port_len(edev, port->pin->nid); if (num_ports < 0) return -EIO; @@ -250,13 +253,13 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac, if (num_ports + 1 < port->id) return 0; - snd_hdac_codec_write(&hdac->hdac, port->pin->nid, 0, + snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, AC_VERB_SET_DEVICE_SEL, port->id); - if (port->id != hdac_hdmi_port_select_get(hdac, port)) + if (port->id != hdac_hdmi_port_select_get(edev, port)) return -EIO; - dev_dbg(&hdac->hdac.dev, "Selected the port=%d\n", port->id); + dev_dbg(&edev->hdev.dev, "Selected the port=%d\n", port->id); return 0; } @@ -276,9 +279,9 @@ static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) { - struct hdac_device *hdac = dev_to_hdac_dev(dev); + struct hdac_device *hdev = dev_to_hdac_dev(dev); - return to_ehdac_device(hdac); + return to_ehdac_device(hdev); } static unsigned int sad_format(const u8 *sad) @@ -321,14 +324,14 @@ format_constraint: } static void -hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid, +hdac_hdmi_set_dip_index(struct hdac_ext_device *edev, hda_nid_t pin_nid, int packet_index, int byte_index) { int val; val = (packet_index << 5) | (byte_index & 0x1f); - snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + snd_hdac_codec_write(&edev->hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); } @@ -344,14 +347,14 @@ struct dp_audio_infoframe { u8 LFEPBL01_LSV36_DM_INH7; }; -static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, +static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev, struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port) { uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; struct hdmi_audio_infoframe frame; struct hdac_hdmi_pin *pin = port->pin; struct dp_audio_infoframe dp_ai; - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_cvt *cvt = pcm->cvt; u8 *dip; int ret; @@ -360,11 +363,11 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, u8 conn_type; int channels, ca; - ca = snd_hdac_channel_allocation(&hdac->hdac, port->eld.info.spk_alloc, + ca = snd_hdac_channel_allocation(&edev->hdev, port->eld.info.spk_alloc, pcm->channels, pcm->chmap_set, true, pcm->chmap); channels = snd_hdac_get_active_channels(ca); - hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt->nid, channels); + hdmi->chmap.ops.set_channel_count(&edev->hdev, cvt->nid, channels); snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, pcm->channels, pcm->chmap, pcm->chmap_set); @@ -397,32 +400,32 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, break; default: - dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n", + dev_err(&edev->hdev.dev, "Invalid connection type: %d\n", conn_type); return -EIO; } /* stop infoframe transmission */ - hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(&edev->hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); /* Fill infoframe. Index auto-incremented */ - hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); + hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { for (i = 0; i < sizeof(buffer); i++) - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + snd_hdac_codec_write(&edev->hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_DATA, buffer[i]); } else { for (i = 0; i < sizeof(dp_ai); i++) - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + snd_hdac_codec_write(&edev->hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_DATA, dip[i]); } /* Start infoframe */ - hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(&edev->hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); return 0; @@ -433,11 +436,11 @@ static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdac.dev, "%s: strm_tag: %d\n", __func__, tx_mask); + dev_dbg(&edev->hdev.dev, "%s: strm_tag: %d\n", __func__, tx_mask); dai_map = &hdmi->dai_map[dai->id]; @@ -452,8 +455,8 @@ static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) { - struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_port *port; struct hdac_hdmi_pcm *pcm; @@ -466,7 +469,7 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, return -ENODEV; if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { - dev_err(&hdac->hdac.dev, + dev_err(&edev->hdev.dev, "device is not configured for this pin:port%d:%d\n", port->pin->nid, port->id); return -ENODEV; @@ -486,28 +489,28 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, return 0; } -static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *hdac, +static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *edev, struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) { - dev_warn(&hdac->hdac.dev, + if (!(get_wcaps(&edev->hdev, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&edev->hdev.dev, "HDMI: pin %d wcaps %#x does not support connection list\n", - pin->nid, get_wcaps(&hdac->hdac, pin->nid)); + pin->nid, get_wcaps(&edev->hdev, pin->nid)); return -EINVAL; } - if (hdac_hdmi_port_select_set(hdac, port) < 0) + if (hdac_hdmi_port_select_set(edev, port) < 0) return -EIO; - port->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, + port->num_mux_nids = snd_hdac_get_connections(&edev->hdev, pin->nid, port->mux_nids, HDA_MAX_CONNECTIONS); if (port->num_mux_nids == 0) - dev_warn(&hdac->hdac.dev, + dev_warn(&edev->hdev.dev, "No connections found for pin:port %d:%d\n", pin->nid, port->id); - dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin:port %d:%d\n", + dev_dbg(&edev->hdev.dev, "num_mux_nids %d for pin:port %d:%d\n", port->num_mux_nids, pin->nid, port->id); return port->num_mux_nids; @@ -565,8 +568,8 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_port *port; @@ -575,7 +578,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; cvt = dai_map->cvt; - port = hdac_hdmi_get_port_from_cvt(hdac, hdmi, cvt); + port = hdac_hdmi_get_port_from_cvt(edev, hdmi, cvt); /* * To make PA and other userland happy. @@ -586,7 +589,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { - dev_warn(&hdac->hdac.dev, + dev_warn(&edev->hdev.dev, "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n", port->eld.monitor_present, port->eld.eld_valid, port->pin->nid, port->id); @@ -608,8 +611,8 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; @@ -630,14 +633,13 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, } static int -hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) +hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) { unsigned int chans; - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int err; - chans = get_wcaps(hdac, cvt->nid); + chans = get_wcaps(hdev, cvt->nid); chans = get_wcaps_channels(chans); cvt->params.channels_min = 2; @@ -646,12 +648,12 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) if (chans > hdmi->chmap.channels_max) hdmi->chmap.channels_max = chans; - err = snd_hdac_query_supported_pcm(hdac, cvt->nid, + err = snd_hdac_query_supported_pcm(hdev, cvt->nid, &cvt->params.rates, &cvt->params.formats, &cvt->params.maxbps); if (err < 0) - dev_err(&hdac->dev, + dev_err(&hdev->dev, "Failed to query pcm params for nid %d: %d\n", cvt->nid, err); @@ -696,7 +698,7 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, struct hdac_hdmi_port *port) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pcm *pcm = NULL; struct hdac_hdmi_port *p; @@ -716,9 +718,9 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, hda_nid_t nid, unsigned int pwr_state) { - if (get_wcaps(&edev->hdac, nid) & AC_WCAP_POWER) { - if (!snd_hdac_check_power_state(&edev->hdac, nid, pwr_state)) - snd_hdac_codec_write(&edev->hdac, nid, 0, + if (get_wcaps(&edev->hdev, nid) & AC_WCAP_POWER) { + if (!snd_hdac_check_power_state(&edev->hdev, nid, pwr_state)) + snd_hdac_codec_write(&edev->hdev, nid, 0, AC_VERB_SET_POWER_STATE, pwr_state); } } @@ -726,8 +728,8 @@ static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, static void hdac_hdmi_set_amp(struct hdac_ext_device *edev, hda_nid_t nid, int val) { - if (get_wcaps(&edev->hdac, nid) & AC_WCAP_OUT_AMP) - snd_hdac_codec_write(&edev->hdac, nid, 0, + if (get_wcaps(&edev->hdev, nid) & AC_WCAP_OUT_AMP) + snd_hdac_codec_write(&edev->hdev, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); } @@ -739,7 +741,7 @@ static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", __func__, w->name, event); pcm = hdac_hdmi_get_pcm(edev, port); @@ -755,7 +757,7 @@ static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D0); /* Enable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, + snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_UNMUTE); @@ -766,7 +768,7 @@ static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_MUTE); /* Disable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, + snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D3); @@ -782,10 +784,10 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, { struct hdac_hdmi_cvt *cvt = w->priv; struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", __func__, w->name, event); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt); @@ -797,23 +799,23 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0); /* Enable transmission */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_1, 1); /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0); - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, pcm->format); break; case SND_SOC_DAPM_POST_PMD: - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3); @@ -831,7 +833,7 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); int mux_idx; - dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", __func__, w->name, event); if (!kc) @@ -844,7 +846,7 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, return -EIO; if (mux_idx > 0) { - snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, + snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); } @@ -864,7 +866,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = w->dapm; struct hdac_hdmi_port *port = w->priv; struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pcm *pcm = NULL; const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; @@ -922,7 +924,7 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, struct snd_soc_dapm_widget *widget, const char *widget_name) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pin *pin = port->pin; struct snd_kcontrol_new *kc; struct hdac_hdmi_cvt *cvt; @@ -934,17 +936,17 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, int i = 0; int num_items = hdmi->num_cvt + 1; - kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL); + kc = devm_kzalloc(&edev->hdev.dev, sizeof(*kc), GFP_KERNEL); if (!kc) return -ENOMEM; - se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL); + se = devm_kzalloc(&edev->hdev.dev, sizeof(*se), GFP_KERNEL); if (!se) return -ENOMEM; snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input", pin->nid, port->id); - kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL); + kc->name = devm_kstrdup(&edev->hdev.dev, kc_name, GFP_KERNEL); if (!kc->name) return -ENOMEM; @@ -962,24 +964,24 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, se->mask = roundup_pow_of_two(se->items) - 1; sprintf(mux_items, "NONE"); - items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&edev->hdev.dev, mux_items, GFP_KERNEL); if (!items[i]) return -ENOMEM; list_for_each_entry(cvt, &hdmi->cvt_list, head) { i++; sprintf(mux_items, "cvt %d", cvt->nid); - items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&edev->hdev.dev, mux_items, GFP_KERNEL); if (!items[i]) return -ENOMEM; } - se->texts = devm_kmemdup(&edev->hdac.dev, items, + se->texts = devm_kmemdup(&edev->hdev.dev, items, (num_items * sizeof(char *)), GFP_KERNEL); if (!se->texts) return -ENOMEM; - return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget, + return hdac_hdmi_fill_widget_info(&edev->hdev.dev, widget, snd_soc_dapm_mux, port, widget_name, NULL, kc, 1, hdac_hdmi_pin_mux_widget_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG); @@ -990,7 +992,7 @@ static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev, struct snd_soc_dapm_widget *widgets, struct snd_soc_dapm_route *route, int rindex) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); const struct snd_kcontrol_new *kc; struct soc_enum *se; int mux_index = hdmi->num_cvt + hdmi->num_ports; @@ -1033,8 +1035,8 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *route; struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv; char widget_name[NAME_SIZE]; struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_pin *pin; @@ -1134,7 +1136,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; int dai_id = 0; @@ -1150,7 +1152,7 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) dai_id++; if (dai_id == HDA_MAX_CVTS) { - dev_warn(&edev->hdac.dev, + dev_warn(&edev->hdev.dev, "Max dais supported: %d\n", dai_id); break; } @@ -1161,7 +1163,7 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_cvt *cvt; char name[NAME_SIZE]; @@ -1176,7 +1178,7 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) list_add_tail(&cvt->head, &hdmi->cvt_list); hdmi->num_cvt++; - return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); + return hdac_hdmi_query_cvt_params(&edev->hdev, cvt); } static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, @@ -1188,7 +1190,7 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, >> DRM_ELD_VER_SHIFT; if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { - dev_err(&edev->hdac.dev, "HDMI: Unknown ELD version %d\n", ver); + dev_err(&edev->hdev.dev, "HDMI: Unknown ELD version %d\n", ver); return -EINVAL; } @@ -1196,7 +1198,7 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; if (mnl > ELD_MAX_MNL) { - dev_err(&edev->hdac.dev, "HDMI: MNL Invalid %d\n", mnl); + dev_err(&edev->hdev.dev, "HDMI: MNL Invalid %d\n", mnl); return -EINVAL; } @@ -1209,7 +1211,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { struct hdac_ext_device *edev = pin->edev; - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pcm *pcm; int size = 0; int port_id = -1; @@ -1227,7 +1229,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, if (pin->mst_capable) port_id = port->id; - size = snd_hdac_acomp_get_eld(&edev->hdac, pin->nid, port_id, + size = snd_hdac_acomp_get_eld(&edev->hdev, pin->nid, port_id, &port->eld.monitor_present, port->eld.eld_buffer, ELD_MAX_SIZE); @@ -1250,7 +1252,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, if (!port->eld.monitor_present || !port->eld.eld_valid) { - dev_err(&edev->hdac.dev, "%s: disconnect for pin:port %d:%d\n", + dev_err(&edev->hdev.dev, "%s: disconnect for pin:port %d:%d\n", __func__, pin->nid, port->id); /* @@ -1304,7 +1306,7 @@ static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pin *pin; int ret; @@ -1333,40 +1335,38 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) #define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ #define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ -static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac) +static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev) { unsigned int vendor_param; - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); unsigned int vendor_nid = hdmi->drv_data->vendor_nid; - vendor_param = snd_hdac_codec_read(hdac, vendor_nid, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_GET_VENDOR_VERB, 0); if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) return; vendor_param |= INTEL_EN_ALL_PIN_CVTS; - vendor_param = snd_hdac_codec_read(hdac, vendor_nid, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_SET_VENDOR_VERB, vendor_param); if (vendor_param == -1) return; } -static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac) +static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) { unsigned int vendor_param; - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); unsigned int vendor_nid = hdmi->drv_data->vendor_nid; - vendor_param = snd_hdac_codec_read(hdac, vendor_nid, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_GET_VENDOR_VERB, 0); if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) return; /* enable DP1.2 mode */ vendor_param |= INTEL_EN_DP12; - vendor_param = snd_hdac_codec_read(hdac, vendor_nid, 0, + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, INTEL_SET_VENDOR_VERB, vendor_param); if (vendor_param == -1) return; @@ -1384,7 +1384,7 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = { * Each converter can support a stream independently. So a dai is created * based on the number of converter queried. */ -static int hdac_hdmi_create_dais(struct hdac_device *hdac, +static int hdac_hdmi_create_dais(struct hdac_device *hdev, struct snd_soc_dai_driver **dais, struct hdac_hdmi_priv *hdmi, int num_dais) { @@ -1397,20 +1397,20 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, u64 formats; int ret; - hdmi_dais = devm_kzalloc(&hdac->dev, + hdmi_dais = devm_kzalloc(&hdev->dev, (sizeof(*hdmi_dais) * num_dais), GFP_KERNEL); if (!hdmi_dais) return -ENOMEM; list_for_each_entry(cvt, &hdmi->cvt_list, head) { - ret = snd_hdac_query_supported_pcm(hdac, cvt->nid, + ret = snd_hdac_query_supported_pcm(hdev, cvt->nid, &rates, &formats, &bps); if (ret) return ret; sprintf(dai_name, "intel-hdmi-hifi%d", i+1); - hdmi_dais[i].name = devm_kstrdup(&hdac->dev, + hdmi_dais[i].name = devm_kstrdup(&hdev->dev, dai_name, GFP_KERNEL); if (!hdmi_dais[i].name) @@ -1418,7 +1418,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, snprintf(name, sizeof(name), "hifi%d", i+1); hdmi_dais[i].playback.stream_name = - devm_kstrdup(&hdac->dev, name, GFP_KERNEL); + devm_kstrdup(&hdev->dev, name, GFP_KERNEL); if (!hdmi_dais[i].playback.stream_name) return -ENOMEM; @@ -1438,6 +1438,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, } *dais = hdmi_dais; + hdmi->dai_drv = hdmi_dais; return 0; } @@ -1451,29 +1452,26 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, { hda_nid_t nid; int i, num_nodes; - struct hdac_device *hdac = &edev->hdac; - struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_cvt *temp_cvt, *cvt_next; struct hdac_hdmi_pin *temp_pin, *pin_next; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = &edev->hdev; int ret; - hdac_hdmi_skl_enable_all_pins(hdac); - hdac_hdmi_skl_enable_dp12(hdac); + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); - num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid); + num_nodes = snd_hdac_get_sub_nodes(hdev, hdev->afg, &nid); if (!nid || num_nodes <= 0) { - dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n"); + dev_warn(&hdev->dev, "HDMI: failed to get afg sub nodes\n"); return -EINVAL; } - hdac->num_nodes = num_nodes; - hdac->start_nid = nid; - - for (i = 0; i < hdac->num_nodes; i++, nid++) { + for (i = 0; i < num_nodes; i++, nid++) { unsigned int caps; unsigned int type; - caps = get_wcaps(hdac, nid); + caps = get_wcaps(hdev, nid); type = get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL)) @@ -1495,16 +1493,14 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, } } - hdac->end_nid = nid; - if (!hdmi->num_pin || !hdmi->num_cvt) { ret = -EIO; goto free_widgets; } - ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt); + ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt); if (ret) { - dev_err(&hdac->dev, "Failed to create dais with err: %d\n", + dev_err(&hdev->dev, "Failed to create dais with err: %d\n", ret); goto free_widgets; } @@ -1537,7 +1533,7 @@ free_widgets: static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) { struct hdac_ext_device *edev = aptr; - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pin *pin = NULL; struct hdac_hdmi_port *hport = NULL; struct snd_soc_codec *codec = edev->scodec; @@ -1546,7 +1542,7 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) /* Don't know how this mapping is derived */ hda_nid_t pin_nid = port + 0x04; - dev_dbg(&edev->hdac.dev, "%s: for pin:%d port=%d\n", __func__, + dev_dbg(&edev->hdev.dev, "%s: for pin:%d port=%d\n", __func__, pin_nid, pipe); /* @@ -1559,7 +1555,7 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) SNDRV_CTL_POWER_D0) return; - if (atomic_read(&edev->hdac.in_pm)) + if (atomic_read(&edev->hdev.in_pm)) return; list_for_each_entry(pin, &hdmi->pin_list, head) { @@ -1614,7 +1610,7 @@ static int create_fill_jack_kcontrols(struct snd_soc_card *card, char *name; int i = 0, j; struct snd_soc_codec *codec = edev->scodec; - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); kc = devm_kcalloc(codec->dev, hdmi->num_ports, sizeof(*kc), GFP_KERNEL); @@ -1652,7 +1648,7 @@ int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec, struct snd_soc_dapm_context *dapm) { struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pin *pin; struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *route; @@ -1728,7 +1724,7 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, { struct snd_soc_codec *codec = dai->codec; struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pcm *pcm; struct snd_pcm *snd_pcm; int err; @@ -1750,7 +1746,7 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, if (snd_pcm) { err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); if (err < 0) { - dev_err(&edev->hdac.dev, + dev_err(&edev->hdev.dev, "chmap control add failed with err: %d for pcm: %d\n", err, device); kfree(pcm); @@ -1791,7 +1787,7 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, static int hdmi_codec_probe(struct snd_soc_codec *codec) { struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(&codec->component); struct hdac_ext_link *hlink = NULL; @@ -1803,9 +1799,9 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) * hold the ref while we probe, also no need to drop the ref on * exit, we call pm_runtime_suspend() so that will do for us */ - hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdev.dev)); if (!hlink) { - dev_err(&edev->hdac.dev, "hdac link not found\n"); + dev_err(&edev->hdev.dev, "hdac link not found\n"); return -EIO; } @@ -1818,7 +1814,7 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) aops.audio_ptr = edev; ret = snd_hdac_i915_register_notifier(&aops); if (ret < 0) { - dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n", + dev_err(&edev->hdev.dev, "notifier register failed: err: %d\n", ret); return ret; } @@ -1831,9 +1827,9 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) * hdac_device core already sets the state to active and calls * get_noresume. So enable runtime and set the device to suspend. */ - pm_runtime_enable(&edev->hdac.dev); - pm_runtime_put(&edev->hdac.dev); - pm_runtime_suspend(&edev->hdac.dev); + pm_runtime_enable(&edev->hdev.dev); + pm_runtime_put(&edev->hdev.dev); + pm_runtime_suspend(&edev->hdev.dev); return 0; } @@ -1842,7 +1838,7 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec) { struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); - pm_runtime_disable(&edev->hdac.dev); + pm_runtime_disable(&edev->hdev.dev); return 0; } @@ -1850,9 +1846,9 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec) static int hdmi_codec_prepare(struct device *dev) { struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdac = &edev->hdac; + struct hdac_device *hdev = &edev->hdev; - pm_runtime_get_sync(&edev->hdac.dev); + pm_runtime_get_sync(&edev->hdev.dev); /* * Power down afg. @@ -1861,7 +1857,7 @@ static int hdmi_codec_prepare(struct device *dev) * is received. So setting power state is ensured without using loop * to read the state. */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); return 0; @@ -1870,15 +1866,15 @@ static int hdmi_codec_prepare(struct device *dev) static void hdmi_codec_complete(struct device *dev) { struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_device *hdac = &edev->hdac; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = &edev->hdev; /* Power up afg */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - hdac_hdmi_skl_enable_all_pins(&edev->hdac); - hdac_hdmi_skl_enable_dp12(&edev->hdac); + hdac_hdmi_skl_enable_all_pins(&edev->hdev); + hdac_hdmi_skl_enable_dp12(&edev->hdev); /* * As the ELD notify callback request is not entertained while the @@ -1888,7 +1884,7 @@ static void hdmi_codec_complete(struct device *dev) */ hdac_hdmi_present_sense_all_pins(edev, hdmi, false); - pm_runtime_put_sync(&edev->hdac.dev); + pm_runtime_put_sync(&edev->hdev.dev); } #else #define hdmi_codec_prepare NULL @@ -1901,21 +1897,20 @@ static const struct snd_soc_codec_driver hdmi_hda_codec = { .idle_bias_off = true, }; -static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, +static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx, unsigned char *chmap) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap)); } -static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, +static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, unsigned char *chmap, int prepared) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_ext_device *edev = to_ehdac_device(hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; @@ -1934,10 +1929,9 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, mutex_unlock(&pcm->lock); } -static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); if (!pcm) @@ -1949,10 +1943,9 @@ static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) return true; } -static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) +static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) { - struct hdac_ext_device *edev = to_ehdac_device(hdac); - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; @@ -1983,30 +1976,30 @@ static struct hdac_hdmi_drv_data intel_drv_data = { static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) { - struct hdac_device *codec = &edev->hdac; + struct hdac_device *hdev = &edev->hdev; struct hdac_hdmi_priv *hdmi_priv; struct snd_soc_dai_driver *hdmi_dais = NULL; struct hdac_ext_link *hlink = NULL; int num_dais = 0; int ret = 0; - struct hdac_driver *hdrv = drv_to_hdac_driver(codec->dev.driver); - const struct hda_device_id *hdac_id = hdac_get_device_id(codec, hdrv); + struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver); + const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv); /* hold the ref while we probe */ - hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdev.dev)); if (!hlink) { - dev_err(&edev->hdac.dev, "hdac link not found\n"); + dev_err(&edev->hdev.dev, "hdac link not found\n"); return -EIO; } snd_hdac_ext_bus_link_get(edev->ebus, hlink); - hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); + hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) return -ENOMEM; edev->private_data = hdmi_priv; - snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap); + snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap; hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; @@ -2021,7 +2014,7 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) else hdmi_priv->drv_data = &intel_drv_data; - dev_set_drvdata(&codec->dev, edev); + dev_set_drvdata(&hdev->dev, edev); INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); @@ -2032,9 +2025,9 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) * Turned off in the runtime_suspend during the first explicit * pm_runtime_suspend call. */ - ret = snd_hdac_display_power(edev->hdac.bus, true); + ret = snd_hdac_display_power(edev->hdev.bus, true); if (ret < 0) { - dev_err(&edev->hdac.dev, + dev_err(&edev->hdev.dev, "Cannot turn on display power on i915 err: %d\n", ret); return ret; @@ -2042,13 +2035,14 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); if (ret < 0) { - dev_err(&codec->dev, + dev_err(&hdev->dev, "Failed in parse and map nid with err: %d\n", ret); return ret; } + snd_hdac_refresh_widgets(hdev, true); /* ASoC specific initialization */ - ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, + ret = snd_soc_register_codec(&hdev->dev, &hdmi_hda_codec, hdmi_dais, num_dais); snd_hdac_ext_bus_link_put(edev->ebus, hlink); @@ -2058,14 +2052,14 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) { - struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); struct hdac_hdmi_pin *pin, *pin_next; struct hdac_hdmi_cvt *cvt, *cvt_next; struct hdac_hdmi_pcm *pcm, *pcm_next; struct hdac_hdmi_port *port, *port_next; int i; - snd_soc_unregister_codec(&edev->hdac.dev); + snd_soc_unregister_codec(&edev->hdev.dev); list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { pcm->cvt = NULL; @@ -2101,8 +2095,8 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdac = &edev->hdac; - struct hdac_bus *bus = hdac->bus; + struct hdac_device *hdev = &edev->hdev; + struct hdac_bus *bus = hdev->bus; struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; int err; @@ -2120,7 +2114,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) * is received. So setting power state is ensured without using loop * to read the state. */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); err = snd_hdac_display_power(bus, false); if (err < 0) { @@ -2142,8 +2136,8 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) static int hdac_hdmi_runtime_resume(struct device *dev) { struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdac = &edev->hdac; - struct hdac_bus *bus = hdac->bus; + struct hdac_device *hdev = &edev->hdev; + struct hdac_bus *bus = hdev->bus; struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; int err; @@ -2168,11 +2162,11 @@ static int hdac_hdmi_runtime_resume(struct device *dev) return err; } - hdac_hdmi_skl_enable_all_pins(&edev->hdac); - hdac_hdmi_skl_enable_dp12(&edev->hdac); + hdac_hdmi_skl_enable_all_pins(&edev->hdev); + hdac_hdmi_skl_enable_dp12(&edev->hdev); /* Power up afg */ - snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); return 0; @@ -2192,6 +2186,8 @@ static const struct hda_device_id hdmi_list[] = { HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0), HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280c, 0x100000, "Cannonlake HDMI", + &intel_glk_drv_data), HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", &intel_glk_drv_data), {} diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c new file mode 100644 index 000000000000..562e88765129 --- /dev/null +++ b/sound/soc/codecs/max98373.c @@ -0,0 +1,976 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017, Maxim Integrated + +#include <linux/acpi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/tlv.h> +#include "max98373.h" + +static struct reg_default max98373_reg[] = { + {MAX98373_R2000_SW_RESET, 0x00}, + {MAX98373_R2001_INT_RAW1, 0x00}, + {MAX98373_R2002_INT_RAW2, 0x00}, + {MAX98373_R2003_INT_RAW3, 0x00}, + {MAX98373_R2004_INT_STATE1, 0x00}, + {MAX98373_R2005_INT_STATE2, 0x00}, + {MAX98373_R2006_INT_STATE3, 0x00}, + {MAX98373_R2007_INT_FLAG1, 0x00}, + {MAX98373_R2008_INT_FLAG2, 0x00}, + {MAX98373_R2009_INT_FLAG3, 0x00}, + {MAX98373_R200A_INT_EN1, 0x00}, + {MAX98373_R200B_INT_EN2, 0x00}, + {MAX98373_R200C_INT_EN3, 0x00}, + {MAX98373_R200D_INT_FLAG_CLR1, 0x00}, + {MAX98373_R200E_INT_FLAG_CLR2, 0x00}, + {MAX98373_R200F_INT_FLAG_CLR3, 0x00}, + {MAX98373_R2010_IRQ_CTRL, 0x00}, + {MAX98373_R2014_THERM_WARN_THRESH, 0x10}, + {MAX98373_R2015_THERM_SHDN_THRESH, 0x27}, + {MAX98373_R2016_THERM_HYSTERESIS, 0x01}, + {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0}, + {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00}, + {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55}, + {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE}, + {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF}, + {MAX98373_R2022_PCM_TX_SRC_1, 0x00}, + {MAX98373_R2023_PCM_TX_SRC_2, 0x00}, + {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0}, + {MAX98373_R2025_AUDIO_IF_MODE, 0x00}, + {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04}, + {MAX98373_R2027_PCM_SR_SETUP_1, 0x08}, + {MAX98373_R2028_PCM_SR_SETUP_2, 0x88}, + {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00}, + {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00}, + {MAX98373_R202B_PCM_RX_EN, 0x00}, + {MAX98373_R202C_PCM_TX_EN, 0x00}, + {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00}, + {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00}, + {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF}, + {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF}, + {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30}, + {MAX98373_R2034_ICC_TX_CNTL, 0x00}, + {MAX98373_R2035_ICC_TX_EN, 0x00}, + {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05}, + {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00}, + {MAX98373_R203E_AMP_PATH_GAIN, 0x08}, + {MAX98373_R203F_AMP_DSP_CFG, 0x02}, + {MAX98373_R2040_TONE_GEN_CFG, 0x00}, + {MAX98373_R2041_AMP_CFG, 0x03}, + {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00}, + {MAX98373_R2043_AMP_EN, 0x00}, + {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04}, + {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00}, + {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00}, + {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00}, + {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00}, + {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00}, + {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00}, + {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00}, + {MAX98373_R2090_BDE_LVL_HOLD, 0x00}, + {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00}, + {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00}, + {MAX98373_R2097_BDE_L1_THRESH, 0x00}, + {MAX98373_R2098_BDE_L2_THRESH, 0x00}, + {MAX98373_R2099_BDE_L3_THRESH, 0x00}, + {MAX98373_R209A_BDE_L4_THRESH, 0x00}, + {MAX98373_R209B_BDE_THRESH_HYST, 0x00}, + {MAX98373_R20A8_BDE_L1_CFG_1, 0x00}, + {MAX98373_R20A9_BDE_L1_CFG_2, 0x00}, + {MAX98373_R20AA_BDE_L1_CFG_3, 0x00}, + {MAX98373_R20AB_BDE_L2_CFG_1, 0x00}, + {MAX98373_R20AC_BDE_L2_CFG_2, 0x00}, + {MAX98373_R20AD_BDE_L2_CFG_3, 0x00}, + {MAX98373_R20AE_BDE_L3_CFG_1, 0x00}, + {MAX98373_R20AF_BDE_L3_CFG_2, 0x00}, + {MAX98373_R20B0_BDE_L3_CFG_3, 0x00}, + {MAX98373_R20B1_BDE_L4_CFG_1, 0x00}, + {MAX98373_R20B2_BDE_L4_CFG_2, 0x00}, + {MAX98373_R20B3_BDE_L4_CFG_3, 0x00}, + {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00}, + {MAX98373_R20B5_BDE_EN, 0x00}, + {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00}, + {MAX98373_R20D1_DHT_CFG, 0x01}, + {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02}, + {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03}, + {MAX98373_R20D4_DHT_EN, 0x00}, + {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00}, + {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00}, + {MAX98373_R20E2_LIMITER_EN, 0x00}, + {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00}, + {MAX98373_R20FF_GLOBAL_SHDN, 0x00}, + {MAX98373_R21FF_REV_ID, 0x42}, +}; + +static int max98373_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec); + unsigned int format = 0; + unsigned int invert = 0; + + dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2026_PCM_CLOCK_RATIO, + MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98373_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98373_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98373_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98373_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98373_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98373_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98373_set_clock(struct snd_soc_codec *codec, + struct snd_pcm_hw_params *params) +{ + struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98373->ch_size; + int value; + + if (!max98373->tdm_mode) { + /* BCLK configuration */ + value = max98373_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(codec->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2026_PCM_CLOCK_RATIO, + MAX98373_PCM_CLK_SETUP_BSEL_MASK, + value); + } + return 0; +} + +static int max98373_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 max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(codec->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98373->ch_size = snd_pcm_format_width(params_format(params)); + + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + dev_dbg(codec->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_8000; + break; + case 11025: + sampling_rate = MAX98373_PCM_SR_SET1_SR_11025; + break; + case 12000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_12000; + break; + case 16000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_16000; + break; + case 22050: + sampling_rate = MAX98373_PCM_SR_SET1_SR_22050; + break; + case 24000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_24000; + break; + case 32000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_32000; + break; + case 44100: + sampling_rate = MAX98373_PCM_SR_SET1_SR_44100; + break; + case 48000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_48000; + break; + default: + dev_err(codec->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98373->regmap, + MAX98373_R2027_PCM_SR_SETUP_1, + MAX98373_PCM_SR_SET1_SR_MASK, + sampling_rate); + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_SR_MASK, + sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT); + + /* set sampling rate of IV */ + if (max98373->interleave_mode && + sampling_rate > MAX98373_PCM_SR_SET1_SR_16000) + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate - 3); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate); + + return max98373_set_clock(codec, params); +err: + return -EINVAL; +} + +static int max98373_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 max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec); + int bsel = 0; + unsigned int chan_sz = 0; + unsigned int mask; + int x, slot_found; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98373->tdm_mode = false; + else + max98373->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98373_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(codec->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2026_PCM_CLOCK_RATIO, + MAX98373_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(codec->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + slot_found = 0; + mask = rx_mask; + for (x = 0 ; x < 16 ; x++, mask >>= 1) { + if (mask & 0x1) { + if (slot_found == 0) + regmap_update_bits(max98373->regmap, + MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, + MAX98373_PCM_TO_SPK_CH0_SRC_MASK, x); + else + regmap_write(max98373->regmap, + MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, + x); + slot_found++; + if (slot_found > 1) + break; + } + } + + /* Tx slot Hi-Z configuration */ + regmap_write(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + ~tx_mask & 0xFF); + regmap_write(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + (~tx_mask & 0xFF00) >> 8); + + return 0; +} + +#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000 + +#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98373_dai_ops = { + .set_fmt = max98373_dai_set_fmt, + .hw_params = max98373_dai_hw_params, + .set_tdm_slot = max98373_dai_tdm_slot, +}; + +static int max98373_dac_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 max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(max98373->regmap, + MAX98373_R20FF_GLOBAL_SHDN, + MAX98373_GLOBAL_EN_MASK, 1); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(max98373->regmap, + MAX98373_R20FF_GLOBAL_SHDN, + MAX98373_GLOBAL_EN_MASK, 0); + max98373->tdm_mode = 0; + break; + default: + return 0; + } + return 0; +} + +static const char * const max98373_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, + MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT, + 3, max98373_switch_text); + +static const struct snd_kcontrol_new max98373_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98373_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98373_R202C_PCM_TX_EN, 0, 1, 0); + +static const struct snd_kcontrol_new max98373_spkfb_control = + SOC_DAPM_SINGLE("Switch", MAX98373_R2043_AMP_EN, 1, 1, 0); + +static const struct snd_soc_dapm_widget max98373_dapm_widgets[] = { +SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98373_R202B_PCM_RX_EN, 0, 0, max98373_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98373_dai_controls), +SND_SOC_DAPM_OUTPUT("BE_OUT"), +SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98373_R2047_IV_SENSE_ADC_EN, 0, 0), +SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98373_R2047_IV_SENSE_ADC_EN, 1, 0), +SND_SOC_DAPM_AIF_OUT("Speaker FB Sense", "HiFi Capture", 0, + SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98373_vi_control), +SND_SOC_DAPM_SWITCH("SpkFB Sense", SND_SOC_NOPM, 0, 0, + &max98373_spkfb_control), +SND_SOC_DAPM_SIGGEN("VMON"), +SND_SOC_DAPM_SIGGEN("IMON"), +SND_SOC_DAPM_SIGGEN("FBMON"), +}; + +static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, 0, -50, 0); +static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv, + 0, 8, TLV_DB_SCALE_ITEM(0, 50, 0), + 9, 10, TLV_DB_SCALE_ITEM(500, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_spkgain_max_tlv, + 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_dht_step_size_tlv, + 0, 1, TLV_DB_SCALE_ITEM(25, 25, 0), + 2, 4, TLV_DB_SCALE_ITEM(100, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv, + 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0), + 2, 7, TLV_DB_SCALE_ITEM(-200, -100, 0), + 8, 9, TLV_DB_SCALE_ITEM(-1000, -200, 0), + 10, 11, TLV_DB_SCALE_ITEM(-1500, -300, 0), + 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0), + 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv, + 0, 15, TLV_DB_SCALE_ITEM(0, -100, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv, + 0, 60, TLV_DB_SCALE_ITEM(0, -25, 0), +); + +static bool max98373_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3: + case MAX98373_R2010_IRQ_CTRL: + case MAX98373_R2014_THERM_WARN_THRESH + ... MAX98373_R2018_THERM_FOLDBACK_EN: + case MAX98373_R201E_PIN_DRIVE_STRENGTH + ... MAX98373_R2036_SOUNDWIRE_CTRL: + case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN: + case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG + ... MAX98373_R2047_IV_SENSE_ADC_EN: + case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE + ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN: + case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE: + case MAX98373_R2097_BDE_L1_THRESH + ... MAX98373_R209B_BDE_THRESH_HYST: + case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3: + case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK: + case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN: + case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN: + case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG + ... MAX98373_R20FF_GLOBAL_SHDN: + case MAX98373_R21FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98373_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3: + case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK: + case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK: + case MAX98373_R20B6_BDE_CUR_STATE_READBACK: + case MAX98373_R21FF_REV_ID: + return true; + default: + return false; + } +} + +static const char * const max98373_output_voltage_lvl_text[] = { + "5.43V", "6.09V", "6.83V", "7.67V", "8.60V", + "9.65V", "10.83V", "12.15V", "13.63V", "15.29V" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_out_volt_enum, + MAX98373_R203E_AMP_PATH_GAIN, 0, + max98373_output_voltage_lvl_text); + +static const char * const max98373_dht_attack_rate_text[] = { + "17.5us", "35us", "70us", "140us", + "280us", "560us", "1120us", "2240us" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_dht_attack_rate_enum, + MAX98373_R20D2_DHT_ATTACK_CFG, 0, + max98373_dht_attack_rate_text); + +static const char * const max98373_dht_release_rate_text[] = { + "45ms", "225ms", "450ms", "1150ms", + "2250ms", "3100ms", "4500ms", "6750ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_dht_release_rate_enum, + MAX98373_R20D3_DHT_RELEASE_CFG, 0, + max98373_dht_release_rate_text); + +static const char * const max98373_limiter_attack_rate_text[] = { + "10us", "20us", "40us", "80us", + "160us", "320us", "640us", "1.28ms", + "2.56ms", "5.12ms", "10.24ms", "20.48ms", + "40.96ms", "81.92ms", "16.384ms", "32.768ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_limiter_attack_rate_enum, + MAX98373_R20E1_LIMITER_ATK_REL_RATES, 4, + max98373_limiter_attack_rate_text); + +static const char * const max98373_limiter_release_rate_text[] = { + "40us", "80us", "160us", "320us", + "640us", "1.28ms", "2.56ms", "5.120ms", + "10.24ms", "20.48ms", "40.96ms", "81.92ms", + "163.84ms", "327.68ms", "655.36ms", "1310.72ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_limiter_release_rate_enum, + MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0, + max98373_limiter_release_rate_text); + +static const char * const max98373_ADC_samplerate_text[] = { + "333kHz", "192kHz", "64kHz", "48kHz" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum, + MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0, + max98373_ADC_samplerate_text); + +static const struct snd_kcontrol_new max98373_snd_controls[] = { +SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_VOL_SEL_SHIFT, 1, 0), +SOC_SINGLE("Volume Location Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_VOL_SEL_SHIFT, 1, 0), +SOC_SINGLE("Ramp Up Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0), +SOC_SINGLE("Ramp Down Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0), +SOC_SINGLE("CLK Monitor Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, + MAX98373_CLOCK_MON_SHIFT, 1, 0), +SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_DITH_SHIFT, 1, 0), +SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0), +SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL, + 0, 0x7F, 0, max98373_digital_tlv), +SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN, + MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv), +SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN, + MAX98373_FS_GAIN_MAX_SHIFT, 9, 0, max98373_spkgain_max_tlv), +SOC_ENUM("Output Voltage", max98373_out_volt_enum), +/* Dynamic Headroom Tracking */ +SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN, + MAX98373_DHT_EN_SHIFT, 1, 0), +SOC_SINGLE_TLV("DHT Min Volume", MAX98373_R20D1_DHT_CFG, + MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv), +SOC_SINGLE_TLV("DHT Rot Pnt Volume", MAX98373_R20D1_DHT_CFG, + MAX98373_DHT_ROT_PNT_SHIFT, 15, 0, max98373_dht_rotation_point_tlv), +SOC_SINGLE_TLV("DHT Attack Step Volume", MAX98373_R20D2_DHT_ATTACK_CFG, + MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv), +SOC_SINGLE_TLV("DHT Release Step Volume", MAX98373_R20D3_DHT_RELEASE_CFG, + MAX98373_DHT_RELEASE_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv), +SOC_ENUM("DHT Attack Rate", max98373_dht_attack_rate_enum), +SOC_ENUM("DHT Release Rate", max98373_dht_release_rate_enum), +/* ADC configuration */ +SOC_SINGLE("ADC PVDD CH Switch", MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0, 1, 0), +SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, + MAX98373_FLT_EN_SHIFT, 1, 0), +SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, + MAX98373_FLT_EN_SHIFT, 1, 0), +SOC_SINGLE("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0), +SOC_SINGLE("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0), +SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, + 0, 0x3, 0), +SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, + 0, 0x3, 0), +SOC_ENUM("ADC SampleRate", max98373_adc_samplerate_enum), +/* Brownout Detection Engine */ +SOC_SINGLE("BDE Switch", MAX98373_R20B5_BDE_EN, MAX98373_BDE_EN_SHIFT, 1, 0), +SOC_SINGLE("BDE LVL4 Mute Switch", MAX98373_R20B2_BDE_L4_CFG_2, + MAX98373_LVL4_MUTE_EN_SHIFT, 1, 0), +SOC_SINGLE("BDE LVL4 Hold Switch", MAX98373_R20B2_BDE_L4_CFG_2, + MAX98373_LVL4_HOLD_EN_SHIFT, 1, 0), +SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0), +SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0), +SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0), +SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0), +SOC_SINGLE("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0), +SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0), +SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0), +SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0), +SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0), +SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0), +SOC_SINGLE_TLV("BDE LVL1 Clip Thresh Volume", MAX98373_R20A9_BDE_L1_CFG_2, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL2 Clip Thresh Volume", MAX98373_R20AC_BDE_L2_CFG_2, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL3 Clip Thresh Volume", MAX98373_R20AF_BDE_L3_CFG_2, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL4 Clip Thresh Volume", MAX98373_R20B2_BDE_L4_CFG_2, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL1 Clip Reduction Volume", MAX98373_R20AA_BDE_L1_CFG_3, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL2 Clip Reduction Volume", MAX98373_R20AD_BDE_L2_CFG_3, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL3 Clip Reduction Volume", MAX98373_R20B0_BDE_L3_CFG_3, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL4 Clip Reduction Volume", MAX98373_R20B3_BDE_L4_CFG_3, + 0, 0x3C, 0, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh Volume", MAX98373_R20A8_BDE_L1_CFG_1, + 0, 0xF, 0, max98373_limiter_thresh_tlv), +SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh Volume", MAX98373_R20AB_BDE_L2_CFG_1, + 0, 0xF, 0, max98373_limiter_thresh_tlv), +SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh Volume", MAX98373_R20AE_BDE_L3_CFG_1, + 0, 0xF, 0, max98373_limiter_thresh_tlv), +SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh Volume", MAX98373_R20B1_BDE_L4_CFG_1, + 0, 0xF, 0, max98373_limiter_thresh_tlv), +/* Limiter */ +SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN, + MAX98373_LIMITER_EN_SHIFT, 1, 0), +SOC_SINGLE("Limiter Src Switch", MAX98373_R20E0_LIMITER_THRESH_CFG, + MAX98373_LIMITER_THRESH_SRC_SHIFT, 1, 0), +SOC_SINGLE_TLV("Limiter Thresh Volume", MAX98373_R20E0_LIMITER_THRESH_CFG, + MAX98373_LIMITER_THRESH_SHIFT, 15, 0, max98373_limiter_thresh_tlv), +SOC_ENUM("Limiter Attack Rate", max98373_limiter_attack_rate_enum), +SOC_ENUM("Limiter Release Rate", max98373_limiter_release_rate_enum), +}; + +static const struct snd_soc_dapm_route max98373_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "VI Sense", "Switch", "VMON" }, + { "VI Sense", "Switch", "IMON" }, + { "SpkFB Sense", "Switch", "FBMON" }, + { "Voltage Sense", NULL, "VI Sense" }, + { "Current Sense", NULL, "VI Sense" }, + { "Speaker FB Sense", NULL, "SpkFB Sense" }, +}; + +static struct snd_soc_dai_driver max98373_dai[] = { + { + .name = "max98373-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98373_RATES, + .formats = MAX98373_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98373_RATES, + .formats = MAX98373_FORMATS, + }, + .ops = &max98373_dai_ops, + } +}; + +static int max98373_probe(struct snd_soc_codec *codec) +{ + struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec); + + codec->control_data = max98373->regmap; + + /* Software Reset */ + regmap_write(max98373->regmap, + MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET); + + /* IV default slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 0xFF); + regmap_write(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 0xFF); + /* L/R mix configuration */ + regmap_write(max98373->regmap, + MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, + 0x80); + regmap_write(max98373->regmap, + MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, + 0x1); + /* Set inital volume (0dB) */ + regmap_write(max98373->regmap, + MAX98373_R203D_AMP_DIG_VOL_CTRL, + 0x00); + regmap_write(max98373->regmap, + MAX98373_R203E_AMP_PATH_GAIN, + 0x00); + /* Enable DC blocker */ + regmap_write(max98373->regmap, + MAX98373_R203F_AMP_DSP_CFG, + 0x3); + /* Enable IMON VMON DC blocker */ + regmap_write(max98373->regmap, + MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, + 0x7); + /* voltage, current slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2022_PCM_TX_SRC_1, + (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT | + max98373->v_slot) & 0xFF); + if (max98373->v_slot < 8) + regmap_update_bits(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 1 << max98373->v_slot, 0); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 1 << (max98373->v_slot - 8), 0); + + if (max98373->i_slot < 8) + regmap_update_bits(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 1 << max98373->i_slot, 0); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 1 << (max98373->i_slot - 8), 0); + + /* speaker feedback slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2023_PCM_TX_SRC_2, + max98373->spkfb_slot & 0xFF); + + /* Set interleave mode */ + if (max98373->interleave_mode) + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_TX_CH_INTERLEAVE_MASK, + MAX98373_PCM_TX_CH_INTERLEAVE_MASK); + + /* Speaker enable */ + regmap_update_bits(max98373->regmap, + MAX98373_R2043_AMP_EN, + MAX98373_SPK_EN_MASK, 1); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max98373_suspend(struct device *dev) +{ + struct max98373_priv *max98373 = dev_get_drvdata(dev); + + regcache_cache_only(max98373->regmap, true); + regcache_mark_dirty(max98373->regmap); + return 0; +} +static int max98373_resume(struct device *dev) +{ + struct max98373_priv *max98373 = dev_get_drvdata(dev); + + regmap_write(max98373->regmap, + MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET); + regcache_cache_only(max98373->regmap, false); + regcache_sync(max98373->regmap); + return 0; +} +#endif + +static const struct dev_pm_ops max98373_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume) +}; + +static const struct snd_soc_codec_driver soc_codec_dev_max98373 = { + .probe = max98373_probe, + .component_driver = { + .controls = max98373_snd_controls, + .num_controls = ARRAY_SIZE(max98373_snd_controls), + .dapm_widgets = max98373_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets), + .dapm_routes = max98373_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98373_audio_map), + }, +}; + +static const struct regmap_config max98373_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98373_R21FF_REV_ID, + .reg_defaults = max98373_reg, + .num_reg_defaults = ARRAY_SIZE(max98373_reg), + .readable_reg = max98373_readable_register, + .volatile_reg = max98373_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static void max98373_slot_config(struct i2c_client *i2c, + struct max98373_priv *max98373) +{ + int value; + struct device *dev = &i2c->dev; + + if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value)) + max98373->v_slot = value & 0xF; + else + max98373->v_slot = 0; + + if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value)) + max98373->i_slot = value & 0xF; + else + max98373->i_slot = 1; + + if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value)) + max98373->spkfb_slot = value & 0xF; + else + max98373->spkfb_slot = 2; +} + +static int max98373_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + + int ret = 0; + int reg = 0; + struct max98373_priv *max98373 = NULL; + + max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL); + + if (!max98373) { + ret = -ENOMEM; + return ret; + } + i2c_set_clientdata(i2c, max98373); + + /* update interleave mode info */ + if (device_property_read_bool(&i2c->dev, "maxim,interleave_mode")) + max98373->interleave_mode = 1; + else + max98373->interleave_mode = 0; + + + /* regmap initialization */ + max98373->regmap + = devm_regmap_init_i2c(i2c, &max98373_regmap); + if (IS_ERR(max98373->regmap)) { + ret = PTR_ERR(max98373->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + /* Check Revision ID */ + ret = regmap_read(max98373->regmap, + MAX98373_R21FF_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c->dev, + "Failed to read: 0x%02X\n", MAX98373_R21FF_REV_ID); + return ret; + } + dev_info(&i2c->dev, "MAX98373 revisionID: 0x%02X\n", reg); + + /* voltage/current slot configuration */ + max98373_slot_config(i2c, max98373); + + /* codec registeration */ + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98373, + max98373_dai, ARRAY_SIZE(max98373_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static int max98373_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98373_i2c_id[] = { + { "max98373", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98373_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98373_of_match[] = { + { .compatible = "maxim,max98373", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98373_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98373_acpi_match[] = { + { "MX98373", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98373_acpi_match); +#endif + +static struct i2c_driver max98373_i2c_driver = { + .driver = { + .name = "max98373", + .of_match_table = of_match_ptr(max98373_of_match), + .acpi_match_table = ACPI_PTR(max98373_acpi_match), + .pm = &max98373_pm, + }, + .probe = max98373_i2c_probe, + .remove = max98373_i2c_remove, + .id_table = max98373_i2c_id, +}; + +module_i2c_driver(max98373_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98373 driver"); +MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h new file mode 100644 index 000000000000..f6a37aa02f26 --- /dev/null +++ b/sound/soc/codecs/max98373.h @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017, Maxim Integrated + +#ifndef _MAX98373_H +#define _MAX98373_H + +#define MAX98373_R2000_SW_RESET 0x2000 +#define MAX98373_R2001_INT_RAW1 0x2001 +#define MAX98373_R2002_INT_RAW2 0x2002 +#define MAX98373_R2003_INT_RAW3 0x2003 +#define MAX98373_R2004_INT_STATE1 0x2004 +#define MAX98373_R2005_INT_STATE2 0x2005 +#define MAX98373_R2006_INT_STATE3 0x2006 +#define MAX98373_R2007_INT_FLAG1 0x2007 +#define MAX98373_R2008_INT_FLAG2 0x2008 +#define MAX98373_R2009_INT_FLAG3 0x2009 +#define MAX98373_R200A_INT_EN1 0x200A +#define MAX98373_R200B_INT_EN2 0x200B +#define MAX98373_R200C_INT_EN3 0x200C +#define MAX98373_R200D_INT_FLAG_CLR1 0x200D +#define MAX98373_R200E_INT_FLAG_CLR2 0x200E +#define MAX98373_R200F_INT_FLAG_CLR3 0x200F +#define MAX98373_R2010_IRQ_CTRL 0x2010 +#define MAX98373_R2014_THERM_WARN_THRESH 0x2014 +#define MAX98373_R2015_THERM_SHDN_THRESH 0x2015 +#define MAX98373_R2016_THERM_HYSTERESIS 0x2016 +#define MAX98373_R2017_THERM_FOLDBACK_SET 0x2017 +#define MAX98373_R2018_THERM_FOLDBACK_EN 0x2018 +#define MAX98373_R201E_PIN_DRIVE_STRENGTH 0x201E +#define MAX98373_R2020_PCM_TX_HIZ_EN_1 0x2020 +#define MAX98373_R2021_PCM_TX_HIZ_EN_2 0x2021 +#define MAX98373_R2022_PCM_TX_SRC_1 0x2022 +#define MAX98373_R2023_PCM_TX_SRC_2 0x2023 +#define MAX98373_R2024_PCM_DATA_FMT_CFG 0x2024 +#define MAX98373_R2025_AUDIO_IF_MODE 0x2025 +#define MAX98373_R2026_PCM_CLOCK_RATIO 0x2026 +#define MAX98373_R2027_PCM_SR_SETUP_1 0x2027 +#define MAX98373_R2028_PCM_SR_SETUP_2 0x2028 +#define MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 0x2029 +#define MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2 0x202A +#define MAX98373_R202B_PCM_RX_EN 0x202B +#define MAX98373_R202C_PCM_TX_EN 0x202C +#define MAX98373_R202E_ICC_RX_CH_EN_1 0x202E +#define MAX98373_R202F_ICC_RX_CH_EN_2 0x202F +#define MAX98373_R2030_ICC_TX_HIZ_EN_1 0x2030 +#define MAX98373_R2031_ICC_TX_HIZ_EN_2 0x2031 +#define MAX98373_R2032_ICC_LINK_EN_CFG 0x2032 +#define MAX98373_R2034_ICC_TX_CNTL 0x2034 +#define MAX98373_R2035_ICC_TX_EN 0x2035 +#define MAX98373_R2036_SOUNDWIRE_CTRL 0x2036 +#define MAX98373_R203D_AMP_DIG_VOL_CTRL 0x203D +#define MAX98373_R203E_AMP_PATH_GAIN 0x203E +#define MAX98373_R203F_AMP_DSP_CFG 0x203F +#define MAX98373_R2040_TONE_GEN_CFG 0x2040 +#define MAX98373_R2041_AMP_CFG 0x2041 +#define MAX98373_R2042_AMP_EDGE_RATE_CFG 0x2042 +#define MAX98373_R2043_AMP_EN 0x2043 +#define MAX98373_R2046_IV_SENSE_ADC_DSP_CFG 0x2046 +#define MAX98373_R2047_IV_SENSE_ADC_EN 0x2047 +#define MAX98373_R2051_MEAS_ADC_SAMPLING_RATE 0x2051 +#define MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG 0x2052 +#define MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG 0x2053 +#define MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK 0x2054 +#define MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK 0x2055 +#define MAX98373_R2056_MEAS_ADC_PVDD_CH_EN 0x2056 +#define MAX98373_R2090_BDE_LVL_HOLD 0x2090 +#define MAX98373_R2091_BDE_GAIN_ATK_REL_RATE 0x2091 +#define MAX98373_R2092_BDE_CLIPPER_MODE 0x2092 +#define MAX98373_R2097_BDE_L1_THRESH 0x2097 +#define MAX98373_R2098_BDE_L2_THRESH 0x2098 +#define MAX98373_R2099_BDE_L3_THRESH 0x2099 +#define MAX98373_R209A_BDE_L4_THRESH 0x209A +#define MAX98373_R209B_BDE_THRESH_HYST 0x209B +#define MAX98373_R20A8_BDE_L1_CFG_1 0x20A8 +#define MAX98373_R20A9_BDE_L1_CFG_2 0x20A9 +#define MAX98373_R20AA_BDE_L1_CFG_3 0x20AA +#define MAX98373_R20AB_BDE_L2_CFG_1 0x20AB +#define MAX98373_R20AC_BDE_L2_CFG_2 0x20AC +#define MAX98373_R20AD_BDE_L2_CFG_3 0x20AD +#define MAX98373_R20AE_BDE_L3_CFG_1 0x20AE +#define MAX98373_R20AF_BDE_L3_CFG_2 0x20AF +#define MAX98373_R20B0_BDE_L3_CFG_3 0x20B0 +#define MAX98373_R20B1_BDE_L4_CFG_1 0x20B1 +#define MAX98373_R20B2_BDE_L4_CFG_2 0x20B2 +#define MAX98373_R20B3_BDE_L4_CFG_3 0x20B3 +#define MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE 0x20B4 +#define MAX98373_R20B5_BDE_EN 0x20B5 +#define MAX98373_R20B6_BDE_CUR_STATE_READBACK 0x20B6 +#define MAX98373_R20D1_DHT_CFG 0x20D1 +#define MAX98373_R20D2_DHT_ATTACK_CFG 0x20D2 +#define MAX98373_R20D3_DHT_RELEASE_CFG 0x20D3 +#define MAX98373_R20D4_DHT_EN 0x20D4 +#define MAX98373_R20E0_LIMITER_THRESH_CFG 0x20E0 +#define MAX98373_R20E1_LIMITER_ATK_REL_RATES 0x20E1 +#define MAX98373_R20E2_LIMITER_EN 0x20E2 +#define MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG 0x20FE +#define MAX98373_R20FF_GLOBAL_SHDN 0x20FF +#define MAX98373_R21FF_REV_ID 0x21FF + +/* MAX98373_R2022_PCM_TX_SRC_1 */ +#define MAX98373_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98373_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98373_R2024_PCM_DATA_FMT_CFG */ +#define MAX98373_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98373_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98373_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98373_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98373_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98373_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98373_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98373_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98373_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98373_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98373_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98373_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98373_R2026_PCM_CLOCK_RATIO */ +#define MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4) +#define MAX98373_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98373_R2027_PCM_SR_SETUP_1 */ +#define MAX98373_PCM_SR_SET1_SR_MASK (0xF << 0) +#define MAX98373_PCM_SR_SET1_SR_8000 (0x0 << 0) +#define MAX98373_PCM_SR_SET1_SR_11025 (0x1 << 0) +#define MAX98373_PCM_SR_SET1_SR_12000 (0x2 << 0) +#define MAX98373_PCM_SR_SET1_SR_16000 (0x3 << 0) +#define MAX98373_PCM_SR_SET1_SR_22050 (0x4 << 0) +#define MAX98373_PCM_SR_SET1_SR_24000 (0x5 << 0) +#define MAX98373_PCM_SR_SET1_SR_32000 (0x6 << 0) +#define MAX98373_PCM_SR_SET1_SR_44100 (0x7 << 0) +#define MAX98373_PCM_SR_SET1_SR_48000 (0x8 << 0) + +/* MAX98373_R2028_PCM_SR_SETUP_2 */ +#define MAX98373_PCM_SR_SET2_SR_MASK (0xF << 4) +#define MAX98373_PCM_SR_SET2_SR_SHIFT (4) +#define MAX98373_PCM_SR_SET2_IVADC_SR_MASK (0xF << 0) + +/* MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 */ +#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6) +#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6) +#define MAX98373_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0) + +/* MAX98373_R203E_AMP_PATH_GAIN */ +#define MAX98373_SPK_DIGI_GAIN_MASK (0xF << 4) +#define MAX98373_SPK_DIGI_GAIN_SHIFT (4) +#define MAX98373_FS_GAIN_MAX_MASK (0xF << 0) +#define MAX98373_FS_GAIN_MAX_SHIFT (0) + +/* MAX98373_R203F_AMP_DSP_CFG */ +#define MAX98373_AMP_DSP_CFG_DCBLK_SHIFT (0) +#define MAX98373_AMP_DSP_CFG_DITH_SHIFT (1) +#define MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT (2) +#define MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT (3) +#define MAX98373_AMP_DSP_CFG_DAC_INV_SHIFT (5) +#define MAX98373_AMP_VOL_SEL_SHIFT (7) + +/* MAX98373_R2043_AMP_EN */ +#define MAX98373_SPKFB_EN_MASK (0x1 << 1) +#define MAX98373_SPK_EN_MASK (0x1 << 0) +#define MAX98373_SPKFB_EN_SHIFT (1) + +/*MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG */ +#define MAX98373_FLT_EN_SHIFT (4) + +/* MAX98373_R20B2_BDE_L4_CFG_2 */ +#define MAX98373_LVL4_MUTE_EN_SHIFT (7) +#define MAX98373_LVL4_HOLD_EN_SHIFT (6) + +/* MAX98373_R20B5_BDE_EN */ +#define MAX98373_BDE_EN_SHIFT (0) + +/* MAX98373_R20D1_DHT_CFG */ +#define MAX98373_DHT_SPK_GAIN_MIN_SHIFT (4) +#define MAX98373_DHT_ROT_PNT_SHIFT (0) + +/* MAX98373_R20D2_DHT_ATTACK_CFG */ +#define MAX98373_DHT_ATTACK_STEP_SHIFT (3) +#define MAX98373_DHT_ATTACK_RATE_SHIFT (0) + +/* MAX98373_R20D3_DHT_RELEASE_CFG */ +#define MAX98373_DHT_RELEASE_STEP_SHIFT (3) +#define MAX98373_DHT_RELEASE_RATE_SHIFT (0) + +/* MAX98373_R20D4_DHT_EN */ +#define MAX98373_DHT_EN_SHIFT (0) + +/* MAX98373_R20E0_LIMITER_THRESH_CFG */ +#define MAX98373_LIMITER_THRESH_SHIFT (2) +#define MAX98373_LIMITER_THRESH_SRC_SHIFT (0) + +/* MAX98373_R20E2_LIMITER_EN */ +#define MAX98373_LIMITER_EN_SHIFT (0) + +/* MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG */ +#define MAX98373_CLOCK_MON_SHIFT (0) + +/* MAX98373_R20FF_GLOBAL_SHDN */ +#define MAX98373_GLOBAL_EN_MASK (0x1 << 0) + +/* MAX98373_R2000_SW_RESET */ +#define MAX98373_SOFT_RESET (0x1 << 0) + +struct max98373_priv { + struct regmap *regmap; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spkfb_slot; + bool interleave_mode; + unsigned int ch_size; + bool tdm_mode; +}; +#endif diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index 03d07bf4d942..7b1d1b0fa879 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -490,7 +490,7 @@ static int max98926_probe(struct snd_soc_codec *codec) struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); max98926->codec = codec; - codec->control_data = max98926->regmap; + /* Hi-Z all the slots */ regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0); return 0; diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index a1d39353719d..f701fdc81175 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -682,7 +682,6 @@ static int max98927_probe(struct snd_soc_codec *codec) struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); max98927->codec = codec; - codec->control_data = max98927->regmap; /* Software Reset */ regmap_write(max98927->regmap, diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 4fd8d1dc4eef..be7a45f05bbf 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -610,6 +610,9 @@ static int mc13783_probe(struct snd_soc_codec *codec) { struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); + snd_soc_codec_init_regmap(codec, + dev_get_regmap(codec->dev->parent, NULL)); + /* these are the reset values */ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893); mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A); @@ -728,15 +731,9 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = { } }; -static struct regmap *mc13783_get_regmap(struct device *dev) -{ - return dev_get_regmap(dev->parent, NULL); -} - static const struct snd_soc_codec_driver soc_codec_dev_mc13783 = { .probe = mc13783_probe, .remove = mc13783_remove, - .get_regmap = mc13783_get_regmap, .component_driver = { .controls = mc13783_control_list, .num_controls = ARRAY_SIZE(mc13783_control_list), diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 5f3c42c4f74a..44062bb7bf2f 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -267,7 +267,7 @@ #define MSM8916_WCD_ANALOG_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) #define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S32_LE) static int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4; @@ -712,6 +712,8 @@ static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec) return err; } + snd_soc_codec_init_regmap(codec, + dev_get_regmap(codec->dev->parent, NULL)); snd_soc_codec_set_drvdata(codec, priv); priv->pmic_rev = snd_soc_read(codec, CDC_D_REVISION1); priv->codec_version = snd_soc_read(codec, CDC_D_PERPH_SUBTYPE); @@ -943,11 +945,6 @@ static int pm8916_wcd_analog_set_jack(struct snd_soc_codec *codec, return 0; } -static struct regmap *pm8916_get_regmap(struct device *dev) -{ - return dev_get_regmap(dev->parent, NULL); -} - static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg) { struct pm8916_wcd_analog_priv *priv = arg; @@ -1082,7 +1079,6 @@ static const struct snd_soc_codec_driver pm8916_wcd_analog = { .probe = pm8916_wcd_analog_probe, .remove = pm8916_wcd_analog_remove, .set_jack = pm8916_wcd_analog_set_jack, - .get_regmap = pm8916_get_regmap, .component_driver = { .controls = pm8916_wcd_analog_snd_controls, .num_controls = ARRAY_SIZE(pm8916_wcd_analog_snd_controls), diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c index a10a724eb448..13354d6304a8 100644 --- a/sound/soc/codecs/msm8916-wcd-digital.c +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -194,7 +194,7 @@ SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_48000) #define MSM8916_WCD_DIGITAL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S32_LE) struct msm8916_wcd_digital_priv { struct clk *ahbclk, *mclk; @@ -645,7 +645,7 @@ static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream, RX_I2S_CTL_RX_I2S_MODE_MASK, RX_I2S_CTL_RX_I2S_MODE_16); break; - case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, TX_I2S_CTL_TX_I2S_MODE_MASK, TX_I2S_CTL_TX_I2S_MODE_32); diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index f9c9933acffb..b08fb7e243c3 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -233,6 +233,41 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new digital_ch1_mux = SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum); +static int adc_power_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + msleep(300); + /* DO12 and DO34 pad output enable */ + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_DO12_TRI, 0); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_TRI, 0); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI); + } + return 0; +} + +static int aiftx_power_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0001); + regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0000); + } + return 0; +} + static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0), @@ -247,14 +282,18 @@ static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0), SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0), - SND_SOC_DAPM_ADC("ADC1", NULL, - NAU8540_REG_POWER_MANAGEMENT, 0, 0), - SND_SOC_DAPM_ADC("ADC2", NULL, - NAU8540_REG_POWER_MANAGEMENT, 1, 0), - SND_SOC_DAPM_ADC("ADC3", NULL, - NAU8540_REG_POWER_MANAGEMENT, 2, 0), - SND_SOC_DAPM_ADC("ADC4", NULL, - NAU8540_REG_POWER_MANAGEMENT, 3, 0), + SND_SOC_DAPM_ADC_E("ADC1", NULL, + NAU8540_REG_POWER_MANAGEMENT, 0, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, + NAU8540_REG_POWER_MANAGEMENT, 1, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC3", NULL, + NAU8540_REG_POWER_MANAGEMENT, 2, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC4", NULL, + NAU8540_REG_POWER_MANAGEMENT, 3, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0), @@ -270,7 +309,8 @@ static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { SND_SOC_DAPM_MUX("Digital CH1 Mux", SND_SOC_NOPM, 0, 0, &digital_ch1_mux), - SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0, + aiftx_power_control, SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { @@ -575,7 +615,8 @@ static void nau8540_fll_apply(struct regmap *regmap, NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK, NAU8540_CLK_SRC_MCLK | fll_param->mclk_src); regmap_update_bits(regmap, NAU8540_REG_FLL1, - NAU8540_FLL_RATIO_MASK, fll_param->ratio); + NAU8540_FLL_RATIO_MASK | NAU8540_ICTRL_LATCH_MASK, + fll_param->ratio | (0x6 << NAU8540_ICTRL_LATCH_SFT)); /* FLL 16-bit fractional input */ regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac); /* FLL 10-bit integer input */ @@ -596,13 +637,14 @@ static void nau8540_fll_apply(struct regmap *regmap, NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | NAU8540_FLL_FTR_SW_FILTER); regmap_update_bits(regmap, NAU8540_REG_FLL6, - NAU8540_SDM_EN, NAU8540_SDM_EN); + NAU8540_SDM_EN | NAU8540_CUTOFF500, + NAU8540_SDM_EN | NAU8540_CUTOFF500); } else { regmap_update_bits(regmap, NAU8540_REG_FLL5, NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU); - regmap_update_bits(regmap, - NAU8540_REG_FLL6, NAU8540_SDM_EN, 0); + regmap_update_bits(regmap, NAU8540_REG_FLL6, + NAU8540_SDM_EN | NAU8540_CUTOFF500, 0); } } @@ -617,17 +659,22 @@ static int nau8540_set_pll(struct snd_soc_codec *codec, int pll_id, int source, switch (pll_id) { case NAU8540_CLK_FLL_MCLK: regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, - NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_MCLK); + NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK, + NAU8540_FLL_CLK_SRC_MCLK | 0); break; case NAU8540_CLK_FLL_BLK: regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, - NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_BLK); + NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK, + NAU8540_FLL_CLK_SRC_BLK | + (0xf << NAU8540_GAIN_ERR_SFT)); break; case NAU8540_CLK_FLL_FS: regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, - NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_FS); + NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK, + NAU8540_FLL_CLK_SRC_FS | + (0xf << NAU8540_GAIN_ERR_SFT)); break; default: @@ -710,9 +757,24 @@ static void nau8540_init_regs(struct nau8540 *nau8540) regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL, NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN, NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN); - /* ADC OSR selection, CLK_ADC = Fs * OSR */ + /* ADC OSR selection, CLK_ADC = Fs * OSR; + * Channel time alignment enable. + */ regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE, - NAU8540_ADC_OSR_MASK, NAU8540_ADC_OSR_64); + NAU8540_CH_SYNC | NAU8540_ADC_OSR_MASK, + NAU8540_CH_SYNC | NAU8540_ADC_OSR_64); + /* PGA input mode selection */ + regmap_update_bits(regmap, NAU8540_REG_FEPGA1, + NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT, + NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT); + regmap_update_bits(regmap, NAU8540_REG_FEPGA2, + NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT, + NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT); + /* DO12 and DO34 pad output disable */ + regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI); + regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI); } static int __maybe_unused nau8540_suspend(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h index 5db5b224944d..732b490edf81 100644 --- a/sound/soc/codecs/nau8540.h +++ b/sound/soc/codecs/nau8540.h @@ -100,9 +100,13 @@ #define NAU8540_CLK_MCLK_SRC_MASK 0xf /* FLL1 (0x04) */ +#define NAU8540_ICTRL_LATCH_SFT 10 +#define NAU8540_ICTRL_LATCH_MASK (0x7 << NAU8540_ICTRL_LATCH_SFT) #define NAU8540_FLL_RATIO_MASK 0x7f /* FLL3 (0x06) */ +#define NAU8540_GAIN_ERR_SFT 12 +#define NAU8540_GAIN_ERR_MASK (0xf << NAU8540_GAIN_ERR_SFT) #define NAU8540_FLL_CLK_SRC_SFT 10 #define NAU8540_FLL_CLK_SRC_MASK (0x3 << NAU8540_FLL_CLK_SRC_SFT) #define NAU8540_FLL_CLK_SRC_MCLK (0 << NAU8540_FLL_CLK_SRC_SFT) @@ -127,6 +131,7 @@ /* FLL6 (0x9) */ #define NAU8540_DCO_EN (0x1 << 15) #define NAU8540_SDM_EN (0x1 << 14) +#define NAU8540_CUTOFF500 (0x1 << 13) /* PCM_CTRL0 (0x10) */ #define NAU8540_I2S_BP_SFT 7 @@ -146,6 +151,7 @@ #define NAU8540_I2S_DF_PCM_AB 0x3 /* PCM_CTRL1 (0x11) */ +#define NAU8540_I2S_DO12_TRI (0x1 << 15) #define NAU8540_I2S_LRC_DIV_SFT 12 #define NAU8540_I2S_LRC_DIV_MASK (0x3 << NAU8540_I2S_LRC_DIV_SFT) #define NAU8540_I2S_DO12_OE (0x1 << 4) @@ -156,6 +162,7 @@ #define NAU8540_I2S_BLK_DIV_MASK 0x7 /* PCM_CTRL1 (0x12) */ +#define NAU8540_I2S_DO34_TRI (0x1 << 15) #define NAU8540_I2S_DO34_OE (0x1 << 11) #define NAU8540_I2S_TSLOT_L_MASK 0x3ff @@ -165,6 +172,7 @@ #define NAU8540_TDM_TX_MASK 0xf /* ADC_SAMPLE_RATE (0x3A) */ +#define NAU8540_CH_SYNC (0x1 << 14) #define NAU8540_ADC_OSR_MASK 0x3 #define NAU8540_ADC_OSR_256 0x3 #define NAU8540_ADC_OSR_128 0x2 @@ -183,6 +191,18 @@ #define NAU8540_PRECHARGE_DIS (0x1 << 13) #define NAU8540_GLOBAL_BIAS_EN (0x1 << 12) +/* FEPGA1 (0x69) */ +#define NAU8540_FEPGA1_MODCH2_SHT_SFT 7 +#define NAU8540_FEPGA1_MODCH2_SHT (0x1 << NAU8540_FEPGA1_MODCH2_SHT_SFT) +#define NAU8540_FEPGA1_MODCH1_SHT_SFT 3 +#define NAU8540_FEPGA1_MODCH1_SHT (0x1 << NAU8540_FEPGA1_MODCH1_SHT_SFT) + +/* FEPGA2 (0x6A) */ +#define NAU8540_FEPGA2_MODCH4_SHT_SFT 7 +#define NAU8540_FEPGA2_MODCH4_SHT (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT) +#define NAU8540_FEPGA2_MODCH3_SHT_SFT 3 +#define NAU8540_FEPGA2_MODCH3_SHT (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT) + /* System Clock Source */ enum { diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 0240759f951c..088e0cef4cb8 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -43,7 +43,7 @@ static bool nau8824_is_jack_inserted(struct nau8824 *nau8824); /* the parameter threshold of FLL */ #define NAU_FREF_MAX 13500000 -#define NAU_FVCO_MAX 124000000 +#define NAU_FVCO_MAX 100000000 #define NAU_FVCO_MIN 90000000 /* scaling for mclk from sysclk_src output */ @@ -811,7 +811,8 @@ static void nau8824_eject_jack(struct nau8824 *nau8824) NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE); /* Close clock for jack type detection at manual mode */ - nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); + if (dapm->bias_level < SND_SOC_BIAS_PREPARE) + nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); } static void nau8824_jdet_work(struct work_struct *work) @@ -843,6 +844,11 @@ static void nau8824_jdet_work(struct work_struct *work) event_mask |= SND_JACK_HEADSET; snd_soc_jack_report(nau8824->jack, event, event_mask); + /* Enable short key press and release interruption. */ + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_KEY_RELEASE_DIS | + NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0); + nau8824_sema_release(nau8824); } @@ -850,15 +856,15 @@ static void nau8824_setup_auto_irq(struct nau8824 *nau8824) { struct regmap *regmap = nau8824->regmap; - /* Enable jack ejection, short key press and release interruption. */ + /* Enable jack ejection interruption. */ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1, NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN, NAU8824_IRQ_EJECT_EN); regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING, - NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_KEY_RELEASE_DIS | - NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0); + NAU8824_IRQ_EJECT_DIS, 0); /* Enable internal VCO needed for interruptions */ - nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0); + if (nau8824->dapm->bias_level < SND_SOC_BIAS_PREPARE) + nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0); regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL, NAU8824_JD_SLEEP_MODE, 0); } diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 714ce17da717..a1b697b6fb64 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -194,10 +194,10 @@ static const struct reg_default nau8825_reg_defaults[] = { /* register backup table when cross talk detection */ static struct reg_default nau8825_xtalk_baktab[] = { - { NAU8825_REG_ADC_DGAIN_CTRL, 0 }, + { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf }, { NAU8825_REG_HSVOL_CTRL, 0 }, - { NAU8825_REG_DACL_CTRL, 0 }, - { NAU8825_REG_DACR_CTRL, 0 }, + { NAU8825_REG_DACL_CTRL, 0x00cf }, + { NAU8825_REG_DACR_CTRL, 0x02cf }, }; static const unsigned short logtable[256] = { @@ -245,13 +245,14 @@ static const unsigned short logtable[256] = { * tasks are allowed to acquire the semaphore, calling this function will * put the task to sleep. If the semaphore is not released within the * specified number of jiffies, this function returns. - * Acquires the semaphore without jiffies. If no more tasks are allowed - * to acquire the semaphore, calling this function will put the task to - * sleep until the semaphore is released. * If the semaphore is not released within the specified number of jiffies, - * this function returns -ETIME. - * If the sleep is interrupted by a signal, this function will return -EINTR. - * It returns 0 if the semaphore was acquired successfully. + * this function returns -ETIME. If the sleep is interrupted by a signal, + * this function will return -EINTR. It returns 0 if the semaphore was + * acquired successfully. + * + * Acquires the semaphore without jiffies. Try to acquire the semaphore + * atomically. Returns 0 if the semaphore has been acquired successfully + * or 1 if it it cannot be acquired. */ static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout) { @@ -262,8 +263,8 @@ static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout) if (ret < 0) dev_warn(nau8825->dev, "Acquire semaphore timeout\n"); } else { - ret = down_interruptible(&nau8825->xtalk_sem); - if (ret < 0) + ret = down_trylock(&nau8825->xtalk_sem); + if (ret) dev_warn(nau8825->dev, "Acquire semaphore fail\n"); } @@ -454,22 +455,32 @@ static void nau8825_xtalk_backup(struct nau8825 *nau8825) { int i; + if (nau8825->xtalk_baktab_initialized) + return; + /* Backup some register values to backup table */ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) regmap_read(nau8825->regmap, nau8825_xtalk_baktab[i].reg, &nau8825_xtalk_baktab[i].def); + + nau8825->xtalk_baktab_initialized = true; } -static void nau8825_xtalk_restore(struct nau8825 *nau8825) +static void nau8825_xtalk_restore(struct nau8825 *nau8825, bool cause_cancel) { int i, volume; + if (!nau8825->xtalk_baktab_initialized) + return; + /* Restore register values from backup table; When the driver restores - * the headphone volumem, it needs recover to original level gradually - * with 3dB per step for less pop noise. + * the headphone volume in XTALK_DONE state, it needs recover to + * original level gradually with 3dB per step for less pop noise. + * Otherwise, the restore should do ASAP. */ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) { - if (nau8825_xtalk_baktab[i].reg == NAU8825_REG_HSVOL_CTRL) { + if (!cause_cancel && nau8825_xtalk_baktab[i].reg == + NAU8825_REG_HSVOL_CTRL) { /* Ramping up the volume change to reduce pop noise */ volume = nau8825_xtalk_baktab[i].def & NAU8825_HPR_VOL_MASK; @@ -479,6 +490,8 @@ static void nau8825_xtalk_restore(struct nau8825 *nau8825) regmap_write(nau8825->regmap, nau8825_xtalk_baktab[i].reg, nau8825_xtalk_baktab[i].def); } + + nau8825->xtalk_baktab_initialized = false; } static void nau8825_xtalk_prepare_dac(struct nau8825 *nau8825) @@ -644,7 +657,7 @@ static void nau8825_xtalk_clean_adc(struct nau8825 *nau8825) NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, 0); } -static void nau8825_xtalk_clean(struct nau8825 *nau8825) +static void nau8825_xtalk_clean(struct nau8825 *nau8825, bool cause_cancel) { /* Enable internal VCO needed for interruptions */ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0); @@ -660,7 +673,7 @@ static void nau8825_xtalk_clean(struct nau8825 *nau8825) NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE); /* Restore value of specific register for cross talk */ - nau8825_xtalk_restore(nau8825); + nau8825_xtalk_restore(nau8825, cause_cancel); } static void nau8825_xtalk_imm_start(struct nau8825 *nau8825, int vol) @@ -779,7 +792,7 @@ static void nau8825_xtalk_measure(struct nau8825 *nau8825) dev_dbg(nau8825->dev, "cross talk sidetone: %x\n", sidetone); regmap_write(nau8825->regmap, NAU8825_REG_DAC_DGAIN_CTRL, (sidetone << 8) | sidetone); - nau8825_xtalk_clean(nau8825); + nau8825_xtalk_clean(nau8825, false); nau8825->xtalk_state = NAU8825_XTALK_DONE; break; default: @@ -815,13 +828,14 @@ static void nau8825_xtalk_work(struct work_struct *work) static void nau8825_xtalk_cancel(struct nau8825 *nau8825) { - /* If the xtalk_protect is true, that means the process is still - * on going. The driver forces to cancel the cross talk task and + /* If the crosstalk is eanbled and the process is on going, + * the driver forces to cancel the crosstalk task and * restores the configuration to original status. */ - if (nau8825->xtalk_protect) { + if (nau8825->xtalk_enable && nau8825->xtalk_state != + NAU8825_XTALK_DONE) { cancel_work_sync(&nau8825->xtalk_work); - nau8825_xtalk_clean(nau8825); + nau8825_xtalk_clean(nau8825, true); } /* Reset parameters for cross talk suppression function */ nau8825_sema_reset(nau8825); @@ -905,6 +919,7 @@ static int nau8825_adc_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: + msleep(125); regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC); break; @@ -1245,8 +1260,10 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream, regmap_read(nau8825->regmap, NAU8825_REG_DAC_CTRL1, &osr); osr &= NAU8825_DAC_OVERSAMPLE_MASK; if (nau8825_clock_check(nau8825, substream->stream, - params_rate(params), osr)) + params_rate(params), osr)) { + nau8825_sema_release(nau8825); return -EINVAL; + } regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_DAC_SRC_MASK, osr_dac_sel[osr].clk_src << NAU8825_CLK_DAC_SRC_SFT); @@ -1254,8 +1271,10 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream, regmap_read(nau8825->regmap, NAU8825_REG_ADC_RATE, &osr); osr &= NAU8825_ADC_SYNC_DOWN_MASK; if (nau8825_clock_check(nau8825, substream->stream, - params_rate(params), osr)) + params_rate(params), osr)) { + nau8825_sema_release(nau8825); return -EINVAL; + } regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_ADC_SRC_MASK, osr_adc_sel[osr].clk_src << NAU8825_CLK_ADC_SRC_SFT); @@ -1272,8 +1291,10 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream, bclk_div = 1; else if (bclk_fs <= 128) bclk_div = 0; - else + else { + nau8825_sema_release(nau8825); return -EINVAL; + } regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK, ((bclk_div + 1) << NAU8825_I2S_LRC_DIV_SFT) | bclk_div); @@ -1293,6 +1314,7 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream, val_len |= NAU8825_I2S_DL_32; break; default: + nau8825_sema_release(nau8825); return -EINVAL; } @@ -1311,8 +1333,6 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); unsigned int ctrl1_val = 0, ctrl2_val = 0; - nau8825_sema_acquire(nau8825, 3 * HZ); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: ctrl2_val |= NAU8825_I2S_MS_MASTER; @@ -1354,6 +1374,8 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } + nau8825_sema_acquire(nau8825, 3 * HZ); + 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, @@ -1686,7 +1708,7 @@ static irqreturn_t nau8825_interrupt(int irq, void *data) } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) { if (nau8825_is_jack_inserted(regmap)) { event |= nau8825_jack_insert(nau8825); - if (!nau8825->xtalk_bypass && !nau8825->high_imped) { + if (nau8825->xtalk_enable && !nau8825->high_imped) { /* Apply the cross talk suppression in the * headset without high impedance. */ @@ -1700,12 +1722,15 @@ static irqreturn_t nau8825_interrupt(int irq, void *data) int ret; nau8825->xtalk_protect = true; ret = nau8825_sema_acquire(nau8825, 0); - if (ret < 0) + if (ret) nau8825->xtalk_protect = false; } /* Startup cross talk detection process */ - nau8825->xtalk_state = NAU8825_XTALK_PREPARE; - schedule_work(&nau8825->xtalk_work); + if (nau8825->xtalk_protect) { + nau8825->xtalk_state = + NAU8825_XTALK_PREPARE; + schedule_work(&nau8825->xtalk_work); + } } else { /* The cross talk suppression shouldn't apply * in the headset with high impedance. Thus, @@ -1732,7 +1757,9 @@ static irqreturn_t nau8825_interrupt(int irq, void *data) nau8825->xtalk_event_mask = event_mask; } } else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) { - schedule_work(&nau8825->xtalk_work); + /* crosstalk detection enable and process on going */ + if (nau8825->xtalk_enable && nau8825->xtalk_protect) + schedule_work(&nau8825->xtalk_work); clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ; } else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) == NAU8825_JACK_INSERTION_DETECTED) { @@ -2381,7 +2408,7 @@ static int __maybe_unused nau8825_resume(struct snd_soc_codec *codec) regcache_sync(nau8825->regmap); nau8825->xtalk_protect = true; ret = nau8825_sema_acquire(nau8825, 0); - if (ret < 0) + if (ret) nau8825->xtalk_protect = false; enable_irq(nau8825->irq); @@ -2440,8 +2467,8 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825) nau8825->jack_insert_debounce); dev_dbg(dev, "jack-eject-debounce: %d\n", nau8825->jack_eject_debounce); - dev_dbg(dev, "crosstalk-bypass: %d\n", - nau8825->xtalk_bypass); + dev_dbg(dev, "crosstalk-enable: %d\n", + nau8825->xtalk_enable); } static int nau8825_read_device_properties(struct device *dev, @@ -2506,8 +2533,8 @@ static int nau8825_read_device_properties(struct device *dev, &nau8825->jack_eject_debounce); if (ret) nau8825->jack_eject_debounce = 0; - nau8825->xtalk_bypass = device_property_read_bool(dev, - "nuvoton,crosstalk-bypass"); + nau8825->xtalk_enable = device_property_read_bool(dev, + "nuvoton,crosstalk-enable"); nau8825->mclk = devm_clk_get(dev, "mclk"); if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { @@ -2568,6 +2595,7 @@ static int nau8825_i2c_probe(struct i2c_client *i2c, */ nau8825->xtalk_state = NAU8825_XTALK_DONE; nau8825->xtalk_protect = false; + nau8825->xtalk_baktab_initialized = false; sema_init(&nau8825->xtalk_sem, 1); INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work); diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 8aee5c8647ae..f7e732125882 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -476,7 +476,8 @@ struct nau8825 { int xtalk_event_mask; bool xtalk_protect; int imp_rms[NAU8825_XTALK_IMM]; - int xtalk_bypass; + int xtalk_enable; + bool xtalk_baktab_initialized; /* True if initialized. */ }; int nau8825_enable_jack_detect(struct snd_soc_codec *codec, diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c new file mode 100644 index 000000000000..543621232d60 --- /dev/null +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC - I2C + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com + * Andreas Dannenberg <dannenberg@ti.com> + * Andrew F. Davis <afd@ti.com> + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> + +#include "pcm186x.h" + +static const struct of_device_id pcm186x_of_match[] = { + { .compatible = "ti,pcm1862", .data = (void *)PCM1862 }, + { .compatible = "ti,pcm1863", .data = (void *)PCM1863 }, + { .compatible = "ti,pcm1864", .data = (void *)PCM1864 }, + { .compatible = "ti,pcm1865", .data = (void *)PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm186x_of_match); + +static int pcm186x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + const enum pcm186x_type type = (enum pcm186x_type)id->driver_data; + int irq = i2c->irq; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &pcm186x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm186x_probe(&i2c->dev, type, irq, regmap); +} + +static int pcm186x_i2c_remove(struct i2c_client *i2c) +{ + pcm186x_remove(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id pcm186x_i2c_id[] = { + { "pcm1862", PCM1862 }, + { "pcm1863", PCM1863 }, + { "pcm1864", PCM1864 }, + { "pcm1865", PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id); + +static struct i2c_driver pcm186x_i2c_driver = { + .probe = pcm186x_i2c_probe, + .remove = pcm186x_i2c_remove, + .id_table = pcm186x_i2c_id, + .driver = { + .name = "pcm186x", + .of_match_table = pcm186x_of_match, + }, +}; +module_i2c_driver(pcm186x_i2c_driver); + +MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("PCM186x Universal Audio ADC I2C Interface Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm186x-spi.c b/sound/soc/codecs/pcm186x-spi.c new file mode 100644 index 000000000000..2366f8e4d4d4 --- /dev/null +++ b/sound/soc/codecs/pcm186x-spi.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC - SPI + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com + * Andreas Dannenberg <dannenberg@ti.com> + * Andrew F. Davis <afd@ti.com> + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "pcm186x.h" + +static const struct of_device_id pcm186x_of_match[] = { + { .compatible = "ti,pcm1862", .data = (void *)PCM1862 }, + { .compatible = "ti,pcm1863", .data = (void *)PCM1863 }, + { .compatible = "ti,pcm1864", .data = (void *)PCM1864 }, + { .compatible = "ti,pcm1865", .data = (void *)PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm186x_of_match); + +static int pcm186x_spi_probe(struct spi_device *spi) +{ + const enum pcm186x_type type = + (enum pcm186x_type)spi_get_device_id(spi)->driver_data; + int irq = spi->irq; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &pcm186x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm186x_probe(&spi->dev, type, irq, regmap); +} + +static int pcm186x_spi_remove(struct spi_device *spi) +{ + pcm186x_remove(&spi->dev); + + return 0; +} + +static const struct spi_device_id pcm186x_spi_id[] = { + { "pcm1862", PCM1862 }, + { "pcm1863", PCM1863 }, + { "pcm1864", PCM1864 }, + { "pcm1865", PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(spi, pcm186x_spi_id); + +static struct spi_driver pcm186x_spi_driver = { + .probe = pcm186x_spi_probe, + .remove = pcm186x_spi_remove, + .id_table = pcm186x_spi_id, + .driver = { + .name = "pcm186x", + .of_match_table = pcm186x_of_match, + }, +}; +module_spi_driver(pcm186x_spi_driver); + +MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("PCM186x Universal Audio ADC SPI Interface Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c new file mode 100644 index 000000000000..cdb51427facc --- /dev/null +++ b/sound/soc/codecs/pcm186x.c @@ -0,0 +1,719 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com + * Andreas Dannenberg <dannenberg@ti.com> + * Andrew F. Davis <afd@ti.com> + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "pcm186x.h" + +static const char * const pcm186x_supply_names[] = { + "avdd", /* Analog power supply. Connect to 3.3-V supply. */ + "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ + "iovdd", /* I/O power supply. Connect to 3.3-V or 1.8-V. */ +}; +#define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names) + +struct pcm186x_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES]; + unsigned int sysclk; + unsigned int tdm_offset; + bool is_tdm_mode; + bool is_master_mode; +}; + +static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 4000, 50); + +static const struct snd_kcontrol_new pcm1863_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L, + PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0, + pcm186x_pga_tlv), +}; + +static const struct snd_kcontrol_new pcm1865_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L, + PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0, + pcm186x_pga_tlv), + SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L, + PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0, + pcm186x_pga_tlv), +}; + +static const unsigned int pcm186x_adc_input_channel_sel_value[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x20, 0x30 +}; + +static const char * const pcm186x_adcl_input_channel_sel_text[] = { + "No Select", + "VINL1[SE]", /* Default for ADC1L */ + "VINL2[SE]", /* Default for ADC2L */ + "VINL2[SE] + VINL1[SE]", + "VINL3[SE]", + "VINL3[SE] + VINL1[SE]", + "VINL3[SE] + VINL2[SE]", + "VINL3[SE] + VINL2[SE] + VINL1[SE]", + "VINL4[SE]", + "VINL4[SE] + VINL1[SE]", + "VINL4[SE] + VINL2[SE]", + "VINL4[SE] + VINL2[SE] + VINL1[SE]", + "VINL4[SE] + VINL3[SE]", + "VINL4[SE] + VINL3[SE] + VINL1[SE]", + "VINL4[SE] + VINL3[SE] + VINL2[SE]", + "VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]", + "{VIN1P, VIN1M}[DIFF]", + "{VIN4P, VIN4M}[DIFF]", + "{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]" +}; + +static const char * const pcm186x_adcr_input_channel_sel_text[] = { + "No Select", + "VINR1[SE]", /* Default for ADC1R */ + "VINR2[SE]", /* Default for ADC2R */ + "VINR2[SE] + VINR1[SE]", + "VINR3[SE]", + "VINR3[SE] + VINR1[SE]", + "VINR3[SE] + VINR2[SE]", + "VINR3[SE] + VINR2[SE] + VINR1[SE]", + "VINR4[SE]", + "VINR4[SE] + VINR1[SE]", + "VINR4[SE] + VINR2[SE]", + "VINR4[SE] + VINR2[SE] + VINR1[SE]", + "VINR4[SE] + VINR3[SE]", + "VINR4[SE] + VINR3[SE] + VINR1[SE]", + "VINR4[SE] + VINR3[SE] + VINR2[SE]", + "VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]", + "{VIN2P, VIN2M}[DIFF]", + "{VIN3P, VIN3M}[DIFF]", + "{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]" +}; + +static const struct soc_enum pcm186x_adc_input_channel_sel[] = { + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text), + pcm186x_adcl_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text), + pcm186x_adcr_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text), + pcm186x_adcl_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text), + pcm186x_adcr_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), +}; + +static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = { + SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]), + SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]), + SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]), + SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]), +}; + +static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("VINL1"), + SND_SOC_DAPM_INPUT("VINR1"), + SND_SOC_DAPM_INPUT("VINL2"), + SND_SOC_DAPM_INPUT("VINR2"), + SND_SOC_DAPM_INPUT("VINL3"), + SND_SOC_DAPM_INPUT("VINR3"), + SND_SOC_DAPM_INPUT("VINL4"), + SND_SOC_DAPM_INPUT("VINR4"), + + SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[0]), + SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[1]), + + /* + * Put the codec into SLEEP mode when not in use, allowing the + * Energysense mechanism to operate. + */ + SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 0), +}; + +static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("VINL1"), + SND_SOC_DAPM_INPUT("VINR1"), + SND_SOC_DAPM_INPUT("VINL2"), + SND_SOC_DAPM_INPUT("VINR2"), + SND_SOC_DAPM_INPUT("VINL3"), + SND_SOC_DAPM_INPUT("VINR3"), + SND_SOC_DAPM_INPUT("VINL4"), + SND_SOC_DAPM_INPUT("VINR4"), + + SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[0]), + SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[1]), + SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[2]), + SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[3]), + + /* + * Put the codec into SLEEP mode when not in use, allowing the + * Energysense mechanism to operate. + */ + SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 0), + SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 0), +}; + +static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = { + { "ADC Left Capture Source", NULL, "VINL1" }, + { "ADC Left Capture Source", NULL, "VINR1" }, + { "ADC Left Capture Source", NULL, "VINL2" }, + { "ADC Left Capture Source", NULL, "VINR2" }, + { "ADC Left Capture Source", NULL, "VINL3" }, + { "ADC Left Capture Source", NULL, "VINR3" }, + { "ADC Left Capture Source", NULL, "VINL4" }, + { "ADC Left Capture Source", NULL, "VINR4" }, + + { "ADC", NULL, "ADC Left Capture Source" }, + + { "ADC Right Capture Source", NULL, "VINL1" }, + { "ADC Right Capture Source", NULL, "VINR1" }, + { "ADC Right Capture Source", NULL, "VINL2" }, + { "ADC Right Capture Source", NULL, "VINR2" }, + { "ADC Right Capture Source", NULL, "VINL3" }, + { "ADC Right Capture Source", NULL, "VINR3" }, + { "ADC Right Capture Source", NULL, "VINL4" }, + { "ADC Right Capture Source", NULL, "VINR4" }, + + { "ADC", NULL, "ADC Right Capture Source" }, +}; + +static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = { + { "ADC1 Left Capture Source", NULL, "VINL1" }, + { "ADC1 Left Capture Source", NULL, "VINR1" }, + { "ADC1 Left Capture Source", NULL, "VINL2" }, + { "ADC1 Left Capture Source", NULL, "VINR2" }, + { "ADC1 Left Capture Source", NULL, "VINL3" }, + { "ADC1 Left Capture Source", NULL, "VINR3" }, + { "ADC1 Left Capture Source", NULL, "VINL4" }, + { "ADC1 Left Capture Source", NULL, "VINR4" }, + + { "ADC1", NULL, "ADC1 Left Capture Source" }, + + { "ADC1 Right Capture Source", NULL, "VINL1" }, + { "ADC1 Right Capture Source", NULL, "VINR1" }, + { "ADC1 Right Capture Source", NULL, "VINL2" }, + { "ADC1 Right Capture Source", NULL, "VINR2" }, + { "ADC1 Right Capture Source", NULL, "VINL3" }, + { "ADC1 Right Capture Source", NULL, "VINR3" }, + { "ADC1 Right Capture Source", NULL, "VINL4" }, + { "ADC1 Right Capture Source", NULL, "VINR4" }, + + { "ADC1", NULL, "ADC1 Right Capture Source" }, + + { "ADC2 Left Capture Source", NULL, "VINL1" }, + { "ADC2 Left Capture Source", NULL, "VINR1" }, + { "ADC2 Left Capture Source", NULL, "VINL2" }, + { "ADC2 Left Capture Source", NULL, "VINR2" }, + { "ADC2 Left Capture Source", NULL, "VINL3" }, + { "ADC2 Left Capture Source", NULL, "VINR3" }, + { "ADC2 Left Capture Source", NULL, "VINL4" }, + { "ADC2 Left Capture Source", NULL, "VINR4" }, + + { "ADC2", NULL, "ADC2 Left Capture Source" }, + + { "ADC2 Right Capture Source", NULL, "VINL1" }, + { "ADC2 Right Capture Source", NULL, "VINR1" }, + { "ADC2 Right Capture Source", NULL, "VINL2" }, + { "ADC2 Right Capture Source", NULL, "VINR2" }, + { "ADC2 Right Capture Source", NULL, "VINL3" }, + { "ADC2 Right Capture Source", NULL, "VINR3" }, + { "ADC2 Right Capture Source", NULL, "VINL4" }, + { "ADC2 Right Capture Source", NULL, "VINR4" }, + + { "ADC2", NULL, "ADC2 Right Capture Source" }, +}; + +static int pcm186x_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 pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int rate = params_rate(params); + unsigned int format = params_format(params); + unsigned int width = params_width(params); + unsigned int channels = params_channels(params); + unsigned int div_lrck; + unsigned int div_bck; + u8 tdm_tx_sel = 0; + u8 pcm_cfg = 0; + + dev_dbg(codec->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n", + __func__, rate, format, width, channels); + + switch (width) { + case 16: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_16 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + case 20: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_20 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + case 24: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_24 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + case 32: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_32 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, PCM186X_PCM_CFG, + PCM186X_PCM_CFG_RX_WLEN_MASK | + PCM186X_PCM_CFG_TX_WLEN_MASK, + pcm_cfg); + + div_lrck = width * channels; + + if (priv->is_tdm_mode) { + /* Select TDM transmission data */ + switch (channels) { + case 2: + tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH; + break; + case 4: + tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH; + break; + case 6: + tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, PCM186X_TDM_TX_SEL, + PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel); + + /* In DSP/TDM mode, the LRCLK divider must be 256 */ + div_lrck = 256; + + /* Configure 1/256 duty cycle for LRCK */ + snd_soc_update_bits(codec, PCM186X_PCM_CFG, + PCM186X_PCM_CFG_TDM_LRCK_MODE, + PCM186X_PCM_CFG_TDM_LRCK_MODE); + } + + /* Only configure clock dividers in master mode. */ + if (priv->is_master_mode) { + div_bck = priv->sysclk / (div_lrck * rate); + + dev_dbg(codec->dev, + "%s() master_clk=%u div_bck=%u div_lrck=%u\n", + __func__, priv->sysclk, div_bck, div_lrck); + + snd_soc_write(codec, PCM186X_BCK_DIV, div_bck - 1); + snd_soc_write(codec, PCM186X_LRK_DIV, div_lrck - 1); + } + + return 0; +} + +static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); + u8 clk_ctrl = 0; + u8 pcm_cfg = 0; + + dev_dbg(codec->dev, "%s() format=0x%x\n", __func__, format); + + /* set master/slave audio interface */ + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + if (!priv->sysclk) { + dev_err(codec->dev, "operating in master mode requires sysclock to be configured\n"); + return -EINVAL; + } + clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE; + priv->is_master_mode = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + priv->is_master_mode = false; + break; + default: + dev_err(codec->dev, "Invalid DAI master/slave interface\n"); + return -EINVAL; + } + + /* set interface polarity */ + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(codec->dev, "Inverted DAI clocks not supported\n"); + return -EINVAL; + } + + /* set interface format */ + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + pcm_cfg = PCM186X_PCM_CFG_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + priv->tdm_offset += 1; + /* Fall through... DSP_A uses the same basic config as DSP_B + * except we need to shift the TDM output by one BCK cycle + */ + case SND_SOC_DAIFMT_DSP_B: + priv->is_tdm_mode = true; + pcm_cfg = PCM186X_PCM_CFG_FMT_TDM; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, PCM186X_CLK_CTRL, + PCM186X_CLK_CTRL_MST_MODE, clk_ctrl); + + snd_soc_write(codec, PCM186X_TDM_TX_OFFSET, priv->tdm_offset); + + snd_soc_update_bits(codec, PCM186X_PCM_CFG, + PCM186X_PCM_CFG_FMT_MASK, pcm_cfg); + + return 0; +} + +static int pcm186x_set_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 pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int first_slot, last_slot, tdm_offset; + + dev_dbg(codec->dev, + "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + if (!tx_mask) { + dev_err(codec->dev, "tdm tx mask must not be 0\n"); + return -EINVAL; + } + + first_slot = __ffs(tx_mask); + last_slot = __fls(tx_mask); + + if (last_slot - first_slot != hweight32(tx_mask) - 1) { + dev_err(codec->dev, "tdm tx mask must be contiguous\n"); + return -EINVAL; + } + + tdm_offset = first_slot * slot_width; + + if (tdm_offset > 255) { + dev_err(codec->dev, "tdm tx slot selection out of bounds\n"); + return -EINVAL; + } + + priv->tdm_offset = tdm_offset; + + return 0; +} + +static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s() clk_id=%d freq=%u dir=%d\n", + __func__, clk_id, freq, dir); + + priv->sysclk = freq; + + return 0; +} + +static const struct snd_soc_dai_ops pcm186x_dai_ops = { + .set_sysclk = pcm186x_set_dai_sysclk, + .set_tdm_slot = pcm186x_set_tdm_slot, + .set_fmt = pcm186x_set_fmt, + .hw_params = pcm186x_hw_params, +}; + +static struct snd_soc_dai_driver pcm1863_dai = { + .name = "pcm1863-aif", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = PCM186X_RATES, + .formats = PCM186X_FORMATS, + }, + .ops = &pcm186x_dai_ops, +}; + +static struct snd_soc_dai_driver pcm1865_dai = { + .name = "pcm1865-aif", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = PCM186X_RATES, + .formats = PCM186X_FORMATS, + }, + .ops = &pcm186x_dai_ops, +}; + +static int pcm186x_power_on(struct snd_soc_codec *codec) +{ + struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) + return ret; + + regcache_cache_only(priv->regmap, false); + ret = regcache_sync(priv->regmap); + if (ret) { + dev_err(codec->dev, "Failed to restore cache\n"); + regcache_cache_only(priv->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); + return ret; + } + + snd_soc_update_bits(codec, PCM186X_POWER_CTRL, + PCM186X_PWR_CTRL_PWRDN, 0); + + return 0; +} + +static int pcm186x_power_off(struct snd_soc_codec *codec) +{ + struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + snd_soc_update_bits(codec, PCM186X_POWER_CTRL, + PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN); + + regcache_cache_only(priv->regmap, true); + + ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) + return ret; + + return 0; +} + +static int pcm186x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__, + snd_soc_codec_get_bias_level(codec), level); + + 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) + pcm186x_power_on(codec); + break; + case SND_SOC_BIAS_OFF: + pcm186x_power_off(codec); + break; + } + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_pcm1863 = { + .set_bias_level = pcm186x_set_bias_level, + + .component_driver = { + .controls = pcm1863_snd_controls, + .num_controls = ARRAY_SIZE(pcm1863_snd_controls), + .dapm_widgets = pcm1863_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1863_dapm_widgets), + .dapm_routes = pcm1863_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1863_dapm_routes), + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm1865 = { + .set_bias_level = pcm186x_set_bias_level, + .suspend_bias_off = true, + + .component_driver = { + .controls = pcm1865_snd_controls, + .num_controls = ARRAY_SIZE(pcm1865_snd_controls), + .dapm_widgets = pcm1865_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1865_dapm_widgets), + .dapm_routes = pcm1865_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1865_dapm_routes), + }, +}; + +static bool pcm186x_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM186X_PAGE: + case PCM186X_DEVICE_STATUS: + case PCM186X_FSAMPLE_STATUS: + case PCM186X_DIV_STATUS: + case PCM186X_CLK_STATUS: + case PCM186X_SUPPLY_STATUS: + case PCM186X_MMAP_STAT_CTRL: + case PCM186X_MMAP_ADDRESS: + return true; + } + + return false; +} + +static const struct regmap_range_cfg pcm186x_range = { + .name = "Pages", + .range_max = PCM186X_MAX_REGISTER, + .selector_reg = PCM186X_PAGE, + .selector_mask = 0xff, + .window_len = PCM186X_PAGE_LEN, +}; + +const struct regmap_config pcm186x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .volatile_reg = pcm186x_volatile, + + .ranges = &pcm186x_range, + .num_ranges = 1, + + .max_register = PCM186X_MAX_REGISTER, + + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(pcm186x_regmap); + +int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq, + struct regmap *regmap) +{ + struct pcm186x_priv *priv; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + priv->regmap = regmap; + + for (i = 0; i < ARRAY_SIZE(priv->supplies); i++) + priv->supplies[i].supply = pcm186x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "failed enable supplies: %d\n", ret); + return ret; + } + + /* Reset device registers for a consistent power-on like state */ + ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET); + if (ret) { + dev_err(dev, "failed to write device: %d\n", ret); + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "failed disable supplies: %d\n", ret); + return ret; + } + + switch (type) { + case PCM1865: + case PCM1864: + ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1865, + &pcm1865_dai, 1); + break; + case PCM1863: + case PCM1862: + default: + ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1863, + &pcm1863_dai, 1); + } + if (ret) { + dev_err(dev, "failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pcm186x_probe); + +int pcm186x_remove(struct device *dev) +{ + snd_soc_unregister_codec(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(pcm186x_remove); + +MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm186x.h b/sound/soc/codecs/pcm186x.h new file mode 100644 index 000000000000..b630111bb3c4 --- /dev/null +++ b/sound/soc/codecs/pcm186x.h @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com + * Andreas Dannenberg <dannenberg@ti.com> + * Andrew F. Davis <afd@ti.com> + */ + +#ifndef _PCM186X_H_ +#define _PCM186X_H_ + +#include <linux/pm.h> +#include <linux/regmap.h> + +enum pcm186x_type { + PCM1862, + PCM1863, + PCM1864, + PCM1865, +}; + +#define PCM186X_RATES SNDRV_PCM_RATE_8000_192000 +#define PCM186X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define PCM186X_PAGE_LEN 0x0100 +#define PCM186X_PAGE_BASE(n) (PCM186X_PAGE_LEN * n) + +/* The page selection register address is the same on all pages */ +#define PCM186X_PAGE 0 + +/* Register Definitions - Page 0 */ +#define PCM186X_PGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 1) +#define PCM186X_PGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 2) +#define PCM186X_PGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 3) +#define PCM186X_PGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 4) +#define PCM186X_PGA_CTRL (PCM186X_PAGE_BASE(0) + 5) +#define PCM186X_ADC1_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 6) +#define PCM186X_ADC1_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 7) +#define PCM186X_ADC2_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 8) +#define PCM186X_ADC2_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 9) +#define PCM186X_AUXADC_INPUT_SEL (PCM186X_PAGE_BASE(0) + 10) +#define PCM186X_PCM_CFG (PCM186X_PAGE_BASE(0) + 11) +#define PCM186X_TDM_TX_SEL (PCM186X_PAGE_BASE(0) + 12) +#define PCM186X_TDM_TX_OFFSET (PCM186X_PAGE_BASE(0) + 13) +#define PCM186X_TDM_RX_OFFSET (PCM186X_PAGE_BASE(0) + 14) +#define PCM186X_DPGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 15) +#define PCM186X_GPIO1_0_CTRL (PCM186X_PAGE_BASE(0) + 16) +#define PCM186X_GPIO3_2_CTRL (PCM186X_PAGE_BASE(0) + 17) +#define PCM186X_GPIO1_0_DIR_CTRL (PCM186X_PAGE_BASE(0) + 18) +#define PCM186X_GPIO3_2_DIR_CTRL (PCM186X_PAGE_BASE(0) + 19) +#define PCM186X_GPIO_IN_OUT (PCM186X_PAGE_BASE(0) + 20) +#define PCM186X_GPIO_PULL_CTRL (PCM186X_PAGE_BASE(0) + 21) +#define PCM186X_DPGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 22) +#define PCM186X_DPGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 23) +#define PCM186X_DPGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 24) +#define PCM186X_DPGA_GAIN_CTRL (PCM186X_PAGE_BASE(0) + 25) +#define PCM186X_DPGA_MIC_CTRL (PCM186X_PAGE_BASE(0) + 26) +#define PCM186X_DIN_RESAMP_CTRL (PCM186X_PAGE_BASE(0) + 27) +#define PCM186X_CLK_CTRL (PCM186X_PAGE_BASE(0) + 32) +#define PCM186X_DSP1_CLK_DIV (PCM186X_PAGE_BASE(0) + 33) +#define PCM186X_DSP2_CLK_DIV (PCM186X_PAGE_BASE(0) + 34) +#define PCM186X_ADC_CLK_DIV (PCM186X_PAGE_BASE(0) + 35) +#define PCM186X_PLL_SCK_DIV (PCM186X_PAGE_BASE(0) + 37) +#define PCM186X_BCK_DIV (PCM186X_PAGE_BASE(0) + 38) +#define PCM186X_LRK_DIV (PCM186X_PAGE_BASE(0) + 39) +#define PCM186X_PLL_CTRL (PCM186X_PAGE_BASE(0) + 40) +#define PCM186X_PLL_P_DIV (PCM186X_PAGE_BASE(0) + 41) +#define PCM186X_PLL_R_DIV (PCM186X_PAGE_BASE(0) + 42) +#define PCM186X_PLL_J_DIV (PCM186X_PAGE_BASE(0) + 43) +#define PCM186X_PLL_D_DIV_LSB (PCM186X_PAGE_BASE(0) + 44) +#define PCM186X_PLL_D_DIV_MSB (PCM186X_PAGE_BASE(0) + 45) +#define PCM186X_SIGDET_MODE (PCM186X_PAGE_BASE(0) + 48) +#define PCM186X_SIGDET_MASK (PCM186X_PAGE_BASE(0) + 49) +#define PCM186X_SIGDET_STAT (PCM186X_PAGE_BASE(0) + 50) +#define PCM186X_SIGDET_LOSS_TIME (PCM186X_PAGE_BASE(0) + 52) +#define PCM186X_SIGDET_SCAN_TIME (PCM186X_PAGE_BASE(0) + 53) +#define PCM186X_SIGDET_INT_INTVL (PCM186X_PAGE_BASE(0) + 54) +#define PCM186X_SIGDET_DC_REF_CH1_L (PCM186X_PAGE_BASE(0) + 64) +#define PCM186X_SIGDET_DC_DIFF_CH1_L (PCM186X_PAGE_BASE(0) + 65) +#define PCM186X_SIGDET_DC_LEV_CH1_L (PCM186X_PAGE_BASE(0) + 66) +#define PCM186X_SIGDET_DC_REF_CH1_R (PCM186X_PAGE_BASE(0) + 67) +#define PCM186X_SIGDET_DC_DIFF_CH1_R (PCM186X_PAGE_BASE(0) + 68) +#define PCM186X_SIGDET_DC_LEV_CH1_R (PCM186X_PAGE_BASE(0) + 69) +#define PCM186X_SIGDET_DC_REF_CH2_L (PCM186X_PAGE_BASE(0) + 70) +#define PCM186X_SIGDET_DC_DIFF_CH2_L (PCM186X_PAGE_BASE(0) + 71) +#define PCM186X_SIGDET_DC_LEV_CH2_L (PCM186X_PAGE_BASE(0) + 72) +#define PCM186X_SIGDET_DC_REF_CH2_R (PCM186X_PAGE_BASE(0) + 73) +#define PCM186X_SIGDET_DC_DIFF_CH2_R (PCM186X_PAGE_BASE(0) + 74) +#define PCM186X_SIGDET_DC_LEV_CH2_R (PCM186X_PAGE_BASE(0) + 75) +#define PCM186X_SIGDET_DC_REF_CH3_L (PCM186X_PAGE_BASE(0) + 76) +#define PCM186X_SIGDET_DC_DIFF_CH3_L (PCM186X_PAGE_BASE(0) + 77) +#define PCM186X_SIGDET_DC_LEV_CH3_L (PCM186X_PAGE_BASE(0) + 78) +#define PCM186X_SIGDET_DC_REF_CH3_R (PCM186X_PAGE_BASE(0) + 79) +#define PCM186X_SIGDET_DC_DIFF_CH3_R (PCM186X_PAGE_BASE(0) + 80) +#define PCM186X_SIGDET_DC_LEV_CH3_R (PCM186X_PAGE_BASE(0) + 81) +#define PCM186X_SIGDET_DC_REF_CH4_L (PCM186X_PAGE_BASE(0) + 82) +#define PCM186X_SIGDET_DC_DIFF_CH4_L (PCM186X_PAGE_BASE(0) + 83) +#define PCM186X_SIGDET_DC_LEV_CH4_L (PCM186X_PAGE_BASE(0) + 84) +#define PCM186X_SIGDET_DC_REF_CH4_R (PCM186X_PAGE_BASE(0) + 85) +#define PCM186X_SIGDET_DC_DIFF_CH4_R (PCM186X_PAGE_BASE(0) + 86) +#define PCM186X_SIGDET_DC_LEV_CH4_R (PCM186X_PAGE_BASE(0) + 87) +#define PCM186X_AUXADC_DATA_CTRL (PCM186X_PAGE_BASE(0) + 88) +#define PCM186X_AUXADC_DATA_LSB (PCM186X_PAGE_BASE(0) + 89) +#define PCM186X_AUXADC_DATA_MSB (PCM186X_PAGE_BASE(0) + 90) +#define PCM186X_INT_ENABLE (PCM186X_PAGE_BASE(0) + 96) +#define PCM186X_INT_FLAG (PCM186X_PAGE_BASE(0) + 97) +#define PCM186X_INT_POL_WIDTH (PCM186X_PAGE_BASE(0) + 98) +#define PCM186X_POWER_CTRL (PCM186X_PAGE_BASE(0) + 112) +#define PCM186X_FILTER_MUTE_CTRL (PCM186X_PAGE_BASE(0) + 113) +#define PCM186X_DEVICE_STATUS (PCM186X_PAGE_BASE(0) + 114) +#define PCM186X_FSAMPLE_STATUS (PCM186X_PAGE_BASE(0) + 115) +#define PCM186X_DIV_STATUS (PCM186X_PAGE_BASE(0) + 116) +#define PCM186X_CLK_STATUS (PCM186X_PAGE_BASE(0) + 117) +#define PCM186X_SUPPLY_STATUS (PCM186X_PAGE_BASE(0) + 120) + +/* Register Definitions - Page 1 */ +#define PCM186X_MMAP_STAT_CTRL (PCM186X_PAGE_BASE(1) + 1) +#define PCM186X_MMAP_ADDRESS (PCM186X_PAGE_BASE(1) + 2) +#define PCM186X_MEM_WDATA0 (PCM186X_PAGE_BASE(1) + 4) +#define PCM186X_MEM_WDATA1 (PCM186X_PAGE_BASE(1) + 5) +#define PCM186X_MEM_WDATA2 (PCM186X_PAGE_BASE(1) + 6) +#define PCM186X_MEM_WDATA3 (PCM186X_PAGE_BASE(1) + 7) +#define PCM186X_MEM_RDATA0 (PCM186X_PAGE_BASE(1) + 8) +#define PCM186X_MEM_RDATA1 (PCM186X_PAGE_BASE(1) + 9) +#define PCM186X_MEM_RDATA2 (PCM186X_PAGE_BASE(1) + 10) +#define PCM186X_MEM_RDATA3 (PCM186X_PAGE_BASE(1) + 11) + +/* Register Definitions - Page 3 */ +#define PCM186X_OSC_PWR_DOWN_CTRL (PCM186X_PAGE_BASE(3) + 18) +#define PCM186X_MIC_BIAS_CTRL (PCM186X_PAGE_BASE(3) + 21) + +/* Register Definitions - Page 253 */ +#define PCM186X_CURR_TRIM_CTRL (PCM186X_PAGE_BASE(253) + 20) + +#define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL + +/* PCM186X_PAGE */ +#define PCM186X_RESET 0xff + +/* PCM186X_ADCX_INPUT_SEL_X */ +#define PCM186X_ADC_INPUT_SEL_POL BIT(7) +#define PCM186X_ADC_INPUT_SEL_MASK GENMASK(5, 0) + +/* PCM186X_PCM_CFG */ +#define PCM186X_PCM_CFG_RX_WLEN_MASK GENMASK(7, 6) +#define PCM186X_PCM_CFG_RX_WLEN_SHIFT 6 +#define PCM186X_PCM_CFG_RX_WLEN_32 0x00 +#define PCM186X_PCM_CFG_RX_WLEN_24 0x01 +#define PCM186X_PCM_CFG_RX_WLEN_20 0x02 +#define PCM186X_PCM_CFG_RX_WLEN_16 0x03 +#define PCM186X_PCM_CFG_TDM_LRCK_MODE BIT(4) +#define PCM186X_PCM_CFG_TX_WLEN_MASK GENMASK(3, 2) +#define PCM186X_PCM_CFG_TX_WLEN_SHIFT 2 +#define PCM186X_PCM_CFG_TX_WLEN_32 0x00 +#define PCM186X_PCM_CFG_TX_WLEN_24 0x01 +#define PCM186X_PCM_CFG_TX_WLEN_20 0x02 +#define PCM186X_PCM_CFG_TX_WLEN_16 0x03 +#define PCM186X_PCM_CFG_FMT_MASK GENMASK(1, 0) +#define PCM186X_PCM_CFG_FMT_SHIFT 0 +#define PCM186X_PCM_CFG_FMT_I2S 0x00 +#define PCM186X_PCM_CFG_FMT_LEFTJ 0x01 +#define PCM186X_PCM_CFG_FMT_RIGHTJ 0x02 +#define PCM186X_PCM_CFG_FMT_TDM 0x03 + +/* PCM186X_TDM_TX_SEL */ +#define PCM186X_TDM_TX_SEL_2CH 0x00 +#define PCM186X_TDM_TX_SEL_4CH 0x01 +#define PCM186X_TDM_TX_SEL_6CH 0x02 +#define PCM186X_TDM_TX_SEL_MASK 0x03 + +/* PCM186X_CLK_CTRL */ +#define PCM186X_CLK_CTRL_SCK_XI_SEL1 BIT(7) +#define PCM186X_CLK_CTRL_SCK_XI_SEL0 BIT(6) +#define PCM186X_CLK_CTRL_SCK_SRC_PLL BIT(5) +#define PCM186X_CLK_CTRL_MST_MODE BIT(4) +#define PCM186X_CLK_CTRL_ADC_SRC_PLL BIT(3) +#define PCM186X_CLK_CTRL_DSP2_SRC_PLL BIT(2) +#define PCM186X_CLK_CTRL_DSP1_SRC_PLL BIT(1) +#define PCM186X_CLK_CTRL_CLKDET_EN BIT(0) + +/* PCM186X_PLL_CTRL */ +#define PCM186X_PLL_CTRL_LOCK BIT(4) +#define PCM186X_PLL_CTRL_REF_SEL BIT(1) +#define PCM186X_PLL_CTRL_EN BIT(0) + +/* PCM186X_POWER_CTRL */ +#define PCM186X_PWR_CTRL_PWRDN BIT(2) +#define PCM186X_PWR_CTRL_SLEEP BIT(1) +#define PCM186X_PWR_CTRL_STBY BIT(0) + +/* PCM186X_CLK_STATUS */ +#define PCM186X_CLK_STATUS_LRCKHLT BIT(6) +#define PCM186X_CLK_STATUS_BCKHLT BIT(5) +#define PCM186X_CLK_STATUS_SCKHLT BIT(4) +#define PCM186X_CLK_STATUS_LRCKERR BIT(2) +#define PCM186X_CLK_STATUS_BCKERR BIT(1) +#define PCM186X_CLK_STATUS_SCKERR BIT(0) + +/* PCM186X_SUPPLY_STATUS */ +#define PCM186X_SUPPLY_STATUS_DVDD BIT(2) +#define PCM186X_SUPPLY_STATUS_AVDD BIT(1) +#define PCM186X_SUPPLY_STATUS_LDO BIT(0) + +/* PCM186X_MMAP_STAT_CTRL */ +#define PCM186X_MMAP_STAT_DONE BIT(4) +#define PCM186X_MMAP_STAT_BUSY BIT(2) +#define PCM186X_MMAP_STAT_R_REQ BIT(1) +#define PCM186X_MMAP_STAT_W_REQ BIT(0) + +extern const struct regmap_config pcm186x_regmap; + +int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq, + struct regmap *regmap); +int pcm186x_remove(struct device *dev); + +#endif /* _PCM186X_H_ */ diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index 25c63510ae15..7cdd2dc4fd79 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -70,3 +70,7 @@ static struct spi_driver pcm512x_spi_driver = { }; module_spi_driver(pcm512x_spi_driver); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver - SPI"); +MODULE_AUTHOR("Mark Brown <broonie@kernel.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index 974a9040651d..7ef3b5476bcc 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/regmap.h> +#include <linux/gcd.h> #include "rl6231.h" /** @@ -106,6 +107,25 @@ static const struct pll_calc_map pll_preset_table[] = { {19200000, 24576000, 3, 30, 3, false}, }; +static unsigned int find_best_div(unsigned int in, + unsigned int max, unsigned int div) +{ + unsigned int d; + + if (in <= max) + return 1; + + d = in / max; + if (in % max) + d++; + + while (div % d != 0) + d++; + + + return d; +} + /** * rl6231_pll_calc - Calcualte PLL M/N/K code. * @freq_in: external clock provided to codec. @@ -120,9 +140,11 @@ int rl6231_pll_calc(const unsigned int freq_in, const unsigned int freq_out, struct rl6231_pll_code *pll_code) { int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX; - int i, k, red, n_t, pll_out, in_t, out_t; - int n = 0, m = 0, m_t = 0; - int red_t = abs(freq_out - freq_in); + int i, k, n_t; + int k_t, min_k, max_k, n = 0, m = 0, m_t = 0; + unsigned int red, pll_out, in_t, out_t, div, div_t; + unsigned int red_t = abs(freq_out - freq_in); + unsigned int f_in, f_out, f_max; bool bypass = false; if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in) @@ -140,39 +162,52 @@ int rl6231_pll_calc(const unsigned int freq_in, } } - k = 100000000 / freq_out - 2; - if (k > RL6231_PLL_K_MAX) - k = RL6231_PLL_K_MAX; - for (n_t = 0; n_t <= max_n; n_t++) { - in_t = freq_in / (k + 2); - pll_out = freq_out / (n_t + 2); - if (in_t < 0) - continue; - if (in_t == pll_out) { - bypass = true; - n = n_t; - goto code_find; - } - red = abs(in_t - pll_out); - if (red < red_t) { - bypass = true; - n = n_t; - m = m_t; - if (red == 0) + min_k = 80000000 / freq_out - 2; + max_k = 150000000 / freq_out - 2; + if (max_k > RL6231_PLL_K_MAX) + max_k = RL6231_PLL_K_MAX; + if (min_k > RL6231_PLL_K_MAX) + min_k = max_k = RL6231_PLL_K_MAX; + div_t = gcd(freq_in, freq_out); + f_max = 0xffffffff / RL6231_PLL_N_MAX; + div = find_best_div(freq_in, f_max, div_t); + f_in = freq_in / div; + f_out = freq_out / div; + k = min_k; + for (k_t = min_k; k_t <= max_k; k_t++) { + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = f_in * (n_t + 2); + pll_out = f_out * (k_t + 2); + if (in_t == pll_out) { + bypass = true; + n = n_t; + k = k_t; goto code_find; - red_t = red; - } - for (m_t = 0; m_t <= max_m; m_t++) { - out_t = in_t / (m_t + 2); - red = abs(out_t - pll_out); + } + out_t = in_t / (k_t + 2); + red = abs(f_out - out_t); if (red < red_t) { - bypass = false; + bypass = true; n = n_t; - m = m_t; + m = 0; + k = k_t; if (red == 0) goto code_find; red_t = red; } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / ((m_t + 2) * (k_t + 2)); + red = abs(f_out - out_t); + if (red < red_t) { + bypass = false; + n = n_t; + m = m_t; + k = k_t; + if (red == 0) + goto code_find; + red_t = red; + } + } } } pr_debug("Only get approximation about PLL\n"); diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 2df91db765ac..2144edca97b0 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -289,6 +289,8 @@ static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform) dev_err(&rt5514_spi->dev, "%s Failed to reguest IRQ: %d\n", __func__, ret); + else + device_init_wakeup(rt5514_dsp->dev, true); } return 0; @@ -379,6 +381,7 @@ int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len) return true; } +EXPORT_SYMBOL_GPL(rt5514_spi_burst_read); /** * rt5514_spi_burst_write - Write data to SPI by rt5514 address. @@ -456,8 +459,6 @@ static int rt5514_spi_probe(struct spi_device *spi) return ret; } - device_init_wakeup(&spi->dev, true); - return 0; } @@ -482,10 +483,13 @@ static int __maybe_unused rt5514_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - if (rt5514_dsp->substream) { - rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, sizeof(buf)); - if (buf[0] & RT5514_IRQ_STATUS_BIT) - rt5514_schedule_copy(rt5514_dsp); + if (rt5514_dsp) { + if (rt5514_dsp->substream) { + rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, + sizeof(buf)); + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + } } return 0; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 2a5b5d74e697..198df016802f 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -295,6 +295,33 @@ static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol, return 0; } +static int rt5514_calibration(struct rt5514_priv *rt5514, bool on) +{ + if (on) { + regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL3, 0x0000000a); + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf, + 0xa); + regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301, + 0x301); + regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL4, + 0x80000000 | rt5514->pll3_cal_value); + regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL1, + 0x8bb80800); + regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5, + 0xc0000000, 0x80000000); + regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5, + 0xc0000000, 0xc0000000); + } else { + regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5, + 0xc0000000, 0x40000000); + regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301, 0); + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf, + 0x4); + } + + return 0; +} + static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -302,6 +329,7 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); struct snd_soc_codec *codec = rt5514->codec; const struct firmware *fw = NULL; + u8 buf[8]; if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled) return 0; @@ -310,6 +338,35 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, rt5514->dsp_enabled = ucontrol->value.integer.value[0]; if (rt5514->dsp_enabled) { + if (rt5514->pdata.dsp_calib_clk_name && + !IS_ERR(rt5514->dsp_calib_clk)) { + if (clk_set_rate(rt5514->dsp_calib_clk, + rt5514->pdata.dsp_calib_clk_rate)) + dev_err(codec->dev, + "Can't set rate for mclk"); + + if (clk_prepare_enable(rt5514->dsp_calib_clk)) + dev_err(codec->dev, + "Can't enable dsp_calib_clk"); + + rt5514_calibration(rt5514, true); + + msleep(20); +#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI) + rt5514_spi_burst_read(RT5514_PLL3_CALIB_CTRL6 | + RT5514_DSP_MAPPING, + (u8 *)&buf, sizeof(buf)); +#else + dev_err(codec->dev, "There is no SPI driver for" + " loading the firmware\n"); +#endif + rt5514->pll3_cal_value = buf[0] | buf[1] << 8 | + buf[2] << 16 | buf[3] << 24; + + rt5514_calibration(rt5514, false); + clk_disable_unprepare(rt5514->dsp_calib_clk); + } + rt5514_enable_dsp_prepare(rt5514); request_firmware(&fw, RT5514_FIRMWARE1, codec->dev); @@ -341,6 +398,20 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, /* DSP run */ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055148); + + if (rt5514->pdata.dsp_calib_clk_name && + !IS_ERR(rt5514->dsp_calib_clk)) { + msleep(20); + + regmap_write(rt5514->i2c_regmap, 0x1800211c, + rt5514->pll3_cal_value); + regmap_write(rt5514->i2c_regmap, 0x18002124, + 0x00220012); + regmap_write(rt5514->i2c_regmap, 0x18002124, + 0x80220042); + regmap_write(rt5514->i2c_regmap, 0x18002124, + 0xe0220042); + } } else { regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch)); @@ -496,7 +567,7 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY_S("DMIC CLK", 1, SND_SOC_NOPM, 0, 0, rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1, @@ -1024,12 +1095,22 @@ static int rt5514_set_bias_level(struct snd_soc_codec *codec, static int rt5514_probe(struct snd_soc_codec *codec) { struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + struct platform_device *pdev = container_of(codec->dev, + struct platform_device, dev); rt5514->mclk = devm_clk_get(codec->dev, "mclk"); if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER) return -EPROBE_DEFER; + if (rt5514->pdata.dsp_calib_clk_name) { + rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev, + rt5514->pdata.dsp_calib_clk_name); + if (PTR_ERR(rt5514->dsp_calib_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + rt5514->codec = codec; + rt5514->pll3_cal_value = 0x0078b000; return 0; } @@ -1147,6 +1228,10 @@ static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev) { device_property_read_u32(dev, "realtek,dmic-init-delay-ms", &rt5514->pdata.dmic_init_delay); + device_property_read_string(dev, "realtek,dsp-calib-clk-name", + &rt5514->pdata.dsp_calib_clk_name); + device_property_read_u32(dev, "realtek,dsp-calib-clk-rate", + &rt5514->pdata.dsp_calib_clk_rate); return 0; } diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h index 2dc40e6d8b3f..f0f3400ce6b1 100644 --- a/sound/soc/codecs/rt5514.h +++ b/sound/soc/codecs/rt5514.h @@ -34,7 +34,9 @@ #define RT5514_CLK_CTRL1 0x2104 #define RT5514_CLK_CTRL2 0x2108 #define RT5514_PLL3_CALIB_CTRL1 0x2110 +#define RT5514_PLL3_CALIB_CTRL4 0x2120 #define RT5514_PLL3_CALIB_CTRL5 0x2124 +#define RT5514_PLL3_CALIB_CTRL6 0x2128 #define RT5514_DELAY_BUF_CTRL1 0x2140 #define RT5514_DELAY_BUF_CTRL3 0x2148 #define RT5514_ASRC_IN_CTRL1 0x2180 @@ -272,7 +274,7 @@ struct rt5514_priv { struct rt5514_platform_data pdata; struct snd_soc_codec *codec; struct regmap *i2c_regmap, *regmap; - struct clk *mclk; + struct clk *mclk, *dsp_calib_clk; int sysclk; int sysclk_src; int lrck; @@ -281,6 +283,7 @@ struct rt5514_priv { int pll_in; int pll_out; int dsp_enabled; + unsigned int pll3_cal_value; }; #endif /* __RT5514_H__ */ diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index f020d2d1eef4..8f140c8b93ac 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -1943,6 +1943,56 @@ static int rt5650_hp_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5645_set_micbias1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5645_GEN_CTRL2, + RT5645_MICBIAS1_POW_CTRL_SEL_MASK, + RT5645_MICBIAS1_POW_CTRL_SEL_M); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, RT5645_GEN_CTRL2, + RT5645_MICBIAS1_POW_CTRL_SEL_MASK, + RT5645_MICBIAS1_POW_CTRL_SEL_A); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_set_micbias2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5645_GEN_CTRL2, + RT5645_MICBIAS2_POW_CTRL_SEL_MASK, + RT5645_MICBIAS2_POW_CTRL_SEL_M); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, RT5645_GEN_CTRL2, + RT5645_MICBIAS2_POW_CTRL_SEL_MASK, + RT5645_MICBIAS2_POW_CTRL_SEL_A); + break; + + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER, RT5645_PWR_LDO2_BIT, 0, NULL, 0), @@ -1980,10 +2030,12 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { /* Input Side */ /* micbias */ - SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2, - RT5645_PWR_MB1_BIT, 0), - SND_SOC_DAPM_MICBIAS("micbias2", RT5645_PWR_ANLG2, - RT5645_PWR_MB2_BIT, 0), + SND_SOC_DAPM_SUPPLY("micbias1", RT5645_PWR_ANLG2, + RT5645_PWR_MB1_BIT, 0, rt5645_set_micbias1_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("micbias2", RT5645_PWR_ANLG2, + RT5645_PWR_MB2_BIT, 0, rt5645_set_micbias2_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), /* Input Lines */ SND_SOC_DAPM_INPUT("DMIC L1"), SND_SOC_DAPM_INPUT("DMIC R1"), @@ -3394,6 +3446,9 @@ static int rt5645_probe(struct snd_soc_codec *codec) snd_soc_dapm_sync(dapm); } + if (rt5645->pdata.long_name) + codec->component.card->long_name = rt5645->pdata.long_name; + rt5645->eq_param = devm_kzalloc(codec->dev, RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL); @@ -3570,63 +3625,74 @@ static const struct acpi_device_id rt5645_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match); #endif -static const struct rt5645_platform_data general_platform_data = { +static const struct rt5645_platform_data intel_braswell_platform_data = { .dmic1_data_pin = RT5645_DMIC1_DISABLE, .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, .jd_mode = 3, }; -static const struct dmi_system_id dmi_platform_intel_braswell[] = { +static const struct rt5645_platform_data buddy_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 3, + .level_trigger_irq = true, +}; + +static const struct rt5645_platform_data gpd_win_platform_data = { + .jd_mode = 3, + .inv_jd1_1 = true, + .long_name = "gpd-win-pocket-rt5645", + /* The GPD pocket has a diff. mic, for the win this does not matter. */ + .in2_diff = true, +}; + +static const struct rt5645_platform_data asus_t100ha_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_IN2N, + .dmic2_data_pin = RT5645_DMIC2_DISABLE, + .jd_mode = 3, + .inv_jd1_1 = true, +}; + +static const struct rt5645_platform_data jd_mode3_platform_data = { + .jd_mode = 3, +}; + +static const struct dmi_system_id dmi_platform_data[] = { + { + .ident = "Chrome Buddy", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"), + }, + .driver_data = (void *)&buddy_platform_data, + }, { .ident = "Intel Strago", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Strago"), }, + .driver_data = (void *)&intel_braswell_platform_data, }, { .ident = "Google Chrome", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), }, + .driver_data = (void *)&intel_braswell_platform_data, }, { .ident = "Google Setzer", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"), }, + .driver_data = (void *)&intel_braswell_platform_data, }, { .ident = "Microsoft Surface 3", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), }, + .driver_data = (void *)&intel_braswell_platform_data, }, - { } -}; - -static const struct rt5645_platform_data buddy_platform_data = { - .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, - .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, - .jd_mode = 3, - .level_trigger_irq = true, -}; - -static const struct dmi_system_id dmi_platform_intel_broadwell[] = { - { - .ident = "Chrome Buddy", - .matches = { - DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"), - }, - }, - { } -}; - -static const struct rt5645_platform_data gpd_win_platform_data = { - .jd_mode = 3, - .inv_jd1_1 = true, -}; - -static const struct dmi_system_id dmi_platform_gpd_win[] = { { /* * Match for the GPDwin which unfortunately uses somewhat @@ -3637,46 +3703,38 @@ static const struct dmi_system_id dmi_platform_gpd_win[] = { * the same default product_name. Also the GPDwin is the * only device to have both board_ and product_name not set. */ - .ident = "GPD Win", + .ident = "GPD Win / Pocket", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), DMI_MATCH(DMI_BOARD_NAME, "Default string"), DMI_MATCH(DMI_BOARD_SERIAL, "Default string"), DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), }, + .driver_data = (void *)&gpd_win_platform_data, }, - {} -}; - -static const struct rt5645_platform_data general_platform_data2 = { - .dmic1_data_pin = RT5645_DMIC_DATA_IN2N, - .dmic2_data_pin = RT5645_DMIC2_DISABLE, - .jd_mode = 3, - .inv_jd1_1 = true, -}; - -static const struct dmi_system_id dmi_platform_asus_t100ha[] = { { .ident = "ASUS T100HAN", .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "T100HAN"), }, + .driver_data = (void *)&asus_t100ha_platform_data, }, - { } -}; - -static const struct rt5645_platform_data minix_z83_4_platform_data = { - .jd_mode = 3, -}; - -static const struct dmi_system_id dmi_platform_minix_z83_4[] = { { .ident = "MINIX Z83-4", .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"), DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), }, + .driver_data = (void *)&jd_mode3_platform_data, + }, + { + .ident = "Teclast X80 Pro", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), + DMI_MATCH(DMI_PRODUCT_NAME, "X80 Pro"), + }, + .driver_data = (void *)&jd_mode3_platform_data, }, { } }; @@ -3684,9 +3742,9 @@ static const struct dmi_system_id dmi_platform_minix_z83_4[] = { static bool rt5645_check_dp(struct device *dev) { if (device_property_present(dev, "realtek,in2-differential") || - device_property_present(dev, "realtek,dmic1-data-pin") || - device_property_present(dev, "realtek,dmic2-data-pin") || - device_property_present(dev, "realtek,jd-mode")) + device_property_present(dev, "realtek,dmic1-data-pin") || + device_property_present(dev, "realtek,dmic2-data-pin") || + device_property_present(dev, "realtek,jd-mode")) return true; return false; @@ -3710,6 +3768,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev); + const struct dmi_system_id *dmi_data; struct rt5645_priv *rt5645; int ret, i; unsigned int val; @@ -3723,20 +3782,18 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, rt5645->i2c = i2c; i2c_set_clientdata(i2c, rt5645); + dmi_data = dmi_first_match(dmi_platform_data); + if (dmi_data) { + dev_info(&i2c->dev, "Detected %s platform\n", dmi_data->ident); + pdata = dmi_data->driver_data; + } + if (pdata) rt5645->pdata = *pdata; - else if (dmi_check_system(dmi_platform_intel_broadwell)) - rt5645->pdata = buddy_platform_data; else if (rt5645_check_dp(&i2c->dev)) rt5645_parse_dt(rt5645, &i2c->dev); - else if (dmi_check_system(dmi_platform_intel_braswell)) - rt5645->pdata = general_platform_data; - else if (dmi_check_system(dmi_platform_gpd_win)) - rt5645->pdata = gpd_win_platform_data; - else if (dmi_check_system(dmi_platform_asus_t100ha)) - rt5645->pdata = general_platform_data2; - else if (dmi_check_system(dmi_platform_minix_z83_4)) - rt5645->pdata = minix_z83_4_platform_data; + else + rt5645->pdata = jd_mode3_platform_data; if (quirk != -1) { rt5645->pdata.in2_diff = QUIRK_IN2_DIFF(quirk); @@ -3823,6 +3880,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, regmap_read(regmap, RT5645_VENDOR_ID, &val); rt5645->v_id = val & 0xff; + regmap_write(rt5645->regmap, RT5645_AD_DA_MIXER, 0x8080); + ret = regmap_register_patch(rt5645->regmap, init_list, ARRAY_SIZE(init_list)); if (ret != 0) diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index cfc5f97549eb..940325b28c29 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2117,6 +2117,12 @@ enum { #define RT5645_RXDC_SRC_STO (0x0 << 7) #define RT5645_RXDC_SRC_MONO (0x1 << 7) #define RT5645_RXDC_SRC_SFT (7) +#define RT5645_MICBIAS1_POW_CTRL_SEL_MASK (0x1 << 5) +#define RT5645_MICBIAS1_POW_CTRL_SEL_A (0x0 << 5) +#define RT5645_MICBIAS1_POW_CTRL_SEL_M (0x1 << 5) +#define RT5645_MICBIAS2_POW_CTRL_SEL_MASK (0x1 << 4) +#define RT5645_MICBIAS2_POW_CTRL_SEL_A (0x0 << 4) +#define RT5645_MICBIAS2_POW_CTRL_SEL_M (0x1 << 4) #define RT5645_RXDP2_SEL_MASK (0x1 << 3) #define RT5645_RXDP2_SEL_IF2 (0x0 << 3) #define RT5645_RXDP2_SEL_ADC (0x1 << 3) diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index b036c9dc0c8c..d329bf719d80 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -1560,6 +1560,10 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN); snd_soc_update_bits(codec, RT5663_IRQ_1, RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN); + snd_soc_update_bits(codec, RT5663_EM_JACK_TYPE_1, + RT5663_EM_JD_MASK, RT5663_EM_JD_RST); + snd_soc_update_bits(codec, RT5663_EM_JACK_TYPE_1, + RT5663_EM_JD_MASK, RT5663_EM_JD_NOR); while (true) { regmap_read(rt5663->regmap, RT5663_INT_ST_2, &val); diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h index c5a9b69579ad..03adc8004ba9 100644 --- a/sound/soc/codecs/rt5663.h +++ b/sound/soc/codecs/rt5663.h @@ -1029,6 +1029,10 @@ #define RT5663_POL_EXT_JD_SHIFT 10 #define RT5663_POL_EXT_JD_EN (0x1 << 10) #define RT5663_POL_EXT_JD_DIS (0x0 << 10) +#define RT5663_EM_JD_MASK (0x1 << 7) +#define RT5663_EM_JD_SHIFT 7 +#define RT5663_EM_JD_NOR (0x1 << 7) +#define RT5663_EM_JD_RST (0x0 << 7) /* DACREF LDO Control (0x0112)*/ #define RT5663_PWR_LDO_DACREFL_MASK (0x1 << 9) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index f2bb4feba3b6..e1ab5537d27a 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1332,10 +1332,13 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, sgtl5000->mclk = devm_clk_get(&client->dev, NULL); if (IS_ERR(sgtl5000->mclk)) { ret = PTR_ERR(sgtl5000->mclk); - dev_err(&client->dev, "Failed to get mclock: %d\n", ret); /* Defer the probe to see if the clk will be provided later */ if (ret == -ENOENT) ret = -EPROBE_DEFER; + + if (ret != -EPROBE_DEFER) + dev_err(&client->dev, "Failed to get mclock: %d\n", + ret); goto disable_regs; } @@ -1389,7 +1392,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, ana_pwr |= SGTL5000_LINEREG_D_POWERUP; dev_info(&client->dev, - "Using internal LDO instead of VDDD: check ER1\n"); + "Using internal LDO instead of VDDD: check ER1 erratum\n"); } else { /* using external LDO for VDDD * Clear startup powerup and simple powerup diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 354dc0d64f11..7b91ee267b4e 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -231,14 +231,17 @@ static struct snd_soc_dai_driver si476x_dai = { .ops = &si476x_dai_ops, }; -static struct regmap *si476x_get_regmap(struct device *dev) +static int si476x_probe(struct snd_soc_component *component) { - return dev_get_regmap(dev->parent, NULL); + snd_soc_component_init_regmap(component, + dev_get_regmap(component->dev->parent, NULL)); + + return 0; } static const struct snd_soc_codec_driver soc_codec_dev_si476x = { - .get_regmap = si476x_get_regmap, .component_driver = { + .probe = si476x_probe, .dapm_widgets = si476x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), .dapm_routes = si476x_dapm_routes, diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c deleted file mode 100644 index 887923e68849..000000000000 --- a/sound/soc/codecs/sn95031.c +++ /dev/null @@ -1,936 +0,0 @@ -/* - * sn95031.c - TI sn95031 Codec driver - * - * Copyright (C) 2010 Intel Corp - * Author: Vinod Koul <vinod.koul@intel.com> - * Author: Harsha Priya <priya.harsha@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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; version 2 of the License. - * - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> - -#include <asm/intel_scu_ipc.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/initval.h> -#include <sound/tlv.h> -#include <sound/jack.h> -#include "sn95031.h" - -#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100) -#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) - -/* adc helper functions */ - -/* enables mic bias voltage */ -static void sn95031_enable_mic_bias(struct snd_soc_codec *codec) -{ - snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0)); - snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2)); -} - -/* Enable/Disable the ADC depending on the argument */ -static void configure_adc(struct snd_soc_codec *sn95031_codec, int val) -{ - int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); - - if (val) { - /* Enable and start the ADC */ - value |= (SN95031_ADC_ENBL | SN95031_ADC_START); - value &= (~SN95031_ADC_NO_LOOP); - } else { - /* Just stop the ADC */ - value &= (~SN95031_ADC_START); - } - snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value); -} - -/* - * finds an empty channel for conversion - * If the ADC is not enabled then start using 0th channel - * itself. Otherwise find an empty channel by looking for a - * channel in which the stopbit is set to 1. returns the index - * of the first free channel if succeeds or an error code. - * - * Context: can sleep - * - */ -static int find_free_channel(struct snd_soc_codec *sn95031_codec) -{ - int i, value; - - /* check whether ADC is enabled */ - value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); - - if ((value & SN95031_ADC_ENBL) == 0) - return 0; - - /* ADC is already enabled; Looking for an empty channel */ - for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) { - value = snd_soc_read(sn95031_codec, - SN95031_ADC_CHNL_START_ADDR + i); - if (value & SN95031_STOPBIT_MASK) - break; - } - return (i == SN95031_ADC_CHANLS_MAX) ? (-EINVAL) : i; -} - -/* Initialize the ADC for reading micbias values. Can sleep. */ -static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec) -{ - int base_addr, chnl_addr; - int value; - int channel_index; - - /* Index of the first channel in which the stop bit is set */ - channel_index = find_free_channel(sn95031_codec); - if (channel_index < 0) { - pr_err("No free ADC channels"); - return channel_index; - } - - base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index; - - if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) { - /* Reset stop bit for channels other than 0 and 12 */ - value = snd_soc_read(sn95031_codec, base_addr); - /* Set the stop bit to zero */ - snd_soc_write(sn95031_codec, base_addr, value & 0xEF); - /* Index of the first free channel */ - base_addr++; - channel_index++; - } - - /* Since this is the last channel, set the stop bit - to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ - snd_soc_write(sn95031_codec, base_addr, - SN95031_AUDIO_DETECT_CODE | 0x10); - - chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index; - pr_debug("mid_initialize : %x", chnl_addr); - configure_adc(sn95031_codec, 1); - return chnl_addr; -} - - -/* reads the ADC registers and gets the mic bias value in mV. */ -static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec) -{ - u16 adc_adr = sn95031_initialize_adc(codec); - u16 adc_val1, adc_val2; - unsigned int mic_bias; - - sn95031_enable_mic_bias(codec); - - /* Enable the sound card for conversion before reading */ - snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05); - /* Re-toggle the RRDATARD bit */ - snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04); - - /* Read the higher bits of data */ - msleep(1000); - adc_val1 = snd_soc_read(codec, adc_adr); - adc_adr++; - adc_val2 = snd_soc_read(codec, adc_adr); - - /* Adding lower two bits to the higher bits */ - mic_bias = (adc_val1 << 2) + (adc_val2 & 3); - mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000; - pr_debug("mic bias = %dmV\n", mic_bias); - return mic_bias; -} -/*end - adc helper functions */ - -static int sn95031_read(void *ctx, unsigned int reg, unsigned int *val) -{ - u8 value = 0; - int ret; - - ret = intel_scu_ipc_ioread8(reg, &value); - if (ret == 0) - *val = value; - - return ret; -} - -static int sn95031_write(void *ctx, unsigned int reg, unsigned int value) -{ - return intel_scu_ipc_iowrite8(reg, value); -} - -static const struct regmap_config sn95031_regmap = { - .reg_read = sn95031_read, - .reg_write = sn95031_write, -}; - -static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) -{ - switch (level) { - case SND_SOC_BIAS_ON: - break; - - case SND_SOC_BIAS_PREPARE: - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { - pr_debug("vaud_bias powering up pll\n"); - /* power up the pll */ - snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5)); - /* enable pcm 2 */ - snd_soc_update_bits(codec, SN95031_PCM2C2, - BIT(0), BIT(0)); - } - break; - - case SND_SOC_BIAS_STANDBY: - switch (snd_soc_codec_get_bias_level(codec)) { - case SND_SOC_BIAS_OFF: - pr_debug("vaud_bias power up rail\n"); - /* power up the rail */ - snd_soc_write(codec, SN95031_VAUD, - BIT(2)|BIT(1)|BIT(0)); - msleep(1); - break; - case SND_SOC_BIAS_PREPARE: - /* turn off pcm */ - pr_debug("vaud_bias power dn pcm\n"); - snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0); - snd_soc_write(codec, SN95031_AUDPLLCTRL, 0); - break; - default: - break; - } - break; - - - case SND_SOC_BIAS_OFF: - pr_debug("vaud_bias _OFF doing rail shutdown\n"); - snd_soc_write(codec, SN95031_VAUD, BIT(3)); - break; - } - - return 0; -} - -static int sn95031_vhs_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); - - if (SND_SOC_DAPM_EVENT_ON(event)) { - pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n"); - /* power up the rail */ - snd_soc_write(codec, SN95031_VHSP, 0x3D); - snd_soc_write(codec, SN95031_VHSN, 0x3F); - msleep(1); - } else if (SND_SOC_DAPM_EVENT_OFF(event)) { - pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n"); - snd_soc_write(codec, SN95031_VHSP, 0xC4); - snd_soc_write(codec, SN95031_VHSN, 0x04); - } - return 0; -} - -static int sn95031_vihf_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); - - if (SND_SOC_DAPM_EVENT_ON(event)) { - pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n"); - /* power up the rail */ - snd_soc_write(codec, SN95031_VIHF, 0x27); - msleep(1); - } else if (SND_SOC_DAPM_EVENT_OFF(event)) { - pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n"); - snd_soc_write(codec, SN95031_VIHF, 0x24); - } - return 0; -} - -static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - unsigned int ldo = 0, clk_dir = 0, data_dir = 0; - - if (SND_SOC_DAPM_EVENT_ON(event)) { - ldo = BIT(5)|BIT(4); - clk_dir = BIT(0); - data_dir = BIT(7); - } - /* program DMIC LDO, clock and set clock */ - snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); - snd_soc_update_bits(codec, SN95031_DMICBUF0123, BIT(0), clk_dir); - snd_soc_update_bits(codec, SN95031_DMICBUF0123, BIT(7), data_dir); - return 0; -} - -static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - unsigned int ldo = 0, clk_dir = 0, data_dir = 0; - - if (SND_SOC_DAPM_EVENT_ON(event)) { - ldo = BIT(5)|BIT(4); - clk_dir = BIT(2); - data_dir = BIT(1); - } - /* program DMIC LDO, clock and set clock */ - snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); - snd_soc_update_bits(codec, SN95031_DMICBUF0123, BIT(2), clk_dir); - snd_soc_update_bits(codec, SN95031_DMICBUF45, BIT(1), data_dir); - return 0; -} - -static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - unsigned int ldo = 0; - - if (SND_SOC_DAPM_EVENT_ON(event)) - ldo = BIT(7)|BIT(6); - - /* program DMIC LDO */ - snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo); - return 0; -} - -/* mux controls */ -static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" }; - -static SOC_ENUM_SINGLE_DECL(sn95031_micl_enum, - SN95031_ADCCONFIG, 1, sn95031_mic_texts); - -static const struct snd_kcontrol_new sn95031_micl_mux_control = - SOC_DAPM_ENUM("Route", sn95031_micl_enum); - -static SOC_ENUM_SINGLE_DECL(sn95031_micr_enum, - SN95031_ADCCONFIG, 3, sn95031_mic_texts); - -static const struct snd_kcontrol_new sn95031_micr_mux_control = - SOC_DAPM_ENUM("Route", sn95031_micr_enum); - -static const char *sn95031_input_texts[] = { "DMIC1", "DMIC2", "DMIC3", - "DMIC4", "DMIC5", "DMIC6", - "ADC Left", "ADC Right" }; - -static SOC_ENUM_SINGLE_DECL(sn95031_input1_enum, - SN95031_AUDIOMUX12, 0, sn95031_input_texts); - -static const struct snd_kcontrol_new sn95031_input1_mux_control = - SOC_DAPM_ENUM("Route", sn95031_input1_enum); - -static SOC_ENUM_SINGLE_DECL(sn95031_input2_enum, - SN95031_AUDIOMUX12, 4, sn95031_input_texts); - -static const struct snd_kcontrol_new sn95031_input2_mux_control = - SOC_DAPM_ENUM("Route", sn95031_input2_enum); - -static SOC_ENUM_SINGLE_DECL(sn95031_input3_enum, - SN95031_AUDIOMUX34, 0, sn95031_input_texts); - -static const struct snd_kcontrol_new sn95031_input3_mux_control = - SOC_DAPM_ENUM("Route", sn95031_input3_enum); - -static SOC_ENUM_SINGLE_DECL(sn95031_input4_enum, - SN95031_AUDIOMUX34, 4, sn95031_input_texts); - -static const struct snd_kcontrol_new sn95031_input4_mux_control = - SOC_DAPM_ENUM("Route", sn95031_input4_enum); - -/* capture path controls */ - -static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"}; - -/* 0dB to 30dB in 10dB steps */ -static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0); - -static SOC_ENUM_SINGLE_DECL(sn95031_micmode1_enum, - SN95031_MICAMP1, 1, sn95031_micmode_text); -static SOC_ENUM_SINGLE_DECL(sn95031_micmode2_enum, - SN95031_MICAMP2, 1, sn95031_micmode_text); - -static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"}; - -static SOC_ENUM_SINGLE_DECL(sn95031_dmic12_cfg_enum, - SN95031_DMICMUX, 0, sn95031_dmic_cfg_text); -static SOC_ENUM_SINGLE_DECL(sn95031_dmic34_cfg_enum, - SN95031_DMICMUX, 1, sn95031_dmic_cfg_text); -static SOC_ENUM_SINGLE_DECL(sn95031_dmic56_cfg_enum, - SN95031_DMICMUX, 2, sn95031_dmic_cfg_text); - -static const struct snd_kcontrol_new sn95031_snd_controls[] = { - SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum), - SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum), - SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum), - SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum), - SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum), - SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1, - 2, 4, 0, mic_tlv), - SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2, - 2, 4, 0, mic_tlv), -}; - -/* DAPM widgets */ -static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = { - - /* all end points mic, hs etc */ - SND_SOC_DAPM_OUTPUT("HPOUTL"), - SND_SOC_DAPM_OUTPUT("HPOUTR"), - SND_SOC_DAPM_OUTPUT("EPOUT"), - SND_SOC_DAPM_OUTPUT("IHFOUTL"), - SND_SOC_DAPM_OUTPUT("IHFOUTR"), - SND_SOC_DAPM_OUTPUT("LINEOUTL"), - SND_SOC_DAPM_OUTPUT("LINEOUTR"), - SND_SOC_DAPM_OUTPUT("VIB1OUT"), - SND_SOC_DAPM_OUTPUT("VIB2OUT"), - - SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */ - SND_SOC_DAPM_INPUT("AMIC2"), - SND_SOC_DAPM_INPUT("DMIC1"), - SND_SOC_DAPM_INPUT("DMIC2"), - SND_SOC_DAPM_INPUT("DMIC3"), - SND_SOC_DAPM_INPUT("DMIC4"), - SND_SOC_DAPM_INPUT("DMIC5"), - SND_SOC_DAPM_INPUT("DMIC6"), - SND_SOC_DAPM_INPUT("LINEINL"), - SND_SOC_DAPM_INPUT("LINEINR"), - - SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0), - SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0), - SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0), - SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0), - SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0), - - SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0, - sn95031_dmic12_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0, - sn95031_dmic34_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0, - sn95031_dmic56_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0, - SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0, - sn95031_vhs_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0, - sn95031_vihf_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - - /* playback path driver enables */ - SND_SOC_DAPM_PGA("Headset Left Playback", - SN95031_DRIVEREN, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("Headset Right Playback", - SN95031_DRIVEREN, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("Speaker Left Playback", - SN95031_DRIVEREN, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("Speaker Right Playback", - SN95031_DRIVEREN, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("Vibra1 Playback", - SN95031_DRIVEREN, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("Vibra2 Playback", - SN95031_DRIVEREN, 5, 0, NULL, 0), - SND_SOC_DAPM_PGA("Earpiece Playback", - SN95031_DRIVEREN, 6, 0, NULL, 0), - SND_SOC_DAPM_PGA("Lineout Left Playback", - SN95031_LOCTL, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("Lineout Right Playback", - SN95031_LOCTL, 4, 0, NULL, 0), - - /* playback path filter enable */ - SND_SOC_DAPM_PGA("Headset Left Filter", - SN95031_HSEPRXCTRL, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("Headset Right Filter", - SN95031_HSEPRXCTRL, 5, 0, NULL, 0), - SND_SOC_DAPM_PGA("Speaker Left Filter", - SN95031_IHFRXCTRL, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("Speaker Right Filter", - SN95031_IHFRXCTRL, 1, 0, NULL, 0), - - /* DACs */ - SND_SOC_DAPM_DAC("HSDAC Left", "Headset", - SN95031_DACCONFIG, 0, 0), - SND_SOC_DAPM_DAC("HSDAC Right", "Headset", - SN95031_DACCONFIG, 1, 0), - SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker", - SN95031_DACCONFIG, 2, 0), - SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker", - SN95031_DACCONFIG, 3, 0), - SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1", - SN95031_VIB1C5, 1, 0), - SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2", - SN95031_VIB2C5, 1, 0), - - /* capture widgets */ - SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1, - 7, 0, NULL, 0), - SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2, - 7, 0, NULL, 0), - - SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0), - - /* ADC have null stream as they will be turned ON by TX path */ - SND_SOC_DAPM_ADC("ADC Left", NULL, - SN95031_ADCCONFIG, 0, 0), - SND_SOC_DAPM_ADC("ADC Right", NULL, - SN95031_ADCCONFIG, 2, 0), - - SND_SOC_DAPM_MUX("Mic_InputL Capture Route", - SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control), - SND_SOC_DAPM_MUX("Mic_InputR Capture Route", - SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control), - - SND_SOC_DAPM_MUX("Txpath1 Capture Route", - SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control), - SND_SOC_DAPM_MUX("Txpath2 Capture Route", - SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control), - SND_SOC_DAPM_MUX("Txpath3 Capture Route", - SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control), - SND_SOC_DAPM_MUX("Txpath4 Capture Route", - SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control), - -}; - -static const struct snd_soc_dapm_route sn95031_audio_map[] = { - /* headset and earpiece map */ - { "HPOUTL", NULL, "Headset Rail"}, - { "HPOUTR", NULL, "Headset Rail"}, - { "HPOUTL", NULL, "Headset Left Playback" }, - { "HPOUTR", NULL, "Headset Right Playback" }, - { "EPOUT", NULL, "Earpiece Playback" }, - { "Headset Left Playback", NULL, "Headset Left Filter"}, - { "Headset Right Playback", NULL, "Headset Right Filter"}, - { "Earpiece Playback", NULL, "Headset Left Filter"}, - { "Headset Left Filter", NULL, "HSDAC Left"}, - { "Headset Right Filter", NULL, "HSDAC Right"}, - - /* speaker map */ - { "IHFOUTL", NULL, "Speaker Rail"}, - { "IHFOUTR", NULL, "Speaker Rail"}, - { "IHFOUTL", NULL, "Speaker Left Playback"}, - { "IHFOUTR", NULL, "Speaker Right Playback"}, - { "Speaker Left Playback", NULL, "Speaker Left Filter"}, - { "Speaker Right Playback", NULL, "Speaker Right Filter"}, - { "Speaker Left Filter", NULL, "IHFDAC Left"}, - { "Speaker Right Filter", NULL, "IHFDAC Right"}, - - /* vibra map */ - { "VIB1OUT", NULL, "Vibra1 Playback"}, - { "Vibra1 Playback", NULL, "Vibra1 DAC"}, - - { "VIB2OUT", NULL, "Vibra2 Playback"}, - { "Vibra2 Playback", NULL, "Vibra2 DAC"}, - - /* lineout */ - { "LINEOUTL", NULL, "Lineout Left Playback"}, - { "LINEOUTR", NULL, "Lineout Right Playback"}, - { "Lineout Left Playback", NULL, "Headset Left Filter"}, - { "Lineout Left Playback", NULL, "Speaker Left Filter"}, - { "Lineout Left Playback", NULL, "Vibra1 DAC"}, - { "Lineout Right Playback", NULL, "Headset Right Filter"}, - { "Lineout Right Playback", NULL, "Speaker Right Filter"}, - { "Lineout Right Playback", NULL, "Vibra2 DAC"}, - - /* Headset (AMIC1) mic */ - { "AMIC1Bias", NULL, "AMIC1"}, - { "MIC1 Enable", NULL, "AMIC1Bias"}, - { "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"}, - - /* AMIC2 */ - { "AMIC2Bias", NULL, "AMIC2"}, - { "MIC2 Enable", NULL, "AMIC2Bias"}, - { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"}, - - - /* Linein */ - { "LineIn Enable Left", NULL, "LINEINL"}, - { "LineIn Enable Right", NULL, "LINEINR"}, - { "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"}, - { "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"}, - - /* ADC connection */ - { "ADC Left", NULL, "Mic_InputL Capture Route"}, - { "ADC Right", NULL, "Mic_InputR Capture Route"}, - - /*DMIC connections */ - { "DMIC1", NULL, "DMIC12supply"}, - { "DMIC2", NULL, "DMIC12supply"}, - { "DMIC3", NULL, "DMIC34supply"}, - { "DMIC4", NULL, "DMIC34supply"}, - { "DMIC5", NULL, "DMIC56supply"}, - { "DMIC6", NULL, "DMIC56supply"}, - - { "DMIC12Bias", NULL, "DMIC1"}, - { "DMIC12Bias", NULL, "DMIC2"}, - { "DMIC34Bias", NULL, "DMIC3"}, - { "DMIC34Bias", NULL, "DMIC4"}, - { "DMIC56Bias", NULL, "DMIC5"}, - { "DMIC56Bias", NULL, "DMIC6"}, - - /*TX path inputs*/ - { "Txpath1 Capture Route", "ADC Left", "ADC Left"}, - { "Txpath2 Capture Route", "ADC Left", "ADC Left"}, - { "Txpath3 Capture Route", "ADC Left", "ADC Left"}, - { "Txpath4 Capture Route", "ADC Left", "ADC Left"}, - { "Txpath1 Capture Route", "ADC Right", "ADC Right"}, - { "Txpath2 Capture Route", "ADC Right", "ADC Right"}, - { "Txpath3 Capture Route", "ADC Right", "ADC Right"}, - { "Txpath4 Capture Route", "ADC Right", "ADC Right"}, - { "Txpath1 Capture Route", "DMIC1", "DMIC1"}, - { "Txpath2 Capture Route", "DMIC1", "DMIC1"}, - { "Txpath3 Capture Route", "DMIC1", "DMIC1"}, - { "Txpath4 Capture Route", "DMIC1", "DMIC1"}, - { "Txpath1 Capture Route", "DMIC2", "DMIC2"}, - { "Txpath2 Capture Route", "DMIC2", "DMIC2"}, - { "Txpath3 Capture Route", "DMIC2", "DMIC2"}, - { "Txpath4 Capture Route", "DMIC2", "DMIC2"}, - { "Txpath1 Capture Route", "DMIC3", "DMIC3"}, - { "Txpath2 Capture Route", "DMIC3", "DMIC3"}, - { "Txpath3 Capture Route", "DMIC3", "DMIC3"}, - { "Txpath4 Capture Route", "DMIC3", "DMIC3"}, - { "Txpath1 Capture Route", "DMIC4", "DMIC4"}, - { "Txpath2 Capture Route", "DMIC4", "DMIC4"}, - { "Txpath3 Capture Route", "DMIC4", "DMIC4"}, - { "Txpath4 Capture Route", "DMIC4", "DMIC4"}, - { "Txpath1 Capture Route", "DMIC5", "DMIC5"}, - { "Txpath2 Capture Route", "DMIC5", "DMIC5"}, - { "Txpath3 Capture Route", "DMIC5", "DMIC5"}, - { "Txpath4 Capture Route", "DMIC5", "DMIC5"}, - { "Txpath1 Capture Route", "DMIC6", "DMIC6"}, - { "Txpath2 Capture Route", "DMIC6", "DMIC6"}, - { "Txpath3 Capture Route", "DMIC6", "DMIC6"}, - { "Txpath4 Capture Route", "DMIC6", "DMIC6"}, - - /* tx path */ - { "TX1 Enable", NULL, "Txpath1 Capture Route"}, - { "TX2 Enable", NULL, "Txpath2 Capture Route"}, - { "TX3 Enable", NULL, "Txpath3 Capture Route"}, - { "TX4 Enable", NULL, "Txpath4 Capture Route"}, - { "PCM_Out", NULL, "TX1 Enable"}, - { "PCM_Out", NULL, "TX2 Enable"}, - { "PCM_Out", NULL, "TX3 Enable"}, - { "PCM_Out", NULL, "TX4 Enable"}, - -}; - -/* speaker and headset mutes, for audio pops and clicks */ -static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute) -{ - snd_soc_update_bits(dai->codec, - SN95031_HSLVOLCTRL, BIT(7), (!mute << 7)); - snd_soc_update_bits(dai->codec, - SN95031_HSRVOLCTRL, BIT(7), (!mute << 7)); - return 0; -} - -static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute) -{ - snd_soc_update_bits(dai->codec, - SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7)); - snd_soc_update_bits(dai->codec, - SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7)); - return 0; -} - -static int sn95031_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) -{ - unsigned int format, rate; - - switch (params_width(params)) { - case 16: - format = BIT(4)|BIT(5); - break; - - case 24: - format = 0; - break; - default: - return -EINVAL; - } - snd_soc_update_bits(dai->codec, SN95031_PCM2C2, - BIT(4)|BIT(5), format); - - switch (params_rate(params)) { - case 48000: - pr_debug("RATE_48000\n"); - rate = 0; - break; - - case 44100: - pr_debug("RATE_44100\n"); - rate = BIT(7); - break; - - default: - pr_err("ERR rate %d\n", params_rate(params)); - return -EINVAL; - } - snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate); - - return 0; -} - -/* Codec DAI section */ -static const struct snd_soc_dai_ops sn95031_headset_dai_ops = { - .digital_mute = sn95031_pcm_hs_mute, - .hw_params = sn95031_pcm_hw_params, -}; - -static const struct snd_soc_dai_ops sn95031_speaker_dai_ops = { - .digital_mute = sn95031_pcm_spkr_mute, - .hw_params = sn95031_pcm_hw_params, -}; - -static const struct snd_soc_dai_ops sn95031_vib1_dai_ops = { - .hw_params = sn95031_pcm_hw_params, -}; - -static const struct snd_soc_dai_ops sn95031_vib2_dai_ops = { - .hw_params = sn95031_pcm_hw_params, -}; - -static struct snd_soc_dai_driver sn95031_dais[] = { -{ - .name = "SN95031 Headset", - .playback = { - .stream_name = "Headset", - .channels_min = 2, - .channels_max = 2, - .rates = SN95031_RATES, - .formats = SN95031_FORMATS, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 5, - .rates = SN95031_RATES, - .formats = SN95031_FORMATS, - }, - .ops = &sn95031_headset_dai_ops, -}, -{ .name = "SN95031 Speaker", - .playback = { - .stream_name = "Speaker", - .channels_min = 2, - .channels_max = 2, - .rates = SN95031_RATES, - .formats = SN95031_FORMATS, - }, - .ops = &sn95031_speaker_dai_ops, -}, -{ .name = "SN95031 Vibra1", - .playback = { - .stream_name = "Vibra1", - .channels_min = 1, - .channels_max = 1, - .rates = SN95031_RATES, - .formats = SN95031_FORMATS, - }, - .ops = &sn95031_vib1_dai_ops, -}, -{ .name = "SN95031 Vibra2", - .playback = { - .stream_name = "Vibra2", - .channels_min = 1, - .channels_max = 1, - .rates = SN95031_RATES, - .formats = SN95031_FORMATS, - }, - .ops = &sn95031_vib2_dai_ops, -}, -}; - -static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec) -{ - snd_soc_write(codec, SN95031_BTNCTRL2, 0x00); -} - -static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec) -{ - snd_soc_write(codec, SN95031_BTNCTRL1, 0x77); - snd_soc_write(codec, SN95031_BTNCTRL2, 0x01); -} - -static int sn95031_get_headset_state(struct snd_soc_codec *codec, - struct snd_soc_jack *mfld_jack) -{ - int micbias = sn95031_get_mic_bias(codec); - - int jack_type = snd_soc_jack_get_type(mfld_jack, micbias); - - pr_debug("jack type detected = %d\n", jack_type); - if (jack_type == SND_JACK_HEADSET) - sn95031_enable_jack_btn(codec); - return jack_type; -} - -void sn95031_jack_detection(struct snd_soc_codec *codec, - struct mfld_jack_data *jack_data) -{ - unsigned int status; - unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; - - pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id); - if (jack_data->intr_id & 0x1) { - pr_debug("short_push detected\n"); - status = SND_JACK_HEADSET | SND_JACK_BTN_0; - } else if (jack_data->intr_id & 0x2) { - pr_debug("long_push detected\n"); - status = SND_JACK_HEADSET | SND_JACK_BTN_1; - } else if (jack_data->intr_id & 0x4) { - pr_debug("headset or headphones inserted\n"); - status = sn95031_get_headset_state(codec, jack_data->mfld_jack); - } else if (jack_data->intr_id & 0x8) { - pr_debug("headset or headphones removed\n"); - status = 0; - sn95031_disable_jack_btn(codec); - } else { - pr_err("unidentified interrupt\n"); - return; - } - - snd_soc_jack_report(jack_data->mfld_jack, status, mask); - /*button pressed and released so we send explicit button release */ - if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1)) - snd_soc_jack_report(jack_data->mfld_jack, - SND_JACK_HEADSET, mask); -} -EXPORT_SYMBOL_GPL(sn95031_jack_detection); - -/* codec registration */ -static int sn95031_codec_probe(struct snd_soc_codec *codec) -{ - pr_debug("codec_probe called\n"); - - /* PCM interface config - * This sets the pcm rx slot conguration to max 6 slots - * for max 4 dais (2 stereo and 2 mono) - */ - snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10); - snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32); - snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54); - snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10); - snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32); - /* pcm port setting - * This sets the pcm port to slave and clock at 19.2Mhz which - * can support 6slots, sampling rate set per stream in hw-params - */ - snd_soc_write(codec, SN95031_PCM1C1, 0x00); - snd_soc_write(codec, SN95031_PCM2C1, 0x01); - snd_soc_write(codec, SN95031_PCM2C2, 0x0A); - snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4)); - /* vendor vibra workround, the vibras are muted by - * custom register so unmute them - */ - snd_soc_write(codec, SN95031_SSR5, 0x80); - snd_soc_write(codec, SN95031_SSR6, 0x80); - snd_soc_write(codec, SN95031_VIB1C5, 0x00); - snd_soc_write(codec, SN95031_VIB2C5, 0x00); - /* configure vibras for pcm port */ - snd_soc_write(codec, SN95031_VIB1C3, 0x00); - snd_soc_write(codec, SN95031_VIB2C3, 0x00); - - /* soft mute ramp time */ - snd_soc_write(codec, SN95031_SOFTMUTE, 0x3); - /* fix the initial volume at 1dB, - * default in +9dB, - * 1dB give optimal swing on DAC, amps - */ - snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08); - snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08); - snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08); - snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08); - /* dac mode and lineout workaround */ - snd_soc_write(codec, SN95031_SSR2, 0x10); - snd_soc_write(codec, SN95031_SSR3, 0x40); - - return 0; -} - -static const struct snd_soc_codec_driver sn95031_codec = { - .probe = sn95031_codec_probe, - .set_bias_level = sn95031_set_vaud_bias, - .idle_bias_off = true, - - .component_driver = { - .controls = sn95031_snd_controls, - .num_controls = ARRAY_SIZE(sn95031_snd_controls), - .dapm_widgets = sn95031_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets), - .dapm_routes = sn95031_audio_map, - .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map), - }, -}; - -static int sn95031_device_probe(struct platform_device *pdev) -{ - struct regmap *regmap; - - pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev)); - - regmap = devm_regmap_init(&pdev->dev, NULL, NULL, &sn95031_regmap); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - return snd_soc_register_codec(&pdev->dev, &sn95031_codec, - sn95031_dais, ARRAY_SIZE(sn95031_dais)); -} - -static int sn95031_device_remove(struct platform_device *pdev) -{ - pr_debug("codec device remove called\n"); - snd_soc_unregister_codec(&pdev->dev); - return 0; -} - -static struct platform_driver sn95031_codec_driver = { - .driver = { - .name = "sn95031", - }, - .probe = sn95031_device_probe, - .remove = sn95031_device_remove, -}; - -module_platform_driver(sn95031_codec_driver); - -MODULE_DESCRIPTION("ASoC TI SN95031 codec driver"); -MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); -MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sn95031"); diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h deleted file mode 100644 index 7651fe4e6a45..000000000000 --- a/sound/soc/codecs/sn95031.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * sn95031.h - TI sn95031 Codec driver - * - * Copyright (C) 2010 Intel Corp - * Author: Vinod Koul <vinod.koul@intel.com> - * Author: Harsha Priya <priya.harsha@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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; version 2 of the License. - * - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * - */ -#ifndef _SN95031_H -#define _SN95031_H - -/*register map*/ -#define SN95031_VAUD 0xDB -#define SN95031_VHSP 0xDC -#define SN95031_VHSN 0xDD -#define SN95031_VIHF 0xC9 - -#define SN95031_AUDPLLCTRL 0x240 -#define SN95031_DMICBUF0123 0x241 -#define SN95031_DMICBUF45 0x242 -#define SN95031_DMICGPO 0x244 -#define SN95031_DMICMUX 0x245 -#define SN95031_DMICLK 0x246 -#define SN95031_MICBIAS 0x247 -#define SN95031_ADCCONFIG 0x248 -#define SN95031_MICAMP1 0x249 -#define SN95031_MICAMP2 0x24A -#define SN95031_NOISEMUX 0x24B -#define SN95031_AUDIOMUX12 0x24C -#define SN95031_AUDIOMUX34 0x24D -#define SN95031_AUDIOSINC 0x24E -#define SN95031_AUDIOTXEN 0x24F -#define SN95031_HSEPRXCTRL 0x250 -#define SN95031_IHFRXCTRL 0x251 -#define SN95031_HSMIXER 0x256 -#define SN95031_DACCONFIG 0x257 -#define SN95031_SOFTMUTE 0x258 -#define SN95031_HSLVOLCTRL 0x259 -#define SN95031_HSRVOLCTRL 0x25A -#define SN95031_IHFLVOLCTRL 0x25B -#define SN95031_IHFRVOLCTRL 0x25C -#define SN95031_DRIVEREN 0x25D -#define SN95031_LOCTL 0x25E -#define SN95031_VIB1C1 0x25F -#define SN95031_VIB1C2 0x260 -#define SN95031_VIB1C3 0x261 -#define SN95031_VIB1SPIPCM1 0x262 -#define SN95031_VIB1SPIPCM2 0x263 -#define SN95031_VIB1C5 0x264 -#define SN95031_VIB2C1 0x265 -#define SN95031_VIB2C2 0x266 -#define SN95031_VIB2C3 0x267 -#define SN95031_VIB2SPIPCM1 0x268 -#define SN95031_VIB2SPIPCM2 0x269 -#define SN95031_VIB2C5 0x26A -#define SN95031_BTNCTRL1 0x26B -#define SN95031_BTNCTRL2 0x26C -#define SN95031_PCM1TXSLOT01 0x26D -#define SN95031_PCM1TXSLOT23 0x26E -#define SN95031_PCM1TXSLOT45 0x26F -#define SN95031_PCM1RXSLOT0_3 0x270 -#define SN95031_PCM1RXSLOT45 0x271 -#define SN95031_PCM2TXSLOT01 0x272 -#define SN95031_PCM2TXSLOT23 0x273 -#define SN95031_PCM2TXSLOT45 0x274 -#define SN95031_PCM2RXSLOT01 0x275 -#define SN95031_PCM2RXSLOT23 0x276 -#define SN95031_PCM2RXSLOT45 0x277 -#define SN95031_PCM1C1 0x278 -#define SN95031_PCM1C2 0x279 -#define SN95031_PCM1C3 0x27A -#define SN95031_PCM2C1 0x27B -#define SN95031_PCM2C2 0x27C -/*end codec register defn*/ - -/*vendor defn these are not part of avp*/ -#define SN95031_SSR2 0x381 -#define SN95031_SSR3 0x382 -#define SN95031_SSR5 0x384 -#define SN95031_SSR6 0x385 - -/* ADC registers */ - -#define SN95031_ADC1CNTL1 0x1C0 -#define SN95031_ADC_ENBL 0x10 -#define SN95031_ADC_START 0x08 -#define SN95031_ADC1CNTL3 0x1C2 -#define SN95031_ADCTHERM_ENBL 0x04 -#define SN95031_ADCRRDATA_ENBL 0x05 -#define SN95031_STOPBIT_MASK 16 -#define SN95031_ADCTHERM_MASK 4 -#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */ -#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1) -#define SN95031_ADC_NO_LOOP 0x07 -#define SN95031_AUDIO_GPIO_CTRL 0x070 - -/* ADC channel code values */ -#define SN95031_AUDIO_DETECT_CODE 0x06 - -/* ADC base addresses */ -#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ -#define SN95031_ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ -/* multipier to convert to mV */ -#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346 - - -struct mfld_jack_data { - int intr_id; - int micbias_vol; - struct snd_soc_jack *mfld_jack; -}; - -extern void sn95031_jack_detection(struct snd_soc_codec *codec, - struct mfld_jack_data *jack_data); - -#endif diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index 7acd05140a81..c8fd6367f6c0 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -34,10 +34,11 @@ static const struct snd_soc_dapm_route dir_routes[] = { #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) -static const struct snd_soc_codec_driver soc_codec_spdif_dir = { +static struct snd_soc_codec_driver soc_codec_spdif_dir = { .component_driver = { .dapm_widgets = dir_widgets, .num_dapm_widgets = ARRAY_SIZE(dir_widgets), diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index 063a64ff82d3..037aa1d45559 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -27,7 +27,8 @@ #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dapm_widget dit_widgets[] = { SND_SOC_DAPM_OUTPUT("spdif-out"), @@ -37,7 +38,7 @@ static const struct snd_soc_dapm_route dit_routes[] = { { "spdif-out", NULL, "Playback" }, }; -static const struct snd_soc_codec_driver soc_codec_spdif_dit = { +static struct snd_soc_codec_driver soc_codec_spdif_dit = { .component_driver = { .dapm_widgets = dit_widgets, .num_dapm_widgets = ARRAY_SIZE(dit_widgets), diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index a736a2a6976c..f3006f301fe8 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -36,6 +36,11 @@ /* Define how often to check (and clear) the fault status register (in ms) */ #define TAS5720_FAULT_CHECK_INTERVAL 200 +enum tas572x_type { + TAS5720, + TAS5722, +}; + static const char * const tas5720_supply_names[] = { "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ "pvdd", /* Class-D amp and analog power supply (connected). */ @@ -47,6 +52,7 @@ struct tas5720_data { struct snd_soc_codec *codec; struct regmap *regmap; struct i2c_client *tas5720_client; + enum tas572x_type devtype; struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES]; struct delayed_work fault_check_work; unsigned int last_fault; @@ -264,7 +270,7 @@ out: static int tas5720_codec_probe(struct snd_soc_codec *codec) { struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); - unsigned int device_id; + unsigned int device_id, expected_device_id; int ret; tas5720->codec = codec; @@ -276,6 +282,11 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec) return ret; } + /* + * Take a liberal approach to checking the device ID to allow the + * driver to be used even if the device ID does not match, however + * issue a warning if there is a mismatch. + */ ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id); if (ret < 0) { dev_err(codec->dev, "failed to read device ID register: %d\n", @@ -283,13 +294,22 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec) goto probe_fail; } - if (device_id != TAS5720_DEVICE_ID) { - dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n", - TAS5720_DEVICE_ID, device_id); - ret = -ENODEV; - goto probe_fail; + switch (tas5720->devtype) { + case TAS5720: + expected_device_id = TAS5720_DEVICE_ID; + break; + case TAS5722: + expected_device_id = TAS5722_DEVICE_ID; + break; + default: + dev_err(codec->dev, "unexpected private driver data\n"); + return -EINVAL; } + if (device_id != expected_device_id) + dev_warn(codec->dev, "wrong device ID. expected: %u read: %u\n", + expected_device_id, device_id); + /* Set device to mute */ ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, TAS5720_MUTE, TAS5720_MUTE); @@ -446,6 +466,15 @@ static const struct regmap_config tas5720_regmap_config = { .volatile_reg = tas5720_is_volatile_reg, }; +static const struct regmap_config tas5722_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS5722_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5720_is_volatile_reg, +}; + /* * DAC analog gain. There are four discrete values to select from, ranging * from 19.2 dB to 26.3dB. @@ -544,6 +573,7 @@ static int tas5720_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct tas5720_data *data; + const struct regmap_config *regmap_config; int ret; int i; @@ -552,7 +582,20 @@ static int tas5720_probe(struct i2c_client *client, return -ENOMEM; data->tas5720_client = client; - data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config); + data->devtype = id->driver_data; + + switch (id->driver_data) { + case TAS5720: + regmap_config = &tas5720_regmap_config; + break; + case TAS5722: + regmap_config = &tas5722_regmap_config; + break; + default: + dev_err(dev, "unexpected private driver data\n"); + return -EINVAL; + } + data->regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(data->regmap)) { ret = PTR_ERR(data->regmap); dev_err(dev, "failed to allocate register map: %d\n", ret); @@ -592,7 +635,8 @@ static int tas5720_remove(struct i2c_client *client) } static const struct i2c_device_id tas5720_id[] = { - { "tas5720", 0 }, + { "tas5720", TAS5720 }, + { "tas5722", TAS5722 }, { } }; MODULE_DEVICE_TABLE(i2c, tas5720_id); @@ -600,6 +644,7 @@ MODULE_DEVICE_TABLE(i2c, tas5720_id); #if IS_ENABLED(CONFIG_OF) static const struct of_device_id tas5720_of_match[] = { { .compatible = "ti,tas5720", }, + { .compatible = "ti,tas5722", }, { }, }; MODULE_DEVICE_TABLE(of, tas5720_of_match); diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h index 3d077c779b12..1dda3095961d 100644 --- a/sound/soc/codecs/tas5720.h +++ b/sound/soc/codecs/tas5720.h @@ -30,8 +30,14 @@ #define TAS5720_DIGITAL_CLIP1_REG 0x11 #define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG +/* Additional TAS5722-specific Registers */ +#define TAS5722_DIGITAL_CTRL2_REG 0x13 +#define TAS5722_ANALOG_CTRL2_REG 0x14 +#define TAS5722_MAX_REG TAS5722_ANALOG_CTRL2_REG + /* TAS5720_DEVICE_ID_REG */ #define TAS5720_DEVICE_ID 0x01 +#define TAS5722_DEVICE_ID 0x12 /* TAS5720_POWER_CTRL_REG */ #define TAS5720_DIG_CLIP_MASK GENMASK(7, 2) @@ -51,6 +57,7 @@ #define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0) /* TAS5720_DIGITAL_CTRL2_REG */ +#define TAS5722_VOL_RAMP_RATE BIT(6) #define TAS5720_MUTE BIT(4) #define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0) @@ -87,4 +94,28 @@ #define TAS5720_CLIP1_MASK GENMASK(7, 2) #define TAS5720_CLIP1_SHIFT (0x2) +/* TAS5722_DIGITAL_CTRL2_REG */ +#define TAS5722_HPF_3_7HZ (0x0 << 5) +#define TAS5722_HPF_7_4HZ (0x1 << 5) +#define TAS5722_HPF_14_9HZ (0x2 << 5) +#define TAS5722_HPF_29_7HZ (0x3 << 5) +#define TAS5722_HPF_59_4HZ (0x4 << 5) +#define TAS5722_HPF_118_4HZ (0x5 << 5) +#define TAS5722_HPF_235_0HZ (0x6 << 5) +#define TAS5722_HPF_463_2HZ (0x7 << 5) +#define TAS5722_HPF_MASK GENMASK(7, 5) +#define TAS5722_AUTO_SLEEP_OFF (0x0 << 3) +#define TAS5722_AUTO_SLEEP_1024LR (0x1 << 3) +#define TAS5722_AUTO_SLEEP_65536LR (0x2 << 3) +#define TAS5722_AUTO_SLEEP_262144LR (0x3 << 3) +#define TAS5722_AUTO_SLEEP_MASK GENMASK(4, 3) +#define TAS5722_TDM_SLOT_16B BIT(2) +#define TAS5722_MCLK_PIN_CFG BIT(1) +#define TAS5722_VOL_CONTROL_LSB BIT(0) + +/* TAS5722_ANALOG_CTRL2_REG */ +#define TAS5722_FAULTZ_PU BIT(3) +#define TAS5722_VREG_LVL BIT(2) +#define TAS5722_PWR_TUNE BIT(0) + #endif /* __TAS5720_H__ */ diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c new file mode 100644 index 000000000000..49b87f6e85bf --- /dev/null +++ b/sound/soc/codecs/tas6424.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier + * + * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Andreas Dannenberg <dannenberg@ti.com> + * Andrew F. Davis <afd@ti.com> + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "tas6424.h" + +/* Define how often to check (and clear) the fault status register (in ms) */ +#define TAS6424_FAULT_CHECK_INTERVAL 200 + +static const char * const tas6424_supply_names[] = { + "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ + "vbat", /* Supply used for higher voltage analog circuits. */ + "pvdd", /* Class-D amp output FETs supply. */ +}; +#define TAS6424_NUM_SUPPLIES ARRAY_SIZE(tas6424_supply_names) + +struct tas6424_data { + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES]; + struct delayed_work fault_check_work; + unsigned int last_fault1; + unsigned int last_fault2; + unsigned int last_warn; +}; + +/* + * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that + * setting the gain below -100 dB (register value <0x7) is effectively a MUTE + * as per device datasheet. + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); + +static const struct snd_kcontrol_new tas6424_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Driver CH1 Playback Volume", + TAS6424_CH1_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_TLV("Speaker Driver CH2 Playback Volume", + TAS6424_CH2_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_TLV("Speaker Driver CH3 Playback Volume", + TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume", + TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv), +}; + +static int tas6424_dac_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 tas6424_data *tas6424 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s() event=0x%0x\n", __func__, event); + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Observe codec shutdown-to-active time */ + msleep(12); + + /* Turn on TAS6424 periodic fault checking/handling */ + tas6424->last_fault1 = 0; + tas6424->last_fault2 = 0; + tas6424->last_warn = 0; + schedule_delayed_work(&tas6424->fault_check_work, + msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); + } else if (event & SND_SOC_DAPM_PRE_PMD) { + /* Disable TAS6424 periodic fault checking/handling */ + cancel_delayed_work_sync(&tas6424->fault_check_work); + } + + return 0; +} + +static const struct snd_soc_dapm_widget tas6424_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas6424_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas6424_audio_map[] = { + { "DAC", NULL, "DAC IN" }, + { "OUT", NULL, "DAC" }, +}; + +static int tas6424_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; + unsigned int rate = params_rate(params); + unsigned int width = params_width(params); + u8 sap_ctrl = 0; + + dev_dbg(codec->dev, "%s() rate=%u width=%u\n", __func__, rate, width); + + switch (rate) { + case 44100: + sap_ctrl |= TAS6424_SAP_RATE_44100; + break; + case 48000: + sap_ctrl |= TAS6424_SAP_RATE_48000; + break; + case 96000: + sap_ctrl |= TAS6424_SAP_RATE_96000; + break; + default: + dev_err(codec->dev, "unsupported sample rate: %u\n", rate); + return -EINVAL; + } + + switch (width) { + case 16: + sap_ctrl |= TAS6424_SAP_TDM_SLOT_SZ_16; + break; + case 24: + break; + default: + dev_err(codec->dev, "unsupported sample width: %u\n", width); + return -EINVAL; + } + + snd_soc_update_bits(codec, TAS6424_SAP_CTRL, + TAS6424_SAP_RATE_MASK | + TAS6424_SAP_TDM_SLOT_SZ_16, + sap_ctrl); + + return 0; +} + +static int tas6424_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u8 serial_format = 0; + + dev_dbg(codec->dev, "%s() fmt=0x%0x\n", __func__, fmt); + + /* clock masters */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + dev_err(codec->dev, "Invalid DAI master/slave interface\n"); + return -EINVAL; + } + + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(codec->dev, "Invalid DAI clock signal polarity\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + serial_format |= TAS6424_SAP_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + serial_format |= TAS6424_SAP_DSP; + break; + case SND_SOC_DAIFMT_DSP_B: + /* + * We can use the fact that the TAS6424 does not care about the + * LRCLK duty cycle during TDM to receive DSP_B formatted data + * in LEFTJ mode (no delaying of the 1st data bit). + */ + serial_format |= TAS6424_SAP_LEFTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + serial_format |= TAS6424_SAP_LEFTJ; + break; + default: + dev_err(codec->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, TAS6424_SAP_CTRL, + TAS6424_SAP_FMT_MASK, serial_format); + + return 0; +} + +static int tas6424_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; + unsigned int first_slot, last_slot; + bool sap_tdm_slot_last; + + dev_dbg(codec->dev, "%s() tx_mask=%d rx_mask=%d\n", __func__, + tx_mask, rx_mask); + + if (!tx_mask || !rx_mask) + return 0; /* nothing needed to disable TDM mode */ + + /* + * Determine the first slot and last slot that is being requested so + * we'll be able to more easily enforce certain constraints as the + * TAS6424's TDM interface is not fully configurable. + */ + first_slot = __ffs(tx_mask); + last_slot = __fls(rx_mask); + + if (last_slot - first_slot != 4) { + dev_err(codec->dev, "tdm mask must cover 4 contiguous slots\n"); + return -EINVAL; + } + + switch (first_slot) { + case 0: + sap_tdm_slot_last = false; + break; + case 4: + sap_tdm_slot_last = true; + break; + default: + dev_err(codec->dev, "tdm mask must start at slot 0 or 4\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, TAS6424_SAP_CTRL, TAS6424_SAP_TDM_SLOT_LAST, + sap_tdm_slot_last ? TAS6424_SAP_TDM_SLOT_LAST : 0); + + return 0; +} + +static int tas6424_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val; + + dev_dbg(codec->dev, "%s() mute=%d\n", __func__, mute); + + if (mute) + val = TAS6424_ALL_STATE_MUTE; + else + val = TAS6424_ALL_STATE_PLAY; + + snd_soc_write(codec, TAS6424_CH_STATE_CTRL, val); + + return 0; +} + +static int tas6424_power_off(struct snd_soc_codec *codec) +{ + struct tas6424_data *tas6424 = snd_soc_codec_get_drvdata(codec); + int ret; + + snd_soc_write(codec, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_HIZ); + + regcache_cache_only(tas6424->regmap, true); + regcache_mark_dirty(tas6424->regmap); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret < 0) { + dev_err(codec->dev, "failed to disable supplies: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas6424_power_on(struct snd_soc_codec *codec) +{ + struct tas6424_data *tas6424 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret < 0) { + dev_err(codec->dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(tas6424->regmap, false); + + ret = regcache_sync(tas6424->regmap); + if (ret < 0) { + dev_err(codec->dev, "failed to sync regcache: %d\n", ret); + return ret; + } + + snd_soc_write(codec, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_MUTE); + + /* any time we come out of HIZ, the output channels automatically run DC + * load diagnostics, wait here until this completes + */ + msleep(230); + + return 0; +} + +static int tas6424_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + dev_dbg(codec->dev, "%s() level=%d\n", __func__, level); + + 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) + tas6424_power_on(codec); + break; + case SND_SOC_BIAS_OFF: + tas6424_power_off(codec); + break; + } + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_tas6424 = { + .set_bias_level = tas6424_set_bias_level, + .idle_bias_off = true, + + .component_driver = { + .controls = tas6424_snd_controls, + .num_controls = ARRAY_SIZE(tas6424_snd_controls), + .dapm_widgets = tas6424_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas6424_dapm_widgets), + .dapm_routes = tas6424_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas6424_audio_map), + }, +}; + +static struct snd_soc_dai_ops tas6424_speaker_dai_ops = { + .hw_params = tas6424_hw_params, + .set_fmt = tas6424_set_dai_fmt, + .set_tdm_slot = tas6424_set_dai_tdm_slot, + .digital_mute = tas6424_mute, +}; + +static struct snd_soc_dai_driver tas6424_dai[] = { + { + .name = "tas6424-amplifier", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TAS6424_RATES, + .formats = TAS6424_FORMATS, + }, + .ops = &tas6424_speaker_dai_ops, + }, +}; + +static void tas6424_fault_check_work(struct work_struct *work) +{ + struct tas6424_data *tas6424 = container_of(work, struct tas6424_data, + fault_check_work.work); + struct device *dev = tas6424->dev; + unsigned int reg; + int ret; + + ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, ®); + if (ret < 0) { + dev_err(dev, "failed to read FAULT1 register: %d\n", ret); + goto out; + } + + /* + * Ignore any clock faults as there is no clean way to check for them. + * We would need to start checking for those faults *after* the SAIF + * stream has been setup, and stop checking *before* the stream is + * stopped to avoid any false-positives. However there are no + * appropriate hooks to monitor these events. + */ + reg &= TAS6424_FAULT_PVDD_OV | + TAS6424_FAULT_VBAT_OV | + TAS6424_FAULT_PVDD_UV | + TAS6424_FAULT_VBAT_UV; + + if (reg) + goto check_global_fault2_reg; + + /* + * Only flag errors once for a given occurrence. This is needed as + * the TAS6424 will take time clearing the fault condition internally + * during which we don't want to bombard the system with the same + * error message over and over. + */ + if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV)) + dev_crit(dev, "experienced a PVDD overvoltage fault\n"); + + if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV)) + dev_crit(dev, "experienced a VBAT overvoltage fault\n"); + + if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV)) + dev_crit(dev, "experienced a PVDD undervoltage fault\n"); + + if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV)) + dev_crit(dev, "experienced a VBAT undervoltage fault\n"); + + /* Store current fault1 value so we can detect any changes next time */ + tas6424->last_fault1 = reg; + +check_global_fault2_reg: + ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, ®); + if (ret < 0) { + dev_err(dev, "failed to read FAULT2 register: %d\n", ret); + goto out; + } + + reg &= TAS6424_FAULT_OTSD | + TAS6424_FAULT_OTSD_CH1 | + TAS6424_FAULT_OTSD_CH2 | + TAS6424_FAULT_OTSD_CH3 | + TAS6424_FAULT_OTSD_CH4; + + if (!reg) + goto check_warn_reg; + + if ((reg & TAS6424_FAULT_OTSD) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD)) + dev_crit(dev, "experienced a global overtemp shutdown\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH1) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH1)) + dev_crit(dev, "experienced an overtemp shutdown on CH1\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH2) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH2)) + dev_crit(dev, "experienced an overtemp shutdown on CH2\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH3) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH3)) + dev_crit(dev, "experienced an overtemp shutdown on CH3\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH4) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH4)) + dev_crit(dev, "experienced an overtemp shutdown on CH4\n"); + + /* Store current fault2 value so we can detect any changes next time */ + tas6424->last_fault2 = reg; + +check_warn_reg: + ret = regmap_read(tas6424->regmap, TAS6424_WARN, ®); + if (ret < 0) { + dev_err(dev, "failed to read WARN register: %d\n", ret); + goto out; + } + + reg &= TAS6424_WARN_VDD_UV | + TAS6424_WARN_VDD_POR | + TAS6424_WARN_VDD_OTW | + TAS6424_WARN_VDD_OTW_CH1 | + TAS6424_WARN_VDD_OTW_CH2 | + TAS6424_WARN_VDD_OTW_CH3 | + TAS6424_WARN_VDD_OTW_CH4; + + if (!reg) + goto out; + + if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV)) + dev_warn(dev, "experienced a VDD under voltage condition\n"); + + if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR)) + dev_warn(dev, "experienced a VDD POR condition\n"); + + if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW)) + dev_warn(dev, "experienced a global overtemp warning\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1)) + dev_warn(dev, "experienced an overtemp warning on CH1\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2)) + dev_warn(dev, "experienced an overtemp warning on CH2\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3)) + dev_warn(dev, "experienced an overtemp warning on CH3\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4)) + dev_warn(dev, "experienced an overtemp warning on CH4\n"); + + /* Store current warn value so we can detect any changes next time */ + tas6424->last_warn = reg; + + /* Clear any faults by toggling the CLEAR_FAULT control bit */ + ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3, + TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT); + if (ret < 0) + dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret); + + ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3, + TAS6424_CLEAR_FAULT, 0); + if (ret < 0) + dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret); + +out: + /* Schedule the next fault check at the specified interval */ + schedule_delayed_work(&tas6424->fault_check_work, + msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); +} + +static const struct reg_default tas6424_reg_defaults[] = { + { TAS6424_MODE_CTRL, 0x00 }, + { TAS6424_MISC_CTRL1, 0x32 }, + { TAS6424_MISC_CTRL2, 0x62 }, + { TAS6424_SAP_CTRL, 0x04 }, + { TAS6424_CH_STATE_CTRL, 0x55 }, + { TAS6424_CH1_VOL_CTRL, 0xcf }, + { TAS6424_CH2_VOL_CTRL, 0xcf }, + { TAS6424_CH3_VOL_CTRL, 0xcf }, + { TAS6424_CH4_VOL_CTRL, 0xcf }, + { TAS6424_DC_DIAG_CTRL1, 0x00 }, + { TAS6424_DC_DIAG_CTRL2, 0x11 }, + { TAS6424_DC_DIAG_CTRL3, 0x11 }, + { TAS6424_PIN_CTRL, 0xff }, + { TAS6424_AC_DIAG_CTRL1, 0x00 }, + { TAS6424_MISC_CTRL3, 0x00 }, + { TAS6424_CLIP_CTRL, 0x01 }, + { TAS6424_CLIP_WINDOW, 0x14 }, + { TAS6424_CLIP_WARN, 0x00 }, + { TAS6424_CBC_STAT, 0x00 }, + { TAS6424_MISC_CTRL4, 0x40 }, +}; + +static bool tas6424_is_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS6424_MODE_CTRL: + case TAS6424_MISC_CTRL1: + case TAS6424_MISC_CTRL2: + case TAS6424_SAP_CTRL: + case TAS6424_CH_STATE_CTRL: + case TAS6424_CH1_VOL_CTRL: + case TAS6424_CH2_VOL_CTRL: + case TAS6424_CH3_VOL_CTRL: + case TAS6424_CH4_VOL_CTRL: + case TAS6424_DC_DIAG_CTRL1: + case TAS6424_DC_DIAG_CTRL2: + case TAS6424_DC_DIAG_CTRL3: + case TAS6424_PIN_CTRL: + case TAS6424_AC_DIAG_CTRL1: + case TAS6424_MISC_CTRL3: + case TAS6424_CLIP_CTRL: + case TAS6424_CLIP_WINDOW: + case TAS6424_CLIP_WARN: + case TAS6424_CBC_STAT: + case TAS6424_MISC_CTRL4: + return true; + default: + return false; + } +} + +static bool tas6424_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS6424_DC_LOAD_DIAG_REP12: + case TAS6424_DC_LOAD_DIAG_REP34: + case TAS6424_DC_LOAD_DIAG_REPLO: + case TAS6424_CHANNEL_STATE: + case TAS6424_CHANNEL_FAULT: + case TAS6424_GLOB_FAULT1: + case TAS6424_GLOB_FAULT2: + case TAS6424_WARN: + case TAS6424_AC_LOAD_DIAG_REP1: + case TAS6424_AC_LOAD_DIAG_REP2: + case TAS6424_AC_LOAD_DIAG_REP3: + case TAS6424_AC_LOAD_DIAG_REP4: + return true; + default: + return false; + } +} + +static const struct regmap_config tas6424_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .writeable_reg = tas6424_is_writable_reg, + .volatile_reg = tas6424_is_volatile_reg, + + .max_register = TAS6424_MAX, + .reg_defaults = tas6424_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas6424_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas6424_of_ids[] = { + { .compatible = "ti,tas6424", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas6424_of_ids); +#endif + +static int tas6424_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct tas6424_data *tas6424; + int ret; + int i; + + tas6424 = devm_kzalloc(dev, sizeof(*tas6424), GFP_KERNEL); + if (!tas6424) + return -ENOMEM; + dev_set_drvdata(dev, tas6424); + + tas6424->dev = dev; + + tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config); + if (IS_ERR(tas6424->regmap)) { + ret = PTR_ERR(tas6424->regmap); + dev_err(dev, "unable to allocate register map: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++) + tas6424->supplies[i].supply = tas6424_supply_names[i]; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret) { + dev_err(dev, "unable to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret) { + dev_err(dev, "unable to enable supplies: %d\n", ret); + return ret; + } + + /* Reset device to establish well-defined startup state */ + ret = regmap_update_bits(tas6424->regmap, TAS6424_MODE_CTRL, + TAS6424_RESET, TAS6424_RESET); + if (ret) { + dev_err(dev, "unable to reset device: %d\n", ret); + return ret; + } + + INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work); + + ret = snd_soc_register_codec(dev, &soc_codec_dev_tas6424, + tas6424_dai, ARRAY_SIZE(tas6424_dai)); + if (ret < 0) { + dev_err(dev, "unable to register codec: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas6424_i2c_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tas6424_data *tas6424 = dev_get_drvdata(dev); + int ret; + + snd_soc_unregister_codec(dev); + + cancel_delayed_work_sync(&tas6424->fault_check_work); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret < 0) { + dev_err(dev, "unable to disable supplies: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id tas6424_i2c_ids[] = { + { "tas6424", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids); + +static struct i2c_driver tas6424_i2c_driver = { + .driver = { + .name = "tas6424", + .of_match_table = of_match_ptr(tas6424_of_ids), + }, + .probe = tas6424_i2c_probe, + .remove = tas6424_i2c_remove, + .id_table = tas6424_i2c_ids, +}; +module_i2c_driver(tas6424_i2c_driver); + +MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("TAS6424 Audio amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas6424.h b/sound/soc/codecs/tas6424.h new file mode 100644 index 000000000000..430588328a06 --- /dev/null +++ b/sound/soc/codecs/tas6424.h @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier + * + * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Andreas Dannenberg <dannenberg@ti.com> + * Andrew F. Davis <afd@ti.com> + */ + +#ifndef __TAS6424_H__ +#define __TAS6424_H__ + +#define TAS6424_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000) + +#define TAS6424_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* Register Address Map */ +#define TAS6424_MODE_CTRL 0x00 +#define TAS6424_MISC_CTRL1 0x01 +#define TAS6424_MISC_CTRL2 0x02 +#define TAS6424_SAP_CTRL 0x03 +#define TAS6424_CH_STATE_CTRL 0x04 +#define TAS6424_CH1_VOL_CTRL 0x05 +#define TAS6424_CH2_VOL_CTRL 0x06 +#define TAS6424_CH3_VOL_CTRL 0x07 +#define TAS6424_CH4_VOL_CTRL 0x08 +#define TAS6424_DC_DIAG_CTRL1 0x09 +#define TAS6424_DC_DIAG_CTRL2 0x0a +#define TAS6424_DC_DIAG_CTRL3 0x0b +#define TAS6424_DC_LOAD_DIAG_REP12 0x0c +#define TAS6424_DC_LOAD_DIAG_REP34 0x0d +#define TAS6424_DC_LOAD_DIAG_REPLO 0x0e +#define TAS6424_CHANNEL_STATE 0x0f +#define TAS6424_CHANNEL_FAULT 0x10 +#define TAS6424_GLOB_FAULT1 0x11 +#define TAS6424_GLOB_FAULT2 0x12 +#define TAS6424_WARN 0x13 +#define TAS6424_PIN_CTRL 0x14 +#define TAS6424_AC_DIAG_CTRL1 0x15 +#define TAS6424_AC_DIAG_CTRL2 0x16 +#define TAS6424_AC_LOAD_DIAG_REP1 0x17 +#define TAS6424_AC_LOAD_DIAG_REP2 0x18 +#define TAS6424_AC_LOAD_DIAG_REP3 0x19 +#define TAS6424_AC_LOAD_DIAG_REP4 0x1a +#define TAS6424_MISC_CTRL3 0x21 +#define TAS6424_CLIP_CTRL 0x22 +#define TAS6424_CLIP_WINDOW 0x23 +#define TAS6424_CLIP_WARN 0x24 +#define TAS6424_CBC_STAT 0x25 +#define TAS6424_MISC_CTRL4 0x26 +#define TAS6424_MAX TAS6424_MISC_CTRL4 + +/* TAS6424_MODE_CTRL_REG */ +#define TAS6424_RESET BIT(7) + +/* TAS6424_SAP_CTRL_REG */ +#define TAS6424_SAP_RATE_MASK GENMASK(7, 6) +#define TAS6424_SAP_RATE_44100 (0x00 << 6) +#define TAS6424_SAP_RATE_48000 (0x01 << 6) +#define TAS6424_SAP_RATE_96000 (0x02 << 6) +#define TAS6424_SAP_TDM_SLOT_LAST BIT(5) +#define TAS6424_SAP_TDM_SLOT_SZ_16 BIT(4) +#define TAS6424_SAP_TDM_SLOT_SWAP BIT(3) +#define TAS6424_SAP_FMT_MASK GENMASK(2, 0) +#define TAS6424_SAP_RIGHTJ_24 (0x00 << 0) +#define TAS6424_SAP_RIGHTJ_20 (0x01 << 0) +#define TAS6424_SAP_RIGHTJ_18 (0x02 << 0) +#define TAS6424_SAP_RIGHTJ_16 (0x03 << 0) +#define TAS6424_SAP_I2S (0x04 << 0) +#define TAS6424_SAP_LEFTJ (0x05 << 0) +#define TAS6424_SAP_DSP (0x06 << 0) + +/* TAS6424_CH_STATE_CTRL_REG */ +#define TAS6424_CH1_STATE_MASK GENMASK(7, 6) +#define TAS6424_CH1_STATE_PLAY (0x00 << 6) +#define TAS6424_CH1_STATE_HIZ (0x01 << 6) +#define TAS6424_CH1_STATE_MUTE (0x02 << 6) +#define TAS6424_CH1_STATE_DIAG (0x03 << 6) +#define TAS6424_CH2_STATE_MASK GENMASK(5, 4) +#define TAS6424_CH2_STATE_PLAY (0x00 << 4) +#define TAS6424_CH2_STATE_HIZ (0x01 << 4) +#define TAS6424_CH2_STATE_MUTE (0x02 << 4) +#define TAS6424_CH2_STATE_DIAG (0x03 << 4) +#define TAS6424_CH3_STATE_MASK GENMASK(3, 2) +#define TAS6424_CH3_STATE_PLAY (0x00 << 2) +#define TAS6424_CH3_STATE_HIZ (0x01 << 2) +#define TAS6424_CH3_STATE_MUTE (0x02 << 2) +#define TAS6424_CH3_STATE_DIAG (0x03 << 2) +#define TAS6424_CH4_STATE_MASK GENMASK(1, 0) +#define TAS6424_CH4_STATE_PLAY (0x00 << 0) +#define TAS6424_CH4_STATE_HIZ (0x01 << 0) +#define TAS6424_CH4_STATE_MUTE (0x02 << 0) +#define TAS6424_CH4_STATE_DIAG (0x03 << 0) +#define TAS6424_ALL_STATE_PLAY (TAS6424_CH1_STATE_PLAY | \ + TAS6424_CH2_STATE_PLAY | \ + TAS6424_CH3_STATE_PLAY | \ + TAS6424_CH4_STATE_PLAY) +#define TAS6424_ALL_STATE_HIZ (TAS6424_CH1_STATE_HIZ | \ + TAS6424_CH2_STATE_HIZ | \ + TAS6424_CH3_STATE_HIZ | \ + TAS6424_CH4_STATE_HIZ) +#define TAS6424_ALL_STATE_MUTE (TAS6424_CH1_STATE_MUTE | \ + TAS6424_CH2_STATE_MUTE | \ + TAS6424_CH3_STATE_MUTE | \ + TAS6424_CH4_STATE_MUTE) +#define TAS6424_ALL_STATE_DIAG (TAS6424_CH1_STATE_DIAG | \ + TAS6424_CH2_STATE_DIAG | \ + TAS6424_CH3_STATE_DIAG | \ + TAS6424_CH4_STATE_DIAG) + +/* TAS6424_GLOB_FAULT1_REG */ +#define TAS6424_FAULT_CLOCK BIT(4) +#define TAS6424_FAULT_PVDD_OV BIT(3) +#define TAS6424_FAULT_VBAT_OV BIT(2) +#define TAS6424_FAULT_PVDD_UV BIT(1) +#define TAS6424_FAULT_VBAT_UV BIT(0) + +/* TAS6424_GLOB_FAULT2_REG */ +#define TAS6424_FAULT_OTSD BIT(4) +#define TAS6424_FAULT_OTSD_CH1 BIT(3) +#define TAS6424_FAULT_OTSD_CH2 BIT(2) +#define TAS6424_FAULT_OTSD_CH3 BIT(1) +#define TAS6424_FAULT_OTSD_CH4 BIT(0) + +/* TAS6424_WARN_REG */ +#define TAS6424_WARN_VDD_UV BIT(6) +#define TAS6424_WARN_VDD_POR BIT(5) +#define TAS6424_WARN_VDD_OTW BIT(4) +#define TAS6424_WARN_VDD_OTW_CH1 BIT(3) +#define TAS6424_WARN_VDD_OTW_CH2 BIT(2) +#define TAS6424_WARN_VDD_OTW_CH3 BIT(1) +#define TAS6424_WARN_VDD_OTW_CH4 BIT(0) + +/* TAS6424_MISC_CTRL3_REG */ +#define TAS6424_CLEAR_FAULT BIT(7) +#define TAS6424_PBTL_CH_SEL BIT(6) +#define TAS6424_MASK_CBC_WARN BIT(5) +#define TAS6424_MASK_VDD_UV BIT(4) +#define TAS6424_OTSD_AUTO_RECOVERY BIT(3) + +#endif /* __TAS6424_H__ */ diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index f8dd67ca0744..e7ca764b5729 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -316,6 +316,7 @@ static const struct of_device_id tfa9879_of_match[] = { { .compatible = "nxp,tfa9879", }, { } }; +MODULE_DEVICE_TABLE(of, tfa9879_of_match); static struct i2c_driver tfa9879_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index e2862372c26e..858cb8be445f 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1,22 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * ALSA SoC TLV320AIC31XX codec driver + * ALSA SoC TLV320AIC31xx CODEC Driver * - * Copyright (C) 2014 Texas Instruments, Inc. - * - * Author: Jyri Sarha <jsarha@ti.com> + * Copyright (C) 2014-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Jyri Sarha <jsarha@ti.com> * * Based on ground work by: Ajit Kulkarni <x0175765@ti.com> * - * This package 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 PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * The TLV320AIC31xx series of audio codec is a low-power, highly integrated - * high performance codec which provides a stereo DAC, a mono ADC, + * The TLV320AIC31xx series of audio codecs are low-power, highly integrated + * high performance codecs which provides a stereo DAC, a mono ADC, * and mono/stereo Class-D speaker driver. */ @@ -26,7 +18,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/acpi.h> #include <linux/of.h> @@ -144,8 +136,7 @@ static const struct regmap_config aic31xx_i2c_regmap = { .max_register = 12 * 128, }; -#define AIC31XX_NUM_SUPPLIES 6 -static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = { +static const char * const aic31xx_supply_names[] = { "HPVDD", "SPRVDD", "SPLVDD", @@ -154,6 +145,8 @@ static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = { "DVDD", }; +#define AIC31XX_NUM_SUPPLIES ARRAY_SIZE(aic31xx_supply_names) + struct aic31xx_disable_nb { struct notifier_block nb; struct aic31xx_priv *aic31xx; @@ -164,6 +157,9 @@ struct aic31xx_priv { u8 i2c_regs_status; struct device *dev; struct regmap *regmap; + enum aic31xx_type codec_type; + struct gpio_desc *gpio_reset; + int micbias_vg; struct aic31xx_pdata pdata; struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES]; struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES]; @@ -185,7 +181,7 @@ struct aic31xx_rate_divs { u8 madc; }; -/* ADC dividers can be disabled by cofiguring them to 0 */ +/* ADC dividers can be disabled by configuring them to 0 */ static const struct aic31xx_rate_divs aic31xx_divs[] = { /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */ /* 8k rate */ @@ -456,7 +452,7 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, /* change mic bias voltage to user defined */ snd_soc_update_bits(codec, AIC31XX_MICBIAS, AIC31XX_MICBIAS_MASK, - aic31xx->pdata.micbias_vg << + aic31xx->micbias_vg << AIC31XX_MICBIAS_SHIFT); dev_dbg(codec->dev, "%s: turned on\n", __func__); break; @@ -679,14 +675,14 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec) int ret = 0; struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); - if (!(aic31xx->pdata.codec_type & DAC31XX_BIT)) + if (!(aic31xx->codec_type & DAC31XX_BIT)) ret = snd_soc_add_codec_controls( codec, aic31xx_snd_controls, ARRAY_SIZE(aic31xx_snd_controls)); if (ret) return ret; - if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) + if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT) ret = snd_soc_add_codec_controls( codec, aic311x_snd_controls, ARRAY_SIZE(aic311x_snd_controls)); @@ -704,7 +700,7 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec) struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int ret = 0; - if (aic31xx->pdata.codec_type & DAC31XX_BIT) { + if (aic31xx->codec_type & DAC31XX_BIT) { ret = snd_soc_dapm_new_controls( dapm, dac31xx_dapm_widgets, ARRAY_SIZE(dac31xx_dapm_widgets)); @@ -728,7 +724,7 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec) return ret; } - if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) { + if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT) { ret = snd_soc_dapm_new_controls( dapm, aic311x_dapm_widgets, ARRAY_SIZE(aic311x_dapm_widgets)); @@ -760,11 +756,17 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec, { struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int bclk_score = snd_soc_params_to_frame_size(params); - int mclk_p = aic31xx->sysclk / aic31xx->p_div; + int mclk_p; int bclk_n = 0; int match = -1; int i; + if (!aic31xx->sysclk || !aic31xx->p_div) { + dev_err(codec->dev, "Master clock not supplied\n"); + return -EINVAL; + } + mclk_p = aic31xx->sysclk / aic31xx->p_div; + /* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */ snd_soc_update_bits(codec, AIC31XX_CLKMUX, AIC31XX_CODEC_CLKIN_MASK, AIC31XX_CODEC_CLKIN_PLL); @@ -840,11 +842,17 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec, dev_dbg(codec->dev, "pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n", - aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d, - aic31xx->p_div, aic31xx_divs[i].dosr, - aic31xx_divs[i].ndac, aic31xx_divs[i].mdac, - aic31xx_divs[i].aosr, aic31xx_divs[i].nadc, - aic31xx_divs[i].madc, bclk_n); + aic31xx_divs[i].pll_j, + aic31xx_divs[i].pll_d, + aic31xx->p_div, + aic31xx_divs[i].dosr, + aic31xx_divs[i].ndac, + aic31xx_divs[i].mdac, + aic31xx_divs[i].aosr, + aic31xx_divs[i].nadc, + aic31xx_divs[i].madc, + bclk_n + ); return 0; } @@ -919,8 +927,28 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, case SND_SOC_DAIFMT_CBM_CFM: iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER; break; + case SND_SOC_DAIFMT_CBS_CFM: + iface_reg1 |= AIC31XX_WCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBM_CFS: + iface_reg1 |= AIC31XX_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + dev_err(codec->dev, "Invalid DAI master/slave interface\n"); + return -EINVAL; + } + + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + iface_reg2 |= AIC31XX_BCLKINV_MASK; + break; default: - dev_alert(codec->dev, "Invalid DAI master/slave interface\n"); + dev_err(codec->dev, "Invalid DAI clock signal polarity\n"); return -EINVAL; } @@ -931,16 +959,12 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, case SND_SOC_DAIFMT_DSP_A: dsp_a_val = 0x1; /* fall through */ case SND_SOC_DAIFMT_DSP_B: - /* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - iface_reg2 |= AIC31XX_BCLKINV_MASK; - break; - case SND_SOC_DAIFMT_IB_NF: - break; - default: - return -EINVAL; - } + /* + * NOTE: This CODEC samples on the falling edge of BCLK in + * DSP mode, this is inverted compared to what most DAIs + * expect, so we invert for this mode + */ + iface_reg2 ^= AIC31XX_BCLKINV_MASK; iface_reg1 |= (AIC31XX_DSP_MODE << AIC31XX_IFACE1_DATATYPE_SHIFT); break; @@ -981,8 +1005,9 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, dev_dbg(codec->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n", __func__, clk_id, freq, dir); - for (i = 1; freq/i > 20000000 && i < 8; i++) - ; + for (i = 1; i < 8; i++) + if (freq / i <= 20000000) + break; if (freq/i > 20000000) { dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n", __func__, freq); @@ -990,9 +1015,9 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, } aic31xx->p_div = i; - for (i = 0; i < ARRAY_SIZE(aic31xx_divs) && - aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) - ; + for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) + if (aic31xx_divs[i].mclk_p == freq / aic31xx->p_div) + break; if (i == ARRAY_SIZE(aic31xx_divs)) { dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n", __func__, freq); @@ -1004,6 +1029,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, clk_id << AIC31XX_PLL_CLKIN_SHIFT); aic31xx->sysclk = freq; + return 0; } @@ -1019,8 +1045,8 @@ static int aic31xx_regulator_event(struct notifier_block *nb, * Put codec to reset and as at least one of the * supplies was disabled. */ - if (gpio_is_valid(aic31xx->pdata.gpio_reset)) - gpio_set_value(aic31xx->pdata.gpio_reset, 0); + if (aic31xx->gpio_reset) + gpiod_set_value(aic31xx->gpio_reset, 1); regcache_mark_dirty(aic31xx->regmap); dev_dbg(aic31xx->dev, "## %s: DISABLE received\n", __func__); @@ -1029,6 +1055,22 @@ static int aic31xx_regulator_event(struct notifier_block *nb, return 0; } +static int aic31xx_reset(struct aic31xx_priv *aic31xx) +{ + int ret = 0; + + if (aic31xx->gpio_reset) { + gpiod_set_value(aic31xx->gpio_reset, 1); + ndelay(10); /* At least 10ns */ + gpiod_set_value(aic31xx->gpio_reset, 0); + } else { + ret = regmap_write(aic31xx->regmap, AIC31XX_RESET, 1); + } + mdelay(1); /* At least 1ms */ + + return ret; +} + static void aic31xx_clk_on(struct snd_soc_codec *codec) { struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); @@ -1065,20 +1107,22 @@ static void aic31xx_clk_off(struct snd_soc_codec *codec) static int aic31xx_power_on(struct snd_soc_codec *codec) { struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); - int ret = 0; + int ret; ret = regulator_bulk_enable(ARRAY_SIZE(aic31xx->supplies), aic31xx->supplies); if (ret) return ret; - if (gpio_is_valid(aic31xx->pdata.gpio_reset)) { - gpio_set_value(aic31xx->pdata.gpio_reset, 1); - udelay(100); - } regcache_cache_only(aic31xx->regmap, false); + + /* Reset device registers for a consistent power-on like state */ + ret = aic31xx_reset(aic31xx); + if (ret < 0) + dev_err(aic31xx->dev, "Could not reset device: %d\n", ret); + ret = regcache_sync(aic31xx->regmap); - if (ret != 0) { + if (ret) { dev_err(codec->dev, "Failed to restore cache: %d\n", ret); regcache_cache_only(aic31xx->regmap, true); @@ -1086,19 +1130,17 @@ static int aic31xx_power_on(struct snd_soc_codec *codec) aic31xx->supplies); return ret; } + return 0; } -static int aic31xx_power_off(struct snd_soc_codec *codec) +static void aic31xx_power_off(struct snd_soc_codec *codec) { struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); - int ret = 0; regcache_cache_only(aic31xx->regmap, true); - ret = regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies), - aic31xx->supplies); - - return ret; + regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies), + aic31xx->supplies); } static int aic31xx_set_bias_level(struct snd_soc_codec *codec, @@ -1137,14 +1179,11 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec, static int aic31xx_codec_probe(struct snd_soc_codec *codec) { - int ret = 0; struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); - int i; + int i, ret; dev_dbg(aic31xx->dev, "## %s\n", __func__); - aic31xx = snd_soc_codec_get_drvdata(codec); - aic31xx->codec = codec; for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) { @@ -1169,8 +1208,10 @@ static int aic31xx_codec_probe(struct snd_soc_codec *codec) return ret; ret = aic31xx_add_widgets(codec); + if (ret) + return ret; - return ret; + return 0; } static int aic31xx_codec_remove(struct snd_soc_codec *codec) @@ -1258,89 +1299,31 @@ static const struct of_device_id tlv320aic31xx_of_match[] = { {}, }; MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match); - -static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx) -{ - struct device_node *np = aic31xx->dev->of_node; - unsigned int value = MICBIAS_2_0V; - int ret; - - of_property_read_u32(np, "ai31xx-micbias-vg", &value); - switch (value) { - case MICBIAS_2_0V: - case MICBIAS_2_5V: - case MICBIAS_AVDDV: - aic31xx->pdata.micbias_vg = value; - break; - default: - dev_err(aic31xx->dev, - "Bad ai31xx-micbias-vg value %d DT\n", - value); - aic31xx->pdata.micbias_vg = MICBIAS_2_0V; - } - - ret = of_get_named_gpio(np, "gpio-reset", 0); - if (ret > 0) - aic31xx->pdata.gpio_reset = ret; -} -#else /* CONFIG_OF */ -static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx) -{ -} #endif /* CONFIG_OF */ -static int aic31xx_device_init(struct aic31xx_priv *aic31xx) -{ - int ret, i; - - dev_set_drvdata(aic31xx->dev, aic31xx); - - if (dev_get_platdata(aic31xx->dev)) - memcpy(&aic31xx->pdata, dev_get_platdata(aic31xx->dev), - sizeof(aic31xx->pdata)); - else if (aic31xx->dev->of_node) - aic31xx_pdata_from_of(aic31xx); - - if (aic31xx->pdata.gpio_reset) { - ret = devm_gpio_request_one(aic31xx->dev, - aic31xx->pdata.gpio_reset, - GPIOF_OUT_INIT_HIGH, - "aic31xx-reset-pin"); - if (ret < 0) { - dev_err(aic31xx->dev, "not able to acquire gpio\n"); - return ret; - } - } - - for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) - aic31xx->supplies[i].supply = aic31xx_supply_names[i]; - - ret = devm_regulator_bulk_get(aic31xx->dev, - ARRAY_SIZE(aic31xx->supplies), - aic31xx->supplies); - if (ret != 0) - dev_err(aic31xx->dev, "Failed to request supplies: %d\n", ret); - - return ret; -} +#ifdef CONFIG_ACPI +static const struct acpi_device_id aic31xx_acpi_match[] = { + { "10TI3100", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match); +#endif static int aic31xx_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct aic31xx_priv *aic31xx; - int ret; - const struct regmap_config *regmap_config; + unsigned int micbias_value = MICBIAS_2_0V; + int i, ret; dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__, - id->name, (int) id->driver_data); - - regmap_config = &aic31xx_i2c_regmap; + id->name, (int)id->driver_data); aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL); - if (aic31xx == NULL) + if (!aic31xx) return -ENOMEM; - aic31xx->regmap = devm_regmap_init_i2c(i2c, regmap_config); + aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap); if (IS_ERR(aic31xx->regmap)) { ret = PTR_ERR(aic31xx->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", @@ -1349,13 +1332,49 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, } aic31xx->dev = &i2c->dev; - aic31xx->pdata.codec_type = id->driver_data; + aic31xx->codec_type = id->driver_data; - ret = aic31xx_device_init(aic31xx); - if (ret) + dev_set_drvdata(aic31xx->dev, aic31xx); + + fwnode_property_read_u32(aic31xx->dev->fwnode, "ai31xx-micbias-vg", + &micbias_value); + switch (micbias_value) { + case MICBIAS_2_0V: + case MICBIAS_2_5V: + case MICBIAS_AVDDV: + aic31xx->micbias_vg = micbias_value; + break; + default: + dev_err(aic31xx->dev, "Bad ai31xx-micbias-vg value %d\n", + micbias_value); + aic31xx->micbias_vg = MICBIAS_2_0V; + } + + if (dev_get_platdata(aic31xx->dev)) { + memcpy(&aic31xx->pdata, dev_get_platdata(aic31xx->dev), sizeof(aic31xx->pdata)); + aic31xx->codec_type = aic31xx->pdata.codec_type; + aic31xx->micbias_vg = aic31xx->pdata.micbias_vg; + } + + aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(aic31xx->gpio_reset)) { + dev_err(aic31xx->dev, "not able to acquire gpio\n"); + return PTR_ERR(aic31xx->gpio_reset); + } + + for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) + aic31xx->supplies[i].supply = aic31xx_supply_names[i]; + + ret = devm_regulator_bulk_get(aic31xx->dev, + ARRAY_SIZE(aic31xx->supplies), + aic31xx->supplies); + if (ret) { + dev_err(aic31xx->dev, "Failed to request supplies: %d\n", ret); return ret; + } - if (aic31xx->pdata.codec_type & DAC31XX_BIT) + if (aic31xx->codec_type & DAC31XX_BIT) return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx, dac31xx_dai_driver, @@ -1386,14 +1405,6 @@ static const struct i2c_device_id aic31xx_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); -#ifdef CONFIG_ACPI -static const struct acpi_device_id aic31xx_acpi_match[] = { - { "10TI3100", 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match); -#endif - static struct i2c_driver aic31xx_i2c_driver = { .driver = { .name = "tlv320aic31xx-codec", @@ -1404,9 +1415,8 @@ static struct i2c_driver aic31xx_i2c_driver = { .remove = aic31xx_i2c_remove, .id_table = aic31xx_i2c_id, }; - module_i2c_driver(aic31xx_i2c_driver); -MODULE_DESCRIPTION("ASoC TLV320AIC3111 codec driver"); -MODULE_AUTHOR("Jyri Sarha"); -MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>"); +MODULE_DESCRIPTION("ASoC TLV320AIC31xx CODEC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index 730fb2058869..15ac7cba86fe 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -1,36 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * ALSA SoC TLV320AIC31XX codec driver - * - * Copyright (C) 2013 Texas Instruments, Inc. - * - * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * ALSA SoC TLV320AIC31xx CODEC Driver Definitions * + * Copyright (C) 2014-2017 Texas Instruments Incorporated - http://www.ti.com/ */ + #ifndef _TLV320AIC31XX_H #define _TLV320AIC31XX_H #define AIC31XX_RATES SNDRV_PCM_RATE_8000_192000 -#define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ - | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \ - | SNDRV_PCM_FMTBIT_S32_LE) - +#define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) -#define AIC31XX_STEREO_CLASS_D_BIT 0x1 -#define AIC31XX_MINIDSP_BIT 0x2 -#define DAC31XX_BIT 0x4 +#define AIC31XX_STEREO_CLASS_D_BIT BIT(1) +#define AIC31XX_MINIDSP_BIT BIT(2) +#define DAC31XX_BIT BIT(3) enum aic31xx_type { AIC3100 = 0, AIC3110 = AIC31XX_STEREO_CLASS_D_BIT, AIC3120 = AIC31XX_MINIDSP_BIT, - AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT), + AIC3111 = AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT, DAC3100 = DAC31XX_BIT, DAC3101 = DAC31XX_BIT | AIC31XX_STEREO_CLASS_D_BIT, }; @@ -43,222 +37,167 @@ struct aic31xx_pdata { #define AIC31XX_REG(page, reg) ((page * 128) + reg) -/* Page Control Register */ -#define AIC31XX_PAGECTL AIC31XX_REG(0, 0) +#define AIC31XX_PAGECTL AIC31XX_REG(0, 0) /* Page Control Register */ /* Page 0 Registers */ -/* Software reset register */ -#define AIC31XX_RESET AIC31XX_REG(0, 1) -/* OT FLAG register */ -#define AIC31XX_OT_FLAG AIC31XX_REG(0, 3) -/* Clock clock Gen muxing, Multiplexers*/ -#define AIC31XX_CLKMUX AIC31XX_REG(0, 4) -/* PLL P and R-VAL register */ -#define AIC31XX_PLLPR AIC31XX_REG(0, 5) -/* PLL J-VAL register */ -#define AIC31XX_PLLJ AIC31XX_REG(0, 6) -/* PLL D-VAL MSB register */ -#define AIC31XX_PLLDMSB AIC31XX_REG(0, 7) -/* PLL D-VAL LSB register */ -#define AIC31XX_PLLDLSB AIC31XX_REG(0, 8) -/* DAC NDAC_VAL register*/ -#define AIC31XX_NDAC AIC31XX_REG(0, 11) -/* DAC MDAC_VAL register */ -#define AIC31XX_MDAC AIC31XX_REG(0, 12) -/* DAC OSR setting register 1, MSB value */ -#define AIC31XX_DOSRMSB AIC31XX_REG(0, 13) -/* DAC OSR setting register 2, LSB value */ -#define AIC31XX_DOSRLSB AIC31XX_REG(0, 14) +#define AIC31XX_RESET AIC31XX_REG(0, 1) /* Software reset register */ +#define AIC31XX_OT_FLAG AIC31XX_REG(0, 3) /* OT FLAG register */ +#define AIC31XX_CLKMUX AIC31XX_REG(0, 4) /* Clock clock Gen muxing, Multiplexers*/ +#define AIC31XX_PLLPR AIC31XX_REG(0, 5) /* PLL P and R-VAL register */ +#define AIC31XX_PLLJ AIC31XX_REG(0, 6) /* PLL J-VAL register */ +#define AIC31XX_PLLDMSB AIC31XX_REG(0, 7) /* PLL D-VAL MSB register */ +#define AIC31XX_PLLDLSB AIC31XX_REG(0, 8) /* PLL D-VAL LSB register */ +#define AIC31XX_NDAC AIC31XX_REG(0, 11) /* DAC NDAC_VAL register*/ +#define AIC31XX_MDAC AIC31XX_REG(0, 12) /* DAC MDAC_VAL register */ +#define AIC31XX_DOSRMSB AIC31XX_REG(0, 13) /* DAC OSR setting register 1, MSB value */ +#define AIC31XX_DOSRLSB AIC31XX_REG(0, 14) /* DAC OSR setting register 2, LSB value */ #define AIC31XX_MINI_DSP_INPOL AIC31XX_REG(0, 16) -/* Clock setting register 8, PLL */ -#define AIC31XX_NADC AIC31XX_REG(0, 18) -/* Clock setting register 9, PLL */ -#define AIC31XX_MADC AIC31XX_REG(0, 19) -/* ADC Oversampling (AOSR) Register */ -#define AIC31XX_AOSR AIC31XX_REG(0, 20) -/* Clock setting register 9, Multiplexers */ -#define AIC31XX_CLKOUTMUX AIC31XX_REG(0, 25) -/* Clock setting register 10, CLOCKOUT M divider value */ -#define AIC31XX_CLKOUTMVAL AIC31XX_REG(0, 26) -/* Audio Interface Setting Register 1 */ -#define AIC31XX_IFACE1 AIC31XX_REG(0, 27) -/* Audio Data Slot Offset Programming */ -#define AIC31XX_DATA_OFFSET AIC31XX_REG(0, 28) -/* Audio Interface Setting Register 2 */ -#define AIC31XX_IFACE2 AIC31XX_REG(0, 29) -/* Clock setting register 11, BCLK N Divider */ -#define AIC31XX_BCLKN AIC31XX_REG(0, 30) -/* Audio Interface Setting Register 3, Secondary Audio Interface */ -#define AIC31XX_IFACESEC1 AIC31XX_REG(0, 31) -/* Audio Interface Setting Register 4 */ -#define AIC31XX_IFACESEC2 AIC31XX_REG(0, 32) -/* Audio Interface Setting Register 5 */ -#define AIC31XX_IFACESEC3 AIC31XX_REG(0, 33) -/* I2C Bus Condition */ -#define AIC31XX_I2C AIC31XX_REG(0, 34) -/* ADC FLAG */ -#define AIC31XX_ADCFLAG AIC31XX_REG(0, 36) -/* DAC Flag Registers */ -#define AIC31XX_DACFLAG1 AIC31XX_REG(0, 37) +#define AIC31XX_NADC AIC31XX_REG(0, 18) /* Clock setting register 8, PLL */ +#define AIC31XX_MADC AIC31XX_REG(0, 19) /* Clock setting register 9, PLL */ +#define AIC31XX_AOSR AIC31XX_REG(0, 20) /* ADC Oversampling (AOSR) Register */ +#define AIC31XX_CLKOUTMUX AIC31XX_REG(0, 25) /* Clock setting register 9, Multiplexers */ +#define AIC31XX_CLKOUTMVAL AIC31XX_REG(0, 26) /* Clock setting register 10, CLOCKOUT M divider value */ +#define AIC31XX_IFACE1 AIC31XX_REG(0, 27) /* Audio Interface Setting Register 1 */ +#define AIC31XX_DATA_OFFSET AIC31XX_REG(0, 28) /* Audio Data Slot Offset Programming */ +#define AIC31XX_IFACE2 AIC31XX_REG(0, 29) /* Audio Interface Setting Register 2 */ +#define AIC31XX_BCLKN AIC31XX_REG(0, 30) /* Clock setting register 11, BCLK N Divider */ +#define AIC31XX_IFACESEC1 AIC31XX_REG(0, 31) /* Audio Interface Setting Register 3, Secondary Audio Interface */ +#define AIC31XX_IFACESEC2 AIC31XX_REG(0, 32) /* Audio Interface Setting Register 4 */ +#define AIC31XX_IFACESEC3 AIC31XX_REG(0, 33) /* Audio Interface Setting Register 5 */ +#define AIC31XX_I2C AIC31XX_REG(0, 34) /* I2C Bus Condition */ +#define AIC31XX_ADCFLAG AIC31XX_REG(0, 36) /* ADC FLAG */ +#define AIC31XX_DACFLAG1 AIC31XX_REG(0, 37) /* DAC Flag Registers */ #define AIC31XX_DACFLAG2 AIC31XX_REG(0, 38) -/* Sticky Interrupt flag (overflow) */ -#define AIC31XX_OFFLAG AIC31XX_REG(0, 39) -/* Sticy DAC Interrupt flags */ -#define AIC31XX_INTRDACFLAG AIC31XX_REG(0, 44) -/* Sticy ADC Interrupt flags */ -#define AIC31XX_INTRADCFLAG AIC31XX_REG(0, 45) -/* DAC Interrupt flags 2 */ -#define AIC31XX_INTRDACFLAG2 AIC31XX_REG(0, 46) -/* ADC Interrupt flags 2 */ -#define AIC31XX_INTRADCFLAG2 AIC31XX_REG(0, 47) -/* INT1 interrupt control */ -#define AIC31XX_INT1CTRL AIC31XX_REG(0, 48) -/* INT2 interrupt control */ -#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49) -/* GPIO1 control */ -#define AIC31XX_GPIO1 AIC31XX_REG(0, 50) - +#define AIC31XX_OFFLAG AIC31XX_REG(0, 39) /* Sticky Interrupt flag (overflow) */ +#define AIC31XX_INTRDACFLAG AIC31XX_REG(0, 44) /* Sticy DAC Interrupt flags */ +#define AIC31XX_INTRADCFLAG AIC31XX_REG(0, 45) /* Sticy ADC Interrupt flags */ +#define AIC31XX_INTRDACFLAG2 AIC31XX_REG(0, 46) /* DAC Interrupt flags 2 */ +#define AIC31XX_INTRADCFLAG2 AIC31XX_REG(0, 47) /* ADC Interrupt flags 2 */ +#define AIC31XX_INT1CTRL AIC31XX_REG(0, 48) /* INT1 interrupt control */ +#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49) /* INT2 interrupt control */ +#define AIC31XX_GPIO1 AIC31XX_REG(0, 51) /* GPIO1 control */ #define AIC31XX_DACPRB AIC31XX_REG(0, 60) -/* ADC Instruction Set Register */ -#define AIC31XX_ADCPRB AIC31XX_REG(0, 61) -/* DAC channel setup register */ -#define AIC31XX_DACSETUP AIC31XX_REG(0, 63) -/* DAC Mute and volume control register */ -#define AIC31XX_DACMUTE AIC31XX_REG(0, 64) -/* Left DAC channel digital volume control */ -#define AIC31XX_LDACVOL AIC31XX_REG(0, 65) -/* Right DAC channel digital volume control */ -#define AIC31XX_RDACVOL AIC31XX_REG(0, 66) -/* Headset detection */ -#define AIC31XX_HSDETECT AIC31XX_REG(0, 67) -/* ADC Digital Mic */ -#define AIC31XX_ADCSETUP AIC31XX_REG(0, 81) -/* ADC Digital Volume Control Fine Adjust */ -#define AIC31XX_ADCFGA AIC31XX_REG(0, 82) -/* ADC Digital Volume Control Coarse Adjust */ -#define AIC31XX_ADCVOL AIC31XX_REG(0, 83) - +#define AIC31XX_ADCPRB AIC31XX_REG(0, 61) /* ADC Instruction Set Register */ +#define AIC31XX_DACSETUP AIC31XX_REG(0, 63) /* DAC channel setup register */ +#define AIC31XX_DACMUTE AIC31XX_REG(0, 64) /* DAC Mute and volume control register */ +#define AIC31XX_LDACVOL AIC31XX_REG(0, 65) /* Left DAC channel digital volume control */ +#define AIC31XX_RDACVOL AIC31XX_REG(0, 66) /* Right DAC channel digital volume control */ +#define AIC31XX_HSDETECT AIC31XX_REG(0, 67) /* Headset detection */ +#define AIC31XX_ADCSETUP AIC31XX_REG(0, 81) /* ADC Digital Mic */ +#define AIC31XX_ADCFGA AIC31XX_REG(0, 82) /* ADC Digital Volume Control Fine Adjust */ +#define AIC31XX_ADCVOL AIC31XX_REG(0, 83) /* ADC Digital Volume Control Coarse Adjust */ /* Page 1 Registers */ -/* Headphone drivers */ -#define AIC31XX_HPDRIVER AIC31XX_REG(1, 31) -/* Class-D Speakear Amplifier */ -#define AIC31XX_SPKAMP AIC31XX_REG(1, 32) -/* HP Output Drivers POP Removal Settings */ -#define AIC31XX_HPPOP AIC31XX_REG(1, 33) -/* Output Driver PGA Ramp-Down Period Control */ -#define AIC31XX_SPPGARAMP AIC31XX_REG(1, 34) -/* DAC_L and DAC_R Output Mixer Routing */ -#define AIC31XX_DACMIXERROUTE AIC31XX_REG(1, 35) -/* Left Analog Vol to HPL */ -#define AIC31XX_LANALOGHPL AIC31XX_REG(1, 36) -/* Right Analog Vol to HPR */ -#define AIC31XX_RANALOGHPR AIC31XX_REG(1, 37) -/* Left Analog Vol to SPL */ -#define AIC31XX_LANALOGSPL AIC31XX_REG(1, 38) -/* Right Analog Vol to SPR */ -#define AIC31XX_RANALOGSPR AIC31XX_REG(1, 39) -/* HPL Driver */ -#define AIC31XX_HPLGAIN AIC31XX_REG(1, 40) -/* HPR Driver */ -#define AIC31XX_HPRGAIN AIC31XX_REG(1, 41) -/* SPL Driver */ -#define AIC31XX_SPLGAIN AIC31XX_REG(1, 42) -/* SPR Driver */ -#define AIC31XX_SPRGAIN AIC31XX_REG(1, 43) -/* HP Driver Control */ -#define AIC31XX_HPCONTROL AIC31XX_REG(1, 44) -/* MIC Bias Control */ -#define AIC31XX_MICBIAS AIC31XX_REG(1, 46) -/* MIC PGA*/ -#define AIC31XX_MICPGA AIC31XX_REG(1, 47) -/* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */ -#define AIC31XX_MICPGAPI AIC31XX_REG(1, 48) -/* ADC Input Selection for M-Terminal */ -#define AIC31XX_MICPGAMI AIC31XX_REG(1, 49) -/* Input CM Settings */ -#define AIC31XX_MICPGACM AIC31XX_REG(1, 50) - -/* Bits, masks and shifts */ +#define AIC31XX_HPDRIVER AIC31XX_REG(1, 31) /* Headphone drivers */ +#define AIC31XX_SPKAMP AIC31XX_REG(1, 32) /* Class-D Speakear Amplifier */ +#define AIC31XX_HPPOP AIC31XX_REG(1, 33) /* HP Output Drivers POP Removal Settings */ +#define AIC31XX_SPPGARAMP AIC31XX_REG(1, 34) /* Output Driver PGA Ramp-Down Period Control */ +#define AIC31XX_DACMIXERROUTE AIC31XX_REG(1, 35) /* DAC_L and DAC_R Output Mixer Routing */ +#define AIC31XX_LANALOGHPL AIC31XX_REG(1, 36) /* Left Analog Vol to HPL */ +#define AIC31XX_RANALOGHPR AIC31XX_REG(1, 37) /* Right Analog Vol to HPR */ +#define AIC31XX_LANALOGSPL AIC31XX_REG(1, 38) /* Left Analog Vol to SPL */ +#define AIC31XX_RANALOGSPR AIC31XX_REG(1, 39) /* Right Analog Vol to SPR */ +#define AIC31XX_HPLGAIN AIC31XX_REG(1, 40) /* HPL Driver */ +#define AIC31XX_HPRGAIN AIC31XX_REG(1, 41) /* HPR Driver */ +#define AIC31XX_SPLGAIN AIC31XX_REG(1, 42) /* SPL Driver */ +#define AIC31XX_SPRGAIN AIC31XX_REG(1, 43) /* SPR Driver */ +#define AIC31XX_HPCONTROL AIC31XX_REG(1, 44) /* HP Driver Control */ +#define AIC31XX_MICBIAS AIC31XX_REG(1, 46) /* MIC Bias Control */ +#define AIC31XX_MICPGA AIC31XX_REG(1, 47) /* MIC PGA*/ +#define AIC31XX_MICPGAPI AIC31XX_REG(1, 48) /* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */ +#define AIC31XX_MICPGAMI AIC31XX_REG(1, 49) /* ADC Input Selection for M-Terminal */ +#define AIC31XX_MICPGACM AIC31XX_REG(1, 50) /* Input CM Settings */ + +/* Bits, masks, and shifts */ /* AIC31XX_CLKMUX */ -#define AIC31XX_PLL_CLKIN_MASK 0x0c -#define AIC31XX_PLL_CLKIN_SHIFT 2 -#define AIC31XX_PLL_CLKIN_MCLK 0 -#define AIC31XX_CODEC_CLKIN_MASK 0x03 -#define AIC31XX_CODEC_CLKIN_SHIFT 0 -#define AIC31XX_CODEC_CLKIN_PLL 3 -#define AIC31XX_CODEC_CLKIN_BCLK 1 - -/* AIC31XX_PLLPR, AIC31XX_NDAC, AIC31XX_MDAC, AIC31XX_NADC, AIC31XX_MADC, - AIC31XX_BCLKN */ -#define AIC31XX_PLL_MASK 0x7f -#define AIC31XX_PM_MASK 0x80 +#define AIC31XX_PLL_CLKIN_MASK GENMASK(3, 2) +#define AIC31XX_PLL_CLKIN_SHIFT (2) +#define AIC31XX_PLL_CLKIN_MCLK 0x00 +#define AIC31XX_PLL_CLKIN_BCKL 0x01 +#define AIC31XX_PLL_CLKIN_GPIO1 0x02 +#define AIC31XX_PLL_CLKIN_DIN 0x03 +#define AIC31XX_CODEC_CLKIN_MASK GENMASK(1, 0) +#define AIC31XX_CODEC_CLKIN_SHIFT (0) +#define AIC31XX_CODEC_CLKIN_MCLK 0x00 +#define AIC31XX_CODEC_CLKIN_BCLK 0x01 +#define AIC31XX_CODEC_CLKIN_GPIO1 0x02 +#define AIC31XX_CODEC_CLKIN_PLL 0x03 + +/* AIC31XX_PLLPR */ +/* AIC31XX_NDAC */ +/* AIC31XX_MDAC */ +/* AIC31XX_NADC */ +/* AIC31XX_MADC */ +/* AIC31XX_BCLKN */ +#define AIC31XX_PLL_MASK GENMASK(6, 0) +#define AIC31XX_PM_MASK BIT(7) /* AIC31XX_IFACE1 */ -#define AIC31XX_WORD_LEN_16BITS 0x00 -#define AIC31XX_WORD_LEN_20BITS 0x01 -#define AIC31XX_WORD_LEN_24BITS 0x02 -#define AIC31XX_WORD_LEN_32BITS 0x03 -#define AIC31XX_IFACE1_DATALEN_MASK 0x30 -#define AIC31XX_IFACE1_DATALEN_SHIFT (4) -#define AIC31XX_IFACE1_DATATYPE_MASK 0xC0 +#define AIC31XX_IFACE1_DATATYPE_MASK GENMASK(7, 6) #define AIC31XX_IFACE1_DATATYPE_SHIFT (6) #define AIC31XX_I2S_MODE 0x00 #define AIC31XX_DSP_MODE 0x01 #define AIC31XX_RIGHT_JUSTIFIED_MODE 0x02 #define AIC31XX_LEFT_JUSTIFIED_MODE 0x03 -#define AIC31XX_IFACE1_MASTER_MASK 0x0C -#define AIC31XX_BCLK_MASTER 0x08 -#define AIC31XX_WCLK_MASTER 0x04 +#define AIC31XX_IFACE1_DATALEN_MASK GENMASK(5, 4) +#define AIC31XX_IFACE1_DATALEN_SHIFT (4) +#define AIC31XX_WORD_LEN_16BITS 0x00 +#define AIC31XX_WORD_LEN_20BITS 0x01 +#define AIC31XX_WORD_LEN_24BITS 0x02 +#define AIC31XX_WORD_LEN_32BITS 0x03 +#define AIC31XX_IFACE1_MASTER_MASK GENMASK(3, 2) +#define AIC31XX_BCLK_MASTER BIT(2) +#define AIC31XX_WCLK_MASTER BIT(3) /* AIC31XX_DATA_OFFSET */ -#define AIC31XX_DATA_OFFSET_MASK 0xFF +#define AIC31XX_DATA_OFFSET_MASK GENMASK(7, 0) /* AIC31XX_IFACE2 */ -#define AIC31XX_BCLKINV_MASK 0x08 -#define AIC31XX_BDIVCLK_MASK 0x03 +#define AIC31XX_BCLKINV_MASK BIT(3) +#define AIC31XX_BDIVCLK_MASK GENMASK(1, 0) #define AIC31XX_DAC2BCLK 0x00 #define AIC31XX_DACMOD2BCLK 0x01 #define AIC31XX_ADC2BCLK 0x02 #define AIC31XX_ADCMOD2BCLK 0x03 /* AIC31XX_ADCFLAG */ -#define AIC31XX_ADCPWRSTATUS_MASK 0x40 +#define AIC31XX_ADCPWRSTATUS_MASK BIT(6) /* AIC31XX_DACFLAG1 */ -#define AIC31XX_LDACPWRSTATUS_MASK 0x80 -#define AIC31XX_RDACPWRSTATUS_MASK 0x08 -#define AIC31XX_HPLDRVPWRSTATUS_MASK 0x20 -#define AIC31XX_HPRDRVPWRSTATUS_MASK 0x02 -#define AIC31XX_SPLDRVPWRSTATUS_MASK 0x10 -#define AIC31XX_SPRDRVPWRSTATUS_MASK 0x01 +#define AIC31XX_LDACPWRSTATUS_MASK BIT(7) +#define AIC31XX_HPLDRVPWRSTATUS_MASK BIT(5) +#define AIC31XX_SPLDRVPWRSTATUS_MASK BIT(4) +#define AIC31XX_RDACPWRSTATUS_MASK BIT(3) +#define AIC31XX_HPRDRVPWRSTATUS_MASK BIT(1) +#define AIC31XX_SPRDRVPWRSTATUS_MASK BIT(0) /* AIC31XX_INTRDACFLAG */ -#define AIC31XX_HPSCDETECT_MASK 0x80 -#define AIC31XX_BUTTONPRESS_MASK 0x20 -#define AIC31XX_HSPLUG_MASK 0x10 -#define AIC31XX_LDRCTHRES_MASK 0x08 -#define AIC31XX_RDRCTHRES_MASK 0x04 -#define AIC31XX_DACSINT_MASK 0x02 -#define AIC31XX_DACAINT_MASK 0x01 +#define AIC31XX_HPLSCDETECT BIT(7) +#define AIC31XX_HPRSCDETECT BIT(6) +#define AIC31XX_BUTTONPRESS BIT(5) +#define AIC31XX_HSPLUG BIT(4) +#define AIC31XX_LDRCTHRES BIT(3) +#define AIC31XX_RDRCTHRES BIT(2) +#define AIC31XX_DACSINT BIT(1) +#define AIC31XX_DACAINT BIT(0) /* AIC31XX_INT1CTRL */ -#define AIC31XX_HSPLUGDET_MASK 0x80 -#define AIC31XX_BUTTONPRESSDET_MASK 0x40 -#define AIC31XX_DRCTHRES_MASK 0x20 -#define AIC31XX_AGCNOISE_MASK 0x10 -#define AIC31XX_OC_MASK 0x08 -#define AIC31XX_ENGINE_MASK 0x04 +#define AIC31XX_HSPLUGDET BIT(7) +#define AIC31XX_BUTTONPRESSDET BIT(6) +#define AIC31XX_DRCTHRES BIT(5) +#define AIC31XX_AGCNOISE BIT(4) +#define AIC31XX_SC BIT(3) +#define AIC31XX_ENGINE BIT(2) /* AIC31XX_DACSETUP */ -#define AIC31XX_SOFTSTEP_MASK 0x03 +#define AIC31XX_SOFTSTEP_MASK GENMASK(1, 0) /* AIC31XX_DACMUTE */ -#define AIC31XX_DACMUTE_MASK 0x0C +#define AIC31XX_DACMUTE_MASK GENMASK(3, 2) /* AIC31XX_MICBIAS */ -#define AIC31XX_MICBIAS_MASK 0x03 -#define AIC31XX_MICBIAS_SHIFT 0 +#define AIC31XX_MICBIAS_MASK GENMASK(1, 0) +#define AIC31XX_MICBIAS_SHIFT 0 #endif /* _TLV320AIC31XX_H */ diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index e694f5f04eb9..fea019343c3b 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -281,34 +281,34 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = { static const struct aic32x4_rate_divs aic32x4_divs[] = { /* 8k rate */ - {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, - {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, - {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + {12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + {24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + {25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, /* 11.025k rate */ - {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, - {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + {12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, /* 16k rate */ - {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, - {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, - {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + {12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + {25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, /* 22.05k rate */ - {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, - {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, - {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + {12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + {25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, /* 32k rate */ - {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, - {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + {12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, /* 44.1k rate */ - {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, - {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, - {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + {12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + {25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, /* 48k rate */ - {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, - {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, - {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}, + {12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + {25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}, /* 96k rate */ - {AIC32X4_FREQ_25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1}, + {25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1}, }; static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { @@ -601,9 +601,9 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); switch (freq) { - case AIC32X4_FREQ_12000000: - case AIC32X4_FREQ_24000000: - case AIC32X4_FREQ_25000000: + case 12000000: + case 24000000: + case 25000000: aic32x4->sysclk = freq; return 0; } @@ -614,16 +614,9 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u8 iface_reg_1; - u8 iface_reg_2; - u8 iface_reg_3; - - iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1); - iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2); - iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2); - iface_reg_2 = 0; - iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3); - iface_reg_3 = iface_reg_3 & ~(1 << 3); + u8 iface_reg_1 = 0; + u8 iface_reg_2 = 0; + u8 iface_reg_3 = 0; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -641,30 +634,37 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_I2S: break; case SND_SOC_DAIFMT_DSP_A: - iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); - iface_reg_3 |= (1 << 3); /* invert bit clock */ + iface_reg_1 |= (AIC32X4_DSP_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); + iface_reg_3 |= AIC32X4_BCLKINV_MASK; /* invert bit clock */ iface_reg_2 = 0x01; /* add offset 1 */ break; case SND_SOC_DAIFMT_DSP_B: - iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); - iface_reg_3 |= (1 << 3); /* invert bit clock */ + iface_reg_1 |= (AIC32X4_DSP_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); + iface_reg_3 |= AIC32X4_BCLKINV_MASK; /* invert bit clock */ break; case SND_SOC_DAIFMT_RIGHT_J: - iface_reg_1 |= - (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_1 |= (AIC32X4_RIGHT_JUSTIFIED_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); break; case SND_SOC_DAIFMT_LEFT_J: - iface_reg_1 |= - (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_1 |= (AIC32X4_LEFT_JUSTIFIED_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); break; default: printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); return -EINVAL; } - snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1); - snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2); - snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3); + snd_soc_update_bits(codec, AIC32X4_IFACE1, + AIC32X4_IFACE1_DATATYPE_MASK | + AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); + snd_soc_update_bits(codec, AIC32X4_IFACE2, + AIC32X4_DATA_OFFSET_MASK, iface_reg_2); + snd_soc_update_bits(codec, AIC32X4_IFACE3, + AIC32X4_BCLKINV_MASK, iface_reg_3); + return 0; } @@ -674,7 +674,8 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); - u8 data; + u8 iface1_reg = 0; + u8 dacsetup_reg = 0; int i; i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params)); @@ -683,82 +684,88 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, return i; } - /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */ - snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN); - snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK); + /* MCLK as PLL_CLKIN */ + snd_soc_update_bits(codec, AIC32X4_CLKMUX, AIC32X4_PLL_CLKIN_MASK, + AIC32X4_PLL_CLKIN_MCLK << AIC32X4_PLL_CLKIN_SHIFT); + /* PLL as CODEC_CLKIN */ + snd_soc_update_bits(codec, AIC32X4_CLKMUX, AIC32X4_CODEC_CLKIN_MASK, + AIC32X4_CODEC_CLKIN_PLL << AIC32X4_CODEC_CLKIN_SHIFT); + /* DAC_MOD_CLK as BDIV_CLKIN */ + snd_soc_update_bits(codec, AIC32X4_IFACE3, AIC32X4_BDIVCLK_MASK, + AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT); + + /* We will fix R value to 1 and will make P & J=K.D as variable */ + snd_soc_update_bits(codec, AIC32X4_PLLPR, AIC32X4_PLL_R_MASK, 0x01); - /* We will fix R value to 1 and will make P & J=K.D as varialble */ - data = snd_soc_read(codec, AIC32X4_PLLPR); - data &= ~(7 << 4); - snd_soc_write(codec, AIC32X4_PLLPR, - (data | (aic32x4_divs[i].p_val << 4) | 0x01)); + /* PLL P value */ + snd_soc_update_bits(codec, AIC32X4_PLLPR, AIC32X4_PLL_P_MASK, + aic32x4_divs[i].p_val << AIC32X4_PLL_P_SHIFT); + /* PLL J value */ snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j); + /* PLL D value */ snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8)); - snd_soc_write(codec, AIC32X4_PLLDLSB, - (aic32x4_divs[i].pll_d & 0xff)); + snd_soc_write(codec, AIC32X4_PLLDLSB, (aic32x4_divs[i].pll_d & 0xff)); /* NDAC divider value */ - data = snd_soc_read(codec, AIC32X4_NDAC); - data &= ~(0x7f); - snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac); + snd_soc_update_bits(codec, AIC32X4_NDAC, + AIC32X4_NDAC_MASK, aic32x4_divs[i].ndac); /* MDAC divider value */ - data = snd_soc_read(codec, AIC32X4_MDAC); - data &= ~(0x7f); - snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac); + snd_soc_update_bits(codec, AIC32X4_MDAC, + AIC32X4_MDAC_MASK, aic32x4_divs[i].mdac); /* DOSR MSB & LSB values */ snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); - snd_soc_write(codec, AIC32X4_DOSRLSB, - (aic32x4_divs[i].dosr & 0xff)); + snd_soc_write(codec, AIC32X4_DOSRLSB, (aic32x4_divs[i].dosr & 0xff)); /* NADC divider value */ - data = snd_soc_read(codec, AIC32X4_NADC); - data &= ~(0x7f); - snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc); + snd_soc_update_bits(codec, AIC32X4_NADC, + AIC32X4_NADC_MASK, aic32x4_divs[i].nadc); /* MADC divider value */ - data = snd_soc_read(codec, AIC32X4_MADC); - data &= ~(0x7f); - snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc); + snd_soc_update_bits(codec, AIC32X4_MADC, + AIC32X4_MADC_MASK, aic32x4_divs[i].madc); /* AOSR value */ snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr); /* BCLK N divider */ - data = snd_soc_read(codec, AIC32X4_BCLKN); - data &= ~(0x7f); - snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N); + snd_soc_update_bits(codec, AIC32X4_BCLKN, + AIC32X4_BCLK_MASK, aic32x4_divs[i].blck_N); - data = snd_soc_read(codec, AIC32X4_IFACE1); - data = data & ~(3 << 4); switch (params_width(params)) { case 16: + iface1_reg |= (AIC32X4_WORD_LEN_16BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); break; case 20: - data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_20BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); break; case 24: - data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_24BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); break; case 32: - data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT); + iface1_reg |= (AIC32X4_WORD_LEN_32BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); break; } - snd_soc_write(codec, AIC32X4_IFACE1, data); + snd_soc_update_bits(codec, AIC32X4_IFACE1, + AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); if (params_channels(params) == 1) { - data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; + dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; } else { if (aic32x4->swapdacs) - data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN; + dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN; else - data = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; + dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; } - snd_soc_update_bits(codec, AIC32X4_DACSETUP, AIC32X4_DAC_CHAN_MASK, - data); + snd_soc_update_bits(codec, AIC32X4_DACSETUP, + AIC32X4_DAC_CHAN_MASK, dacsetup_reg); return 0; } @@ -766,13 +773,10 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, static int aic32x4_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u8 dac_reg; - dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON; - if (mute) - snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON); - else - snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg); + snd_soc_update_bits(codec, AIC32X4_DACMUTE, + AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); + return 0; } diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index da7cec482bcb..e9df49edbf19 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -19,141 +19,189 @@ int aic32x4_remove(struct device *dev); /* tlv320aic32x4 register space (in decimal to match datasheet) */ -#define AIC32X4_PAGE1 128 - -#define AIC32X4_PSEL 0 -#define AIC32X4_RESET 1 -#define AIC32X4_CLKMUX 4 -#define AIC32X4_PLLPR 5 -#define AIC32X4_PLLJ 6 -#define AIC32X4_PLLDMSB 7 -#define AIC32X4_PLLDLSB 8 -#define AIC32X4_NDAC 11 -#define AIC32X4_MDAC 12 -#define AIC32X4_DOSRMSB 13 -#define AIC32X4_DOSRLSB 14 -#define AIC32X4_NADC 18 -#define AIC32X4_MADC 19 -#define AIC32X4_AOSR 20 -#define AIC32X4_CLKMUX2 25 -#define AIC32X4_CLKOUTM 26 -#define AIC32X4_IFACE1 27 -#define AIC32X4_IFACE2 28 -#define AIC32X4_IFACE3 29 -#define AIC32X4_BCLKN 30 -#define AIC32X4_IFACE4 31 -#define AIC32X4_IFACE5 32 -#define AIC32X4_IFACE6 33 -#define AIC32X4_GPIOCTL 52 -#define AIC32X4_DOUTCTL 53 -#define AIC32X4_DINCTL 54 -#define AIC32X4_MISOCTL 55 -#define AIC32X4_SCLKCTL 56 -#define AIC32X4_DACSPB 60 -#define AIC32X4_ADCSPB 61 -#define AIC32X4_DACSETUP 63 -#define AIC32X4_DACMUTE 64 -#define AIC32X4_LDACVOL 65 -#define AIC32X4_RDACVOL 66 -#define AIC32X4_ADCSETUP 81 -#define AIC32X4_ADCFGA 82 -#define AIC32X4_LADCVOL 83 -#define AIC32X4_RADCVOL 84 -#define AIC32X4_LAGC1 86 -#define AIC32X4_LAGC2 87 -#define AIC32X4_LAGC3 88 -#define AIC32X4_LAGC4 89 -#define AIC32X4_LAGC5 90 -#define AIC32X4_LAGC6 91 -#define AIC32X4_LAGC7 92 -#define AIC32X4_RAGC1 94 -#define AIC32X4_RAGC2 95 -#define AIC32X4_RAGC3 96 -#define AIC32X4_RAGC4 97 -#define AIC32X4_RAGC5 98 -#define AIC32X4_RAGC6 99 -#define AIC32X4_RAGC7 100 -#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1) -#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2) -#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9) -#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10) -#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12) -#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13) -#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14) -#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15) -#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16) -#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17) -#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18) -#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19) -#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20) -#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51) -#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52) -#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54) -#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55) -#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57) -#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58) -#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59) -#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60) - -#define AIC32X4_FREQ_12000000 12000000 -#define AIC32X4_FREQ_24000000 24000000 -#define AIC32X4_FREQ_25000000 25000000 - -#define AIC32X4_WORD_LEN_16BITS 0x00 -#define AIC32X4_WORD_LEN_20BITS 0x01 -#define AIC32X4_WORD_LEN_24BITS 0x02 -#define AIC32X4_WORD_LEN_32BITS 0x03 - -#define AIC32X4_LADC_EN (1 << 7) -#define AIC32X4_RADC_EN (1 << 6) - -#define AIC32X4_I2S_MODE 0x00 -#define AIC32X4_DSP_MODE 0x01 -#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02 -#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03 - -#define AIC32X4_AVDDWEAKDISABLE 0x08 -#define AIC32X4_LDOCTLEN 0x01 - -#define AIC32X4_LDOIN_18_36 0x01 -#define AIC32X4_LDOIN2HP 0x02 - -#define AIC32X4_DACSPBLOCK_MASK 0x1f -#define AIC32X4_ADCSPBLOCK_MASK 0x1f - -#define AIC32X4_PLLJ_SHIFT 6 -#define AIC32X4_DOSRMSB_SHIFT 4 - -#define AIC32X4_PLLCLKIN 0x03 - -#define AIC32X4_MICBIAS_LDOIN 0x08 +#define AIC32X4_REG(page, reg) ((page * 128) + reg) + +#define AIC32X4_PSEL AIC32X4_REG(0, 0) + +#define AIC32X4_RESET AIC32X4_REG(0, 1) +#define AIC32X4_CLKMUX AIC32X4_REG(0, 4) +#define AIC32X4_PLLPR AIC32X4_REG(0, 5) +#define AIC32X4_PLLJ AIC32X4_REG(0, 6) +#define AIC32X4_PLLDMSB AIC32X4_REG(0, 7) +#define AIC32X4_PLLDLSB AIC32X4_REG(0, 8) +#define AIC32X4_NDAC AIC32X4_REG(0, 11) +#define AIC32X4_MDAC AIC32X4_REG(0, 12) +#define AIC32X4_DOSRMSB AIC32X4_REG(0, 13) +#define AIC32X4_DOSRLSB AIC32X4_REG(0, 14) +#define AIC32X4_NADC AIC32X4_REG(0, 18) +#define AIC32X4_MADC AIC32X4_REG(0, 19) +#define AIC32X4_AOSR AIC32X4_REG(0, 20) +#define AIC32X4_CLKMUX2 AIC32X4_REG(0, 25) +#define AIC32X4_CLKOUTM AIC32X4_REG(0, 26) +#define AIC32X4_IFACE1 AIC32X4_REG(0, 27) +#define AIC32X4_IFACE2 AIC32X4_REG(0, 28) +#define AIC32X4_IFACE3 AIC32X4_REG(0, 29) +#define AIC32X4_BCLKN AIC32X4_REG(0, 30) +#define AIC32X4_IFACE4 AIC32X4_REG(0, 31) +#define AIC32X4_IFACE5 AIC32X4_REG(0, 32) +#define AIC32X4_IFACE6 AIC32X4_REG(0, 33) +#define AIC32X4_GPIOCTL AIC32X4_REG(0, 52) +#define AIC32X4_DOUTCTL AIC32X4_REG(0, 53) +#define AIC32X4_DINCTL AIC32X4_REG(0, 54) +#define AIC32X4_MISOCTL AIC32X4_REG(0, 55) +#define AIC32X4_SCLKCTL AIC32X4_REG(0, 56) +#define AIC32X4_DACSPB AIC32X4_REG(0, 60) +#define AIC32X4_ADCSPB AIC32X4_REG(0, 61) +#define AIC32X4_DACSETUP AIC32X4_REG(0, 63) +#define AIC32X4_DACMUTE AIC32X4_REG(0, 64) +#define AIC32X4_LDACVOL AIC32X4_REG(0, 65) +#define AIC32X4_RDACVOL AIC32X4_REG(0, 66) +#define AIC32X4_ADCSETUP AIC32X4_REG(0, 81) +#define AIC32X4_ADCFGA AIC32X4_REG(0, 82) +#define AIC32X4_LADCVOL AIC32X4_REG(0, 83) +#define AIC32X4_RADCVOL AIC32X4_REG(0, 84) +#define AIC32X4_LAGC1 AIC32X4_REG(0, 86) +#define AIC32X4_LAGC2 AIC32X4_REG(0, 87) +#define AIC32X4_LAGC3 AIC32X4_REG(0, 88) +#define AIC32X4_LAGC4 AIC32X4_REG(0, 89) +#define AIC32X4_LAGC5 AIC32X4_REG(0, 90) +#define AIC32X4_LAGC6 AIC32X4_REG(0, 91) +#define AIC32X4_LAGC7 AIC32X4_REG(0, 92) +#define AIC32X4_RAGC1 AIC32X4_REG(0, 94) +#define AIC32X4_RAGC2 AIC32X4_REG(0, 95) +#define AIC32X4_RAGC3 AIC32X4_REG(0, 96) +#define AIC32X4_RAGC4 AIC32X4_REG(0, 97) +#define AIC32X4_RAGC5 AIC32X4_REG(0, 98) +#define AIC32X4_RAGC6 AIC32X4_REG(0, 99) +#define AIC32X4_RAGC7 AIC32X4_REG(0, 100) + +#define AIC32X4_PWRCFG AIC32X4_REG(1, 1) +#define AIC32X4_LDOCTL AIC32X4_REG(1, 2) +#define AIC32X4_OUTPWRCTL AIC32X4_REG(1, 9) +#define AIC32X4_CMMODE AIC32X4_REG(1, 10) +#define AIC32X4_HPLROUTE AIC32X4_REG(1, 12) +#define AIC32X4_HPRROUTE AIC32X4_REG(1, 13) +#define AIC32X4_LOLROUTE AIC32X4_REG(1, 14) +#define AIC32X4_LORROUTE AIC32X4_REG(1, 15) +#define AIC32X4_HPLGAIN AIC32X4_REG(1, 16) +#define AIC32X4_HPRGAIN AIC32X4_REG(1, 17) +#define AIC32X4_LOLGAIN AIC32X4_REG(1, 18) +#define AIC32X4_LORGAIN AIC32X4_REG(1, 19) +#define AIC32X4_HEADSTART AIC32X4_REG(1, 20) +#define AIC32X4_MICBIAS AIC32X4_REG(1, 51) +#define AIC32X4_LMICPGAPIN AIC32X4_REG(1, 52) +#define AIC32X4_LMICPGANIN AIC32X4_REG(1, 54) +#define AIC32X4_RMICPGAPIN AIC32X4_REG(1, 55) +#define AIC32X4_RMICPGANIN AIC32X4_REG(1, 57) +#define AIC32X4_FLOATINGINPUT AIC32X4_REG(1, 58) +#define AIC32X4_LMICPGAVOL AIC32X4_REG(1, 59) +#define AIC32X4_RMICPGAVOL AIC32X4_REG(1, 60) + +/* Bits, masks, and shifts */ + +/* AIC32X4_CLKMUX */ +#define AIC32X4_PLL_CLKIN_MASK GENMASK(3, 2) +#define AIC32X4_PLL_CLKIN_SHIFT (2) +#define AIC32X4_PLL_CLKIN_MCLK (0x00) +#define AIC32X4_PLL_CLKIN_BCKL (0x01) +#define AIC32X4_PLL_CLKIN_GPIO1 (0x02) +#define AIC32X4_PLL_CLKIN_DIN (0x03) +#define AIC32X4_CODEC_CLKIN_MASK GENMASK(1, 0) +#define AIC32X4_CODEC_CLKIN_SHIFT (0) +#define AIC32X4_CODEC_CLKIN_MCLK (0x00) +#define AIC32X4_CODEC_CLKIN_BCLK (0x01) +#define AIC32X4_CODEC_CLKIN_GPIO1 (0x02) +#define AIC32X4_CODEC_CLKIN_PLL (0x03) + +/* AIC32X4_PLLPR */ +#define AIC32X4_PLLEN BIT(7) +#define AIC32X4_PLL_P_MASK GENMASK(6, 4) +#define AIC32X4_PLL_P_SHIFT (4) +#define AIC32X4_PLL_R_MASK GENMASK(3, 0) + +/* AIC32X4_NDAC */ +#define AIC32X4_NDACEN BIT(7) +#define AIC32X4_NDAC_MASK GENMASK(6, 0) + +/* AIC32X4_MDAC */ +#define AIC32X4_MDACEN BIT(7) +#define AIC32X4_MDAC_MASK GENMASK(6, 0) + +/* AIC32X4_NADC */ +#define AIC32X4_NADCEN BIT(7) +#define AIC32X4_NADC_MASK GENMASK(6, 0) + +/* AIC32X4_MADC */ +#define AIC32X4_MADCEN BIT(7) +#define AIC32X4_MADC_MASK GENMASK(6, 0) + +/* AIC32X4_BCLKN */ +#define AIC32X4_BCLKEN BIT(7) +#define AIC32X4_BCLK_MASK GENMASK(6, 0) + +/* AIC32X4_IFACE1 */ +#define AIC32X4_IFACE1_DATATYPE_MASK GENMASK(7, 6) +#define AIC32X4_IFACE1_DATATYPE_SHIFT (6) +#define AIC32X4_I2S_MODE (0x00) +#define AIC32X4_DSP_MODE (0x01) +#define AIC32X4_RIGHT_JUSTIFIED_MODE (0x02) +#define AIC32X4_LEFT_JUSTIFIED_MODE (0x03) +#define AIC32X4_IFACE1_DATALEN_MASK GENMASK(5, 4) +#define AIC32X4_IFACE1_DATALEN_SHIFT (4) +#define AIC32X4_WORD_LEN_16BITS (0x00) +#define AIC32X4_WORD_LEN_20BITS (0x01) +#define AIC32X4_WORD_LEN_24BITS (0x02) +#define AIC32X4_WORD_LEN_32BITS (0x03) +#define AIC32X4_IFACE1_MASTER_MASK GENMASK(3, 2) +#define AIC32X4_BCLKMASTER BIT(2) +#define AIC32X4_WCLKMASTER BIT(3) + +/* AIC32X4_IFACE2 */ +#define AIC32X4_DATA_OFFSET_MASK GENMASK(7, 0) + +/* AIC32X4_IFACE3 */ +#define AIC32X4_BCLKINV_MASK BIT(3) +#define AIC32X4_BDIVCLK_MASK GENMASK(1, 0) +#define AIC32X4_BDIVCLK_SHIFT (0) +#define AIC32X4_DAC2BCLK (0x00) +#define AIC32X4_DACMOD2BCLK (0x01) +#define AIC32X4_ADC2BCLK (0x02) +#define AIC32X4_ADCMOD2BCLK (0x03) + +/* AIC32X4_DACSETUP */ +#define AIC32X4_DAC_CHAN_MASK GENMASK(5, 2) +#define AIC32X4_LDAC2RCHN BIT(5) +#define AIC32X4_LDAC2LCHN BIT(4) +#define AIC32X4_RDAC2LCHN BIT(3) +#define AIC32X4_RDAC2RCHN BIT(2) + +/* AIC32X4_DACMUTE */ +#define AIC32X4_MUTEON 0x0C + +/* AIC32X4_ADCSETUP */ +#define AIC32X4_LADC_EN BIT(7) +#define AIC32X4_RADC_EN BIT(6) + +/* AIC32X4_PWRCFG */ +#define AIC32X4_AVDDWEAKDISABLE BIT(3) + +/* AIC32X4_LDOCTL */ +#define AIC32X4_LDOCTLEN BIT(0) + +/* AIC32X4_CMMODE */ +#define AIC32X4_LDOIN_18_36 BIT(0) +#define AIC32X4_LDOIN2HP BIT(1) + +/* AIC32X4_MICBIAS */ +#define AIC32X4_MICBIAS_LDOIN BIT(3) #define AIC32X4_MICBIAS_2075V 0x60 +/* AIC32X4_LMICPGANIN */ #define AIC32X4_LMICPGANIN_IN2R_10K 0x10 #define AIC32X4_LMICPGANIN_CM1L_10K 0x40 + +/* AIC32X4_RMICPGANIN */ #define AIC32X4_RMICPGANIN_IN1L_10K 0x10 #define AIC32X4_RMICPGANIN_CM1R_10K 0x40 -#define AIC32X4_LMICPGAVOL_NOGAIN 0x80 -#define AIC32X4_RMICPGAVOL_NOGAIN 0x80 - -#define AIC32X4_BCLKMASTER 0x08 -#define AIC32X4_WCLKMASTER 0x04 -#define AIC32X4_PLLEN (0x01 << 7) -#define AIC32X4_NDACEN (0x01 << 7) -#define AIC32X4_MDACEN (0x01 << 7) -#define AIC32X4_NADCEN (0x01 << 7) -#define AIC32X4_MADCEN (0x01 << 7) -#define AIC32X4_BCLKEN (0x01 << 7) -#define AIC32X4_DACEN (0x03 << 6) -#define AIC32X4_RDAC2LCHN (0x02 << 2) -#define AIC32X4_LDAC2RCHN (0x02 << 4) -#define AIC32X4_LDAC2LCHN (0x01 << 4) -#define AIC32X4_RDAC2RCHN (0x01 << 2) -#define AIC32X4_DAC_CHAN_MASK 0x3c - -#define AIC32X4_SSTEP2WCLK 0x01 -#define AIC32X4_MUTEON 0x0C -#define AIC32X4_DACMOD2BCLK 0x01 - #endif /* _TLV320AIC32X4_H */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 06f92571eba4..b751cad545da 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1804,11 +1804,18 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, if (!ai3x_setup) return -ENOMEM; - ret = of_get_named_gpio(np, "gpio-reset", 0); - if (ret >= 0) + ret = of_get_named_gpio(np, "reset-gpios", 0); + if (ret >= 0) { aic3x->gpio_reset = ret; - else - aic3x->gpio_reset = -1; + } else { + ret = of_get_named_gpio(np, "gpio-reset", 0); + if (ret > 0) { + dev_warn(&i2c->dev, "Using deprecated property \"gpio-reset\", please update your DT"); + aic3x->gpio_reset = ret; + } else { + aic3x->gpio_reset = -1; + } + } if (of_property_read_u32_array(np, "ai3x-gpio-func", ai3x_setup->gpio_func, 2) >= 0) { diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 5b94a151539c..675f5b1b90a6 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -106,6 +106,7 @@ struct tlv320dac33_priv { int mode1_latency; /* latency caused by the i2c writes in * us */ u8 burst_bclkdiv; /* BCLK divider value in burst mode */ + u8 *reg_cache; unsigned int burst_rate; /* Interface speed in Burst modes */ int keep_bclk; /* Keep the BCLK continuously running @@ -121,7 +122,7 @@ struct tlv320dac33_priv { unsigned int uthr; enum dac33_state state; - void *control_data; + struct i2c_client *i2c; }; static const u8 dac33_reg[DAC33_CACHEREGNUM] = { @@ -173,7 +174,8 @@ static const u8 dac33_reg[DAC33_CACHEREGNUM] = { static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec, unsigned reg) { - u8 *cache = codec->reg_cache; + struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); + u8 *cache = dac33->reg_cache; if (reg >= DAC33_CACHEREGNUM) return 0; @@ -183,7 +185,8 @@ static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec, static inline void dac33_write_reg_cache(struct snd_soc_codec *codec, u8 reg, u8 value) { - u8 *cache = codec->reg_cache; + struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); + u8 *cache = dac33->reg_cache; if (reg >= DAC33_CACHEREGNUM) return; @@ -200,7 +203,7 @@ static int dac33_read(struct snd_soc_codec *codec, unsigned int reg, /* If powered off, return the cached value */ if (dac33->chip_power) { - val = i2c_smbus_read_byte_data(codec->control_data, value[0]); + val = i2c_smbus_read_byte_data(dac33->i2c, value[0]); if (val < 0) { dev_err(codec->dev, "Read failed (%d)\n", val); value[0] = dac33_read_reg_cache(codec, reg); @@ -233,7 +236,7 @@ static int dac33_write(struct snd_soc_codec *codec, unsigned int reg, dac33_write_reg_cache(codec, data[0], data[1]); if (dac33->chip_power) { - ret = codec->hw_write(codec->control_data, data, 2); + ret = i2c_master_send(dac33->i2c, data, 2); if (ret != 2) dev_err(codec->dev, "Write failed (%d)\n", ret); else @@ -243,19 +246,6 @@ static int dac33_write(struct snd_soc_codec *codec, unsigned int reg, return ret; } -static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - int ret; - - mutex_lock(&dac33->mutex); - ret = dac33_write(codec, reg, value); - mutex_unlock(&dac33->mutex); - - return ret; -} - #define DAC33_I2C_ADDR_AUTOINC 0x80 static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) @@ -280,7 +270,7 @@ static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg, if (dac33->chip_power) { /* We need to set autoincrement mode for 16 bit writes */ data[0] |= DAC33_I2C_ADDR_AUTOINC; - ret = codec->hw_write(codec->control_data, data, 3); + ret = i2c_master_send(dac33->i2c, data, 3); if (ret != 3) dev_err(codec->dev, "Write failed (%d)\n", ret); else @@ -1379,8 +1369,6 @@ static int dac33_soc_probe(struct snd_soc_codec *codec) struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); int ret = 0; - codec->control_data = dac33->control_data; - codec->hw_write = (hw_write_t) i2c_master_send; dac33->codec = codec; /* Read the tlv320dac33 ID registers */ @@ -1434,13 +1422,9 @@ static int dac33_soc_remove(struct snd_soc_codec *codec) } static const struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = { - .read = dac33_read_reg_cache, - .write = dac33_write_locked, .set_bias_level = dac33_set_bias_level, .idle_bias_off = true, - .reg_cache_size = ARRAY_SIZE(dac33_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = dac33_reg, + .probe = dac33_soc_probe, .remove = dac33_soc_remove, @@ -1499,7 +1483,14 @@ static int dac33_i2c_probe(struct i2c_client *client, if (dac33 == NULL) return -ENOMEM; - dac33->control_data = client; + dac33->reg_cache = devm_kmemdup(&client->dev, + dac33_reg, + ARRAY_SIZE(dac33_reg) * sizeof(u8), + GFP_KERNEL); + if (!dac33->reg_cache) + return -ENOMEM; + + dac33->i2c = client; mutex_init(&dac33->mutex); spin_lock_init(&dac33->lock); diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 738e04b09116..1271e7e1fc78 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -241,7 +241,7 @@ int ts3a227e_enable_jack_detect(struct snd_soc_component *component, { struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component); - snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c new file mode 100644 index 000000000000..e7661d0315e6 --- /dev/null +++ b/sound/soc/codecs/tscs42xx.c @@ -0,0 +1,1456 @@ +// SPDX-License-Identifier: GPL-2.0 +// tscs42xx.c -- TSCS42xx ALSA SoC Audio driver +// Copyright 2017 Tempo Semiconductor, Inc. +// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com> + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "tscs42xx.h" + +#define COEFF_SIZE 3 +#define BIQUAD_COEFF_COUNT 5 +#define BIQUAD_SIZE (COEFF_SIZE * BIQUAD_COEFF_COUNT) + +#define COEFF_RAM_MAX_ADDR 0xcd +#define COEFF_RAM_COEFF_COUNT (COEFF_RAM_MAX_ADDR + 1) +#define COEFF_RAM_SIZE (COEFF_SIZE * COEFF_RAM_COEFF_COUNT) + +struct tscs42xx { + + int bclk_ratio; + int samplerate; + unsigned int blrcm; + struct mutex audio_params_lock; + + u8 coeff_ram[COEFF_RAM_SIZE]; + bool coeff_ram_synced; + struct mutex coeff_ram_lock; + + struct mutex pll_lock; + + struct regmap *regmap; + + struct device *dev; +}; + +struct coeff_ram_ctl { + unsigned int addr; + struct soc_bytes_ext bytes_ext; +}; + +static bool tscs42xx_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_DACCRWRL: + case R_DACCRWRM: + case R_DACCRWRH: + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + case R_DACCRSTAT: + case R_DACCRADDR: + case R_PLLCTL0: + return true; + default: + return false; + }; +} + +static bool tscs42xx_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_DACCRWRL: + case R_DACCRWRM: + case R_DACCRWRH: + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + return true; + default: + return false; + }; +} + +static const struct regmap_config tscs42xx_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .volatile_reg = tscs42xx_volatile, + .precious_reg = tscs42xx_precious, + .max_register = R_DACMBCREL3H, + + .cache_type = REGCACHE_RBTREE, + .can_multi_write = true, +}; + +#define MAX_PLL_LOCK_20MS_WAITS 1 +static bool plls_locked(struct snd_soc_codec *codec) +{ + int ret; + int count = MAX_PLL_LOCK_20MS_WAITS; + + do { + ret = snd_soc_read(codec, R_PLLCTL0); + if (ret < 0) { + dev_err(codec->dev, + "Failed to read PLL lock status (%d)\n", ret); + return false; + } else if (ret > 0) { + return true; + } + msleep(20); + } while (count--); + + return false; +} + +static int sample_rate_to_pll_freq_out(int sample_rate) +{ + switch (sample_rate) { + case 11025: + case 22050: + case 44100: + case 88200: + return 112896000; + case 8000: + case 16000: + case 32000: + case 48000: + case 96000: + return 122880000; + default: + return -EINVAL; + } +} + +#define DACCRSTAT_MAX_TRYS 10 +static int write_coeff_ram(struct snd_soc_codec *codec, u8 *coeff_ram, + unsigned int addr, unsigned int coeff_cnt) +{ + struct tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + int cnt; + int trys; + int ret; + + for (cnt = 0; cnt < coeff_cnt; cnt++, addr++) { + + for (trys = 0; trys < DACCRSTAT_MAX_TRYS; trys++) { + ret = snd_soc_read(codec, R_DACCRSTAT); + if (ret < 0) { + dev_err(codec->dev, + "Failed to read stat (%d)\n", ret); + return ret; + } + if (!ret) + break; + } + + if (trys == DACCRSTAT_MAX_TRYS) { + ret = -EIO; + dev_err(codec->dev, + "dac coefficient write error (%d)\n", ret); + return ret; + } + + ret = regmap_write(tscs42xx->regmap, R_DACCRADDR, addr); + if (ret < 0) { + dev_err(codec->dev, + "Failed to write dac ram address (%d)\n", ret); + return ret; + } + + ret = regmap_bulk_write(tscs42xx->regmap, R_DACCRWRL, + &coeff_ram[addr * COEFF_SIZE], + COEFF_SIZE); + if (ret < 0) { + dev_err(codec->dev, + "Failed to write dac ram (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static int power_up_audio_plls(struct snd_soc_codec *codec) +{ + struct tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + int freq_out; + int ret; + unsigned int mask; + unsigned int val; + + freq_out = sample_rate_to_pll_freq_out(tscs42xx->samplerate); + switch (freq_out) { + case 122880000: /* 48k */ + mask = RM_PLLCTL1C_PDB_PLL1; + val = RV_PLLCTL1C_PDB_PLL1_ENABLE; + break; + case 112896000: /* 44.1k */ + mask = RM_PLLCTL1C_PDB_PLL2; + val = RV_PLLCTL1C_PDB_PLL2_ENABLE; + break; + default: + ret = -EINVAL; + dev_err(codec->dev, "Unrecognized PLL output freq (%d)\n", ret); + return ret; + } + + mutex_lock(&tscs42xx->pll_lock); + + ret = snd_soc_update_bits(codec, R_PLLCTL1C, mask, val); + if (ret < 0) { + dev_err(codec->dev, "Failed to turn PLL on (%d)\n", ret); + goto exit; + } + + if (!plls_locked(codec)) { + dev_err(codec->dev, "Failed to lock plls\n"); + ret = -ENOMSG; + goto exit; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->pll_lock); + + return ret; +} + +static int power_down_audio_plls(struct snd_soc_codec *codec) +{ + struct tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&tscs42xx->pll_lock); + + ret = snd_soc_update_bits(codec, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL1, + RV_PLLCTL1C_PDB_PLL1_DISABLE); + if (ret < 0) { + dev_err(codec->dev, "Failed to turn PLL off (%d)\n", ret); + goto exit; + } + ret = snd_soc_update_bits(codec, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL2, + RV_PLLCTL1C_PDB_PLL2_DISABLE); + if (ret < 0) { + dev_err(codec->dev, "Failed to turn PLL off (%d)\n", ret); + goto exit; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->pll_lock); + + return ret; +} + +static int coeff_ram_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + mutex_lock(&tscs42xx->coeff_ram_lock); + + memcpy(ucontrol->value.bytes.data, + &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); + + mutex_unlock(&tscs42xx->coeff_ram_lock); + + return 0; +} + +static int coeff_ram_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + unsigned int coeff_cnt = params->max / COEFF_SIZE; + int ret; + + mutex_lock(&tscs42xx->coeff_ram_lock); + + tscs42xx->coeff_ram_synced = false; + + memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + + mutex_lock(&tscs42xx->pll_lock); + + if (plls_locked(codec)) { + ret = write_coeff_ram(codec, tscs42xx->coeff_ram, + ctl->addr, coeff_cnt); + if (ret < 0) { + dev_err(codec->dev, + "Failed to flush coeff ram cache (%d)\n", ret); + goto exit; + } + tscs42xx->coeff_ram_synced = true; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->pll_lock); + + mutex_unlock(&tscs42xx->coeff_ram_lock); + + return ret; +} + +/* Input L Capture Route */ +static char const * const input_select_text[] = { + "Line 1", "Line 2", "Line 3", "D2S" +}; + +static const struct soc_enum left_input_select_enum = +SOC_ENUM_SINGLE(R_INSELL, FB_INSELL, ARRAY_SIZE(input_select_text), + input_select_text); + +static const struct snd_kcontrol_new left_input_select = +SOC_DAPM_ENUM("LEFT_INPUT_SELECT_ENUM", left_input_select_enum); + +/* Input R Capture Route */ +static const struct soc_enum right_input_select_enum = +SOC_ENUM_SINGLE(R_INSELR, FB_INSELR, ARRAY_SIZE(input_select_text), + input_select_text); + +static const struct snd_kcontrol_new right_input_select = +SOC_DAPM_ENUM("RIGHT_INPUT_SELECT_ENUM", right_input_select_enum); + +/* Input Channel Mapping */ +static char const * const ch_map_select_text[] = { + "Normal", "Left to Right", "Right to Left", "Swap" +}; + +static const struct soc_enum ch_map_select_enum = +SOC_ENUM_SINGLE(R_AIC2, FB_AIC2_ADCDSEL, ARRAY_SIZE(ch_map_select_text), + ch_map_select_text); + +static int dapm_vref_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + msleep(20); + return 0; +} + +static int dapm_micb_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + msleep(20); + return 0; +} + +static int pll_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); + int ret; + + if (SND_SOC_DAPM_EVENT_ON(event)) + ret = power_up_audio_plls(codec); + else + ret = power_down_audio_plls(codec); + + return ret; +} + +static int dac_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 tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&tscs42xx->coeff_ram_lock); + + if (tscs42xx->coeff_ram_synced == false) { + ret = write_coeff_ram(codec, tscs42xx->coeff_ram, 0x00, + COEFF_RAM_COEFF_COUNT); + if (ret < 0) + goto exit; + tscs42xx->coeff_ram_synced = true; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->coeff_ram_lock); + + return ret; +} + +static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { + /* Vref */ + SND_SOC_DAPM_SUPPLY_S("Vref", 1, R_PWRM2, FB_PWRM2_VREF, 0, + dapm_vref_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + + /* PLL */ + SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, pll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Headphone */ + SND_SOC_DAPM_DAC_E("DAC L", "HiFi Playback", R_PWRM2, FB_PWRM2_HPL, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC_E("DAC R", "HiFi Playback", R_PWRM2, FB_PWRM2_HPR, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + + /* Speaker */ + SND_SOC_DAPM_DAC_E("ClassD L", "HiFi Playback", + R_PWRM2, FB_PWRM2_SPKL, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC_E("ClassD R", "HiFi Playback", + R_PWRM2, FB_PWRM2_SPKR, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("Speaker L"), + SND_SOC_DAPM_OUTPUT("Speaker R"), + + /* Capture */ + SND_SOC_DAPM_PGA("Analog In PGA L", R_PWRM1, FB_PWRM1_PGAL, 0, NULL, 0), + SND_SOC_DAPM_PGA("Analog In PGA R", R_PWRM1, FB_PWRM1_PGAR, 0, NULL, 0), + SND_SOC_DAPM_PGA("Analog Boost L", R_PWRM1, FB_PWRM1_BSTL, 0, NULL, 0), + SND_SOC_DAPM_PGA("Analog Boost R", R_PWRM1, FB_PWRM1_BSTR, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC Mute", R_CNVRTR0, FB_CNVRTR0_HPOR, true, NULL, 0), + SND_SOC_DAPM_ADC("ADC L", "HiFi Capture", R_PWRM1, FB_PWRM1_ADCL, 0), + SND_SOC_DAPM_ADC("ADC R", "HiFi Capture", R_PWRM1, FB_PWRM1_ADCR, 0), + + /* Capture Input */ + SND_SOC_DAPM_MUX("Input L Capture Route", R_PWRM2, + FB_PWRM2_INSELL, 0, &left_input_select), + SND_SOC_DAPM_MUX("Input R Capture Route", R_PWRM2, + FB_PWRM2_INSELR, 0, &right_input_select), + + /* Digital Mic */ + SND_SOC_DAPM_SUPPLY_S("Digital Mic Enable", 2, R_DMICCTL, + FB_DMICCTL_DMICEN, 0, NULL, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + + /* Analog Mic */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias", 2, R_PWRM1, FB_PWRM1_MICB, + 0, dapm_micb_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + + /* Line In */ + SND_SOC_DAPM_INPUT("Line In 1 L"), + SND_SOC_DAPM_INPUT("Line In 1 R"), + SND_SOC_DAPM_INPUT("Line In 2 L"), + SND_SOC_DAPM_INPUT("Line In 2 R"), + SND_SOC_DAPM_INPUT("Line In 3 L"), + SND_SOC_DAPM_INPUT("Line In 3 R"), +}; + +static const struct snd_soc_dapm_route tscs42xx_intercon[] = { + {"DAC L", NULL, "PLL"}, + {"DAC R", NULL, "PLL"}, + {"DAC L", NULL, "Vref"}, + {"DAC R", NULL, "Vref"}, + {"Headphone L", NULL, "DAC L"}, + {"Headphone R", NULL, "DAC R"}, + + {"ClassD L", NULL, "PLL"}, + {"ClassD R", NULL, "PLL"}, + {"ClassD L", NULL, "Vref"}, + {"ClassD R", NULL, "Vref"}, + {"Speaker L", NULL, "ClassD L"}, + {"Speaker R", NULL, "ClassD R"}, + + {"Input L Capture Route", NULL, "Vref"}, + {"Input R Capture Route", NULL, "Vref"}, + + {"Mic Bias", NULL, "Vref"}, + + {"Input L Capture Route", "Line 1", "Line In 1 L"}, + {"Input R Capture Route", "Line 1", "Line In 1 R"}, + {"Input L Capture Route", "Line 2", "Line In 2 L"}, + {"Input R Capture Route", "Line 2", "Line In 2 R"}, + {"Input L Capture Route", "Line 3", "Line In 3 L"}, + {"Input R Capture Route", "Line 3", "Line In 3 R"}, + + {"Analog In PGA L", NULL, "Input L Capture Route"}, + {"Analog In PGA R", NULL, "Input R Capture Route"}, + {"Analog Boost L", NULL, "Analog In PGA L"}, + {"Analog Boost R", NULL, "Analog In PGA R"}, + {"ADC Mute", NULL, "Analog Boost L"}, + {"ADC Mute", NULL, "Analog Boost R"}, + {"ADC L", NULL, "PLL"}, + {"ADC R", NULL, "PLL"}, + {"ADC L", NULL, "ADC Mute"}, + {"ADC R", NULL, "ADC Mute"}, +}; + +/************ + * CONTROLS * + ************/ + +static char const * const eq_band_enable_text[] = { + "Prescale only", + "Band1", + "Band1:2", + "Band1:3", + "Band1:4", + "Band1:5", + "Band1:6", +}; + +static char const * const level_detection_text[] = { + "Average", + "Peak", +}; + +static char const * const level_detection_window_text[] = { + "512 Samples", + "64 Samples", +}; + +static char const * const compressor_ratio_text[] = { + "Reserved", "1.5:1", "2:1", "3:1", "4:1", "5:1", "6:1", + "7:1", "8:1", "9:1", "10:1", "11:1", "12:1", "13:1", "14:1", + "15:1", "16:1", "17:1", "18:1", "19:1", "20:1", +}; + +static DECLARE_TLV_DB_SCALE(hpvol_scale, -8850, 75, 0); +static DECLARE_TLV_DB_SCALE(spkvol_scale, -7725, 75, 0); +static DECLARE_TLV_DB_SCALE(dacvol_scale, -9563, 38, 0); +static DECLARE_TLV_DB_SCALE(adcvol_scale, -7125, 38, 0); +static DECLARE_TLV_DB_SCALE(invol_scale, -1725, 75, 0); +static DECLARE_TLV_DB_SCALE(mic_boost_scale, 0, 1000, 0); +static DECLARE_TLV_DB_MINMAX(mugain_scale, 0, 4650); +static DECLARE_TLV_DB_MINMAX(compth_scale, -9562, 0); + +static const struct soc_enum eq1_band_enable_enum = + SOC_ENUM_SINGLE(R_CONFIG1, FB_CONFIG1_EQ1_BE, + ARRAY_SIZE(eq_band_enable_text), eq_band_enable_text); + +static const struct soc_enum eq2_band_enable_enum = + SOC_ENUM_SINGLE(R_CONFIG1, FB_CONFIG1_EQ2_BE, + ARRAY_SIZE(eq_band_enable_text), eq_band_enable_text); + +static const struct soc_enum cle_level_detection_enum = + SOC_ENUM_SINGLE(R_CLECTL, FB_CLECTL_LVL_MODE, + ARRAY_SIZE(level_detection_text), + level_detection_text); + +static const struct soc_enum cle_level_detection_window_enum = + SOC_ENUM_SINGLE(R_CLECTL, FB_CLECTL_WINDOWSEL, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text); + +static const struct soc_enum mbc_level_detection_enums[] = { + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE1, + ARRAY_SIZE(level_detection_text), + level_detection_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE2, + ARRAY_SIZE(level_detection_text), + level_detection_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE3, + ARRAY_SIZE(level_detection_text), + level_detection_text), +}; + +static const struct soc_enum mbc_level_detection_window_enums[] = { + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL1, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL2, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL3, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text), +}; + +static const struct soc_enum compressor_ratio_enum = + SOC_ENUM_SINGLE(R_CMPRAT, FB_CMPRAT, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static const struct soc_enum dac_mbc1_compressor_ratio_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT1, FB_DACMBCRAT1_RATIO, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static const struct soc_enum dac_mbc2_compressor_ratio_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT2, FB_DACMBCRAT2_RATIO, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static const struct soc_enum dac_mbc3_compressor_ratio_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT3, FB_DACMBCRAT3_RATIO, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static int bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +#define COEFF_RAM_CTL(xname, xcount, xaddr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = bytes_info_ext, \ + .get = coeff_ram_get, .put = coeff_ram_put, \ + .private_value = (unsigned long)&(struct coeff_ram_ctl) { \ + .addr = xaddr, \ + .bytes_ext = {.max = xcount, }, \ + } \ +} + +static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { + /* Volumes */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", R_HPVOLL, R_HPVOLR, + FB_HPVOLL, 0x7F, 0, hpvol_scale), + SOC_DOUBLE_R_TLV("Speaker Playback Volume", R_SPKVOLL, R_SPKVOLR, + FB_SPKVOLL, 0x7F, 0, spkvol_scale), + SOC_DOUBLE_R_TLV("Master Playback Volume", R_DACVOLL, R_DACVOLR, + FB_DACVOLL, 0xFF, 0, dacvol_scale), + SOC_DOUBLE_R_TLV("PCM Capture Volume", R_ADCVOLL, R_ADCVOLR, + FB_ADCVOLL, 0xFF, 0, adcvol_scale), + SOC_DOUBLE_R_TLV("Master Capture Volume", R_INVOLL, R_INVOLR, + FB_INVOLL, 0x3F, 0, invol_scale), + + /* INSEL */ + SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", R_INSELL, R_INSELR, + FB_INSELL_MICBSTL, FV_INSELL_MICBSTL_30DB, + 0, mic_boost_scale), + + /* Input Channel Map */ + SOC_ENUM("Input Channel Map", ch_map_select_enum), + + /* Coefficient Ram */ + COEFF_RAM_CTL("Cascade1L BiQuad1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("Cascade1L BiQuad2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("Cascade1L BiQuad3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("Cascade1L BiQuad4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("Cascade1L BiQuad5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("Cascade1L BiQuad6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("Cascade1R BiQuad1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("Cascade1R BiQuad2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("Cascade1R BiQuad3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("Cascade1R BiQuad4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("Cascade1R BiQuad5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("Cascade1R BiQuad6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("Cascade1L Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("Cascade1R Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("Cascade2L BiQuad1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("Cascade2L BiQuad2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("Cascade2L BiQuad3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("Cascade2L BiQuad4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("Cascade2L BiQuad5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("Cascade2L BiQuad6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("Cascade2R BiQuad1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("Cascade2R BiQuad2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("Cascade2R BiQuad3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("Cascade2R BiQuad4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("Cascade2R BiQuad5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("Cascade2R BiQuad6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("Cascade2L Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("Cascade2R Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("Bass Extraction BiQuad1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("Bass Extraction BiQuad2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("Treb Extraction BiQuad1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("Treb Extraction BiQuad2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("MBC1 BiQuad1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("MBC1 BiQuad2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("MBC2 BiQuad1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("MBC2 BiQuad2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("MBC3 BiQuad1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("MBC3 BiQuad2", BIQUAD_SIZE, 0xc9), + + /* EQ */ + SOC_SINGLE("EQ1 Switch", R_CONFIG1, FB_CONFIG1_EQ1_EN, 1, 0), + SOC_SINGLE("EQ2 Switch", R_CONFIG1, FB_CONFIG1_EQ2_EN, 1, 0), + SOC_ENUM("EQ1 Band Enable", eq1_band_enable_enum), + SOC_ENUM("EQ2 Band Enable", eq2_band_enable_enum), + + /* CLE */ + SOC_ENUM("CLE Level Detect", + cle_level_detection_enum), + SOC_ENUM("CLE Level Detect Win", + cle_level_detection_window_enum), + SOC_SINGLE("Expander Switch", + R_CLECTL, FB_CLECTL_EXP_EN, 1, 0), + SOC_SINGLE("Limiter Switch", + R_CLECTL, FB_CLECTL_LIMIT_EN, 1, 0), + SOC_SINGLE("Comp Switch", + R_CLECTL, FB_CLECTL_COMP_EN, 1, 0), + SOC_SINGLE_TLV("CLE Make-Up Gain Playback Volume", + R_MUGAIN, FB_MUGAIN_CLEMUG, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("Comp Thresh Playback Volume", + R_COMPTH, FB_COMPTH, 0xff, 0, compth_scale), + SOC_ENUM("Comp Ratio", compressor_ratio_enum), + SND_SOC_BYTES("Comp Atk Time", R_CATKTCL, 2), + + /* Effects */ + SOC_SINGLE("3D Switch", R_FXCTL, FB_FXCTL_3DEN, 1, 0), + SOC_SINGLE("Treble Switch", R_FXCTL, FB_FXCTL_TEEN, 1, 0), + SOC_SINGLE("Treble Bypass Switch", R_FXCTL, FB_FXCTL_TNLFBYPASS, 1, 0), + SOC_SINGLE("Bass Switch", R_FXCTL, FB_FXCTL_BEEN, 1, 0), + SOC_SINGLE("Bass Bypass Switch", R_FXCTL, FB_FXCTL_BNLFBYPASS, 1, 0), + + /* MBC */ + SOC_SINGLE("MBC Band1 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN1, 1, 0), + SOC_SINGLE("MBC Band2 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("MBC Band3 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN3, 1, 0), + SOC_ENUM("MBC Band1 Level Detect", + mbc_level_detection_enums[0]), + SOC_ENUM("MBC Band2 Level Detect", + mbc_level_detection_enums[1]), + SOC_ENUM("MBC Band3 Level Detect", + mbc_level_detection_enums[2]), + SOC_ENUM("MBC Band1 Level Detect Win", + mbc_level_detection_window_enums[0]), + SOC_ENUM("MBC Band2 Level Detect Win", + mbc_level_detection_window_enums[1]), + SOC_ENUM("MBC Band3 Level Detect Win", + mbc_level_detection_window_enums[2]), + + SOC_SINGLE("MBC1 Phase Invert Switch", + R_DACMBCMUG1, FB_DACMBCMUG1_PHASE, 1, 0), + SOC_SINGLE_TLV("DAC MBC1 Make-Up Gain Playback Volume", + R_DACMBCMUG1, FB_DACMBCMUG1_MUGAIN, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("DAC MBC1 Comp Thresh Playback Volume", + R_DACMBCTHR1, FB_DACMBCTHR1_THRESH, 0xff, 0, compth_scale), + SOC_ENUM("DAC MBC1 Comp Ratio", + dac_mbc1_compressor_ratio_enum), + SND_SOC_BYTES("DAC MBC1 Comp Atk Time", R_DACMBCATK1L, 2), + SND_SOC_BYTES("DAC MBC1 Comp Rel Time Const", + R_DACMBCREL1L, 2), + + SOC_SINGLE("MBC2 Phase Invert Switch", + R_DACMBCMUG2, FB_DACMBCMUG2_PHASE, 1, 0), + SOC_SINGLE_TLV("DAC MBC2 Make-Up Gain Playback Volume", + R_DACMBCMUG2, FB_DACMBCMUG2_MUGAIN, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("DAC MBC2 Comp Thresh Playback Volume", + R_DACMBCTHR2, FB_DACMBCTHR2_THRESH, 0xff, 0, compth_scale), + SOC_ENUM("DAC MBC2 Comp Ratio", + dac_mbc2_compressor_ratio_enum), + SND_SOC_BYTES("DAC MBC2 Comp Atk Time", R_DACMBCATK2L, 2), + SND_SOC_BYTES("DAC MBC2 Comp Rel Time Const", + R_DACMBCREL2L, 2), + + SOC_SINGLE("MBC3 Phase Invert Switch", + R_DACMBCMUG3, FB_DACMBCMUG3_PHASE, 1, 0), + SOC_SINGLE_TLV("DAC MBC3 Make-Up Gain Playback Volume", + R_DACMBCMUG3, FB_DACMBCMUG3_MUGAIN, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("DAC MBC3 Comp Thresh Playback Volume", + R_DACMBCTHR3, FB_DACMBCTHR3_THRESH, 0xff, 0, compth_scale), + SOC_ENUM("DAC MBC3 Comp Ratio", + dac_mbc3_compressor_ratio_enum), + SND_SOC_BYTES("DAC MBC3 Comp Atk Time", R_DACMBCATK3L, 2), + SND_SOC_BYTES("DAC MBC3 Comp Rel Time Const", + R_DACMBCREL3L, 2), +}; + +static int setup_sample_format(struct snd_soc_codec *codec, + snd_pcm_format_t format) +{ + unsigned int width; + int ret; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + width = RV_AIC1_WL_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + width = RV_AIC1_WL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + width = RV_AIC1_WL_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + width = RV_AIC1_WL_32; + break; + default: + ret = -EINVAL; + dev_err(codec->dev, "Unsupported format width (%d)\n", ret); + return ret; + } + ret = snd_soc_update_bits(codec, R_AIC1, RM_AIC1_WL, width); + if (ret < 0) { + dev_err(codec->dev, "Failed to set sample width (%d)\n", ret); + return ret; + } + + return 0; +} + +static int setup_sample_rate(struct snd_soc_codec *codec, unsigned int rate) +{ + struct tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + unsigned int br, bm; + int ret; + + switch (rate) { + case 8000: + br = RV_DACSR_DBR_32; + bm = RV_DACSR_DBM_PT25; + break; + case 16000: + br = RV_DACSR_DBR_32; + bm = RV_DACSR_DBM_PT5; + break; + case 24000: + br = RV_DACSR_DBR_48; + bm = RV_DACSR_DBM_PT5; + break; + case 32000: + br = RV_DACSR_DBR_32; + bm = RV_DACSR_DBM_1; + break; + case 48000: + br = RV_DACSR_DBR_48; + bm = RV_DACSR_DBM_1; + break; + case 96000: + br = RV_DACSR_DBR_48; + bm = RV_DACSR_DBM_2; + break; + case 11025: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_PT25; + break; + case 22050: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_PT5; + break; + case 44100: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_1; + break; + case 88200: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_2; + break; + default: + dev_err(codec->dev, "Unsupported sample rate %d\n", rate); + return -EINVAL; + } + + /* DAC and ADC share bit and frame clock */ + ret = snd_soc_update_bits(codec, R_DACSR, RM_DACSR_DBR, br); + if (ret < 0) { + dev_err(codec->dev, "Failed to update register (%d)\n", ret); + return ret; + } + ret = snd_soc_update_bits(codec, R_DACSR, RM_DACSR_DBM, bm); + if (ret < 0) { + dev_err(codec->dev, "Failed to update register (%d)\n", ret); + return ret; + } + ret = snd_soc_update_bits(codec, R_ADCSR, RM_DACSR_DBR, br); + if (ret < 0) { + dev_err(codec->dev, "Failed to update register (%d)\n", ret); + return ret; + } + ret = snd_soc_update_bits(codec, R_ADCSR, RM_DACSR_DBM, bm); + if (ret < 0) { + dev_err(codec->dev, "Failed to update register (%d)\n", ret); + return ret; + } + + mutex_lock(&tscs42xx->audio_params_lock); + + tscs42xx->samplerate = rate; + + mutex_unlock(&tscs42xx->audio_params_lock); + + return 0; +} + +struct reg_setting { + unsigned int addr; + unsigned int val; + unsigned int mask; +}; + +#define PLL_REG_SETTINGS_COUNT 13 +struct pll_ctl { + int input_freq; + struct reg_setting settings[PLL_REG_SETTINGS_COUNT]; +}; + +#define PLL_CTL(f, rt, rd, r1b_l, r9, ra, rb, \ + rc, r12, r1b_h, re, rf, r10, r11) \ + { \ + .input_freq = f, \ + .settings = { \ + {R_TIMEBASE, rt, 0xFF}, \ + {R_PLLCTLD, rd, 0xFF}, \ + {R_PLLCTL1B, r1b_l, 0x0F}, \ + {R_PLLCTL9, r9, 0xFF}, \ + {R_PLLCTLA, ra, 0xFF}, \ + {R_PLLCTLB, rb, 0xFF}, \ + {R_PLLCTLC, rc, 0xFF}, \ + {R_PLLCTL12, r12, 0xFF}, \ + {R_PLLCTL1B, r1b_h, 0xF0}, \ + {R_PLLCTLE, re, 0xFF}, \ + {R_PLLCTLF, rf, 0xFF}, \ + {R_PLLCTL10, r10, 0xFF}, \ + {R_PLLCTL11, r11, 0xFF}, \ + }, \ + } + +static const struct pll_ctl pll_ctls[] = { + PLL_CTL(1411200, 0x05, + 0x39, 0x04, 0x07, 0x02, 0xC3, 0x04, + 0x1B, 0x10, 0x03, 0x03, 0xD0, 0x02), + PLL_CTL(1536000, 0x05, + 0x1A, 0x04, 0x02, 0x03, 0xE0, 0x01, + 0x1A, 0x10, 0x02, 0x03, 0xB9, 0x01), + PLL_CTL(2822400, 0x0A, + 0x23, 0x04, 0x07, 0x04, 0xC3, 0x04, + 0x22, 0x10, 0x05, 0x03, 0x58, 0x02), + PLL_CTL(3072000, 0x0B, + 0x22, 0x04, 0x07, 0x03, 0x48, 0x03, + 0x1A, 0x10, 0x04, 0x03, 0xB9, 0x01), + PLL_CTL(5644800, 0x15, + 0x23, 0x04, 0x0E, 0x04, 0xC3, 0x04, + 0x1A, 0x10, 0x08, 0x03, 0xE0, 0x01), + PLL_CTL(6144000, 0x17, + 0x1A, 0x04, 0x08, 0x03, 0xE0, 0x01, + 0x1A, 0x10, 0x08, 0x03, 0xB9, 0x01), + PLL_CTL(12000000, 0x2E, + 0x1B, 0x04, 0x19, 0x03, 0x00, 0x03, + 0x2A, 0x10, 0x19, 0x05, 0x98, 0x04), + PLL_CTL(19200000, 0x4A, + 0x13, 0x04, 0x14, 0x03, 0x80, 0x01, + 0x1A, 0x10, 0x19, 0x03, 0xB9, 0x01), + PLL_CTL(22000000, 0x55, + 0x2A, 0x04, 0x37, 0x05, 0x00, 0x06, + 0x22, 0x10, 0x26, 0x03, 0x49, 0x02), + PLL_CTL(22579200, 0x57, + 0x22, 0x04, 0x31, 0x03, 0x20, 0x03, + 0x1A, 0x10, 0x1D, 0x03, 0xB3, 0x01), + PLL_CTL(24000000, 0x5D, + 0x13, 0x04, 0x19, 0x03, 0x80, 0x01, + 0x1B, 0x10, 0x19, 0x05, 0x4C, 0x02), + PLL_CTL(24576000, 0x5F, + 0x13, 0x04, 0x1D, 0x03, 0xB3, 0x01, + 0x22, 0x10, 0x40, 0x03, 0x72, 0x03), + PLL_CTL(27000000, 0x68, + 0x22, 0x04, 0x4B, 0x03, 0x00, 0x04, + 0x2A, 0x10, 0x7D, 0x03, 0x20, 0x06), + PLL_CTL(36000000, 0x8C, + 0x1B, 0x04, 0x4B, 0x03, 0x00, 0x03, + 0x2A, 0x10, 0x7D, 0x03, 0x98, 0x04), + PLL_CTL(25000000, 0x61, + 0x1B, 0x04, 0x37, 0x03, 0x2B, 0x03, + 0x1A, 0x10, 0x2A, 0x03, 0x39, 0x02), + PLL_CTL(26000000, 0x65, + 0x23, 0x04, 0x41, 0x05, 0x00, 0x06, + 0x1A, 0x10, 0x26, 0x03, 0xEF, 0x01), + PLL_CTL(12288000, 0x2F, + 0x1A, 0x04, 0x12, 0x03, 0x1C, 0x02, + 0x22, 0x10, 0x20, 0x03, 0x72, 0x03), + PLL_CTL(40000000, 0x9B, + 0x22, 0x08, 0x7D, 0x03, 0x80, 0x04, + 0x23, 0x10, 0x7D, 0x05, 0xE4, 0x06), + PLL_CTL(512000, 0x01, + 0x22, 0x04, 0x01, 0x03, 0xD0, 0x02, + 0x1B, 0x10, 0x01, 0x04, 0x72, 0x03), + PLL_CTL(705600, 0x02, + 0x22, 0x04, 0x02, 0x03, 0x15, 0x04, + 0x22, 0x10, 0x01, 0x04, 0x80, 0x02), + PLL_CTL(1024000, 0x03, + 0x22, 0x04, 0x02, 0x03, 0xD0, 0x02, + 0x1B, 0x10, 0x02, 0x04, 0x72, 0x03), + PLL_CTL(2048000, 0x07, + 0x22, 0x04, 0x04, 0x03, 0xD0, 0x02, + 0x1B, 0x10, 0x04, 0x04, 0x72, 0x03), + PLL_CTL(2400000, 0x08, + 0x22, 0x04, 0x05, 0x03, 0x00, 0x03, + 0x23, 0x10, 0x05, 0x05, 0x98, 0x04), +}; + +static const struct pll_ctl *get_pll_ctl(int input_freq) +{ + int i; + const struct pll_ctl *pll_ctl = NULL; + + for (i = 0; i < ARRAY_SIZE(pll_ctls); ++i) + if (input_freq == pll_ctls[i].input_freq) { + pll_ctl = &pll_ctls[i]; + break; + } + + return pll_ctl; +} + +static int set_pll_ctl_from_input_freq(struct snd_soc_codec *codec, + const int input_freq) +{ + int ret; + int i; + const struct pll_ctl *pll_ctl; + + pll_ctl = get_pll_ctl(input_freq); + if (!pll_ctl) { + ret = -EINVAL; + dev_err(codec->dev, "No PLL input entry for %d (%d)\n", + input_freq, ret); + return ret; + } + + for (i = 0; i < PLL_REG_SETTINGS_COUNT; ++i) { + ret = snd_soc_update_bits(codec, + pll_ctl->settings[i].addr, + pll_ctl->settings[i].mask, + pll_ctl->settings[i].val); + if (ret < 0) { + dev_err(codec->dev, "Failed to set pll ctl (%d)\n", + ret); + return ret; + } + } + + return 0; +} + +static int tscs42xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int ret; + + ret = setup_sample_format(codec, params_format(params)); + if (ret < 0) { + dev_err(codec->dev, "Failed to setup sample format (%d)\n", + ret); + return ret; + } + + ret = setup_sample_rate(codec, params_rate(params)); + if (ret < 0) { + dev_err(codec->dev, "Failed to setup sample rate (%d)\n", ret); + return ret; + } + + return 0; +} + +static inline int dac_mute(struct snd_soc_codec *codec) +{ + int ret; + + ret = snd_soc_update_bits(codec, R_CNVRTR1, RM_CNVRTR1_DACMU, + RV_CNVRTR1_DACMU_ENABLE); + if (ret < 0) { + dev_err(codec->dev, "Failed to mute DAC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int dac_unmute(struct snd_soc_codec *codec) +{ + int ret; + + ret = snd_soc_update_bits(codec, R_CNVRTR1, RM_CNVRTR1_DACMU, + RV_CNVRTR1_DACMU_DISABLE); + if (ret < 0) { + dev_err(codec->dev, "Failed to unmute DAC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int adc_mute(struct snd_soc_codec *codec) +{ + int ret; + + ret = snd_soc_update_bits(codec, R_CNVRTR0, RM_CNVRTR0_ADCMU, + RV_CNVRTR0_ADCMU_ENABLE); + if (ret < 0) { + dev_err(codec->dev, "Failed to mute ADC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int adc_unmute(struct snd_soc_codec *codec) +{ + int ret; + + ret = snd_soc_update_bits(codec, R_CNVRTR0, RM_CNVRTR0_ADCMU, + RV_CNVRTR0_ADCMU_DISABLE); + if (ret < 0) { + dev_err(codec->dev, "Failed to unmute ADC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int tscs42xx_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + int ret; + + if (mute) + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = dac_mute(codec); + else + ret = adc_mute(codec); + else + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = dac_unmute(codec); + else + ret = adc_unmute(codec); + + return ret; +} + +static int tscs42xx_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int ret; + + /* Slave mode not supported since it needs always-on frame clock */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ret = snd_soc_update_bits(codec, R_AIC1, RM_AIC1_MS, + RV_AIC1_MS_MASTER); + if (ret < 0) { + dev_err(codec->dev, + "Failed to set codec DAI master (%d)\n", ret); + return ret; + } + break; + default: + ret = -EINVAL; + dev_err(codec->dev, "Unsupported format (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, + unsigned int ratio) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct tscs42xx *tscs42xx = snd_soc_codec_get_drvdata(codec); + unsigned int value; + int ret = 0; + + switch (ratio) { + case 32: + value = RV_DACSR_DBCM_32; + break; + case 40: + value = RV_DACSR_DBCM_40; + break; + case 64: + value = RV_DACSR_DBCM_64; + break; + default: + dev_err(codec->dev, "Unsupported bclk ratio (%d)\n", ret); + return -EINVAL; + } + + ret = snd_soc_update_bits(codec, R_DACSR, RM_DACSR_DBCM, value); + if (ret < 0) { + dev_err(codec->dev, "Failed to set DAC BCLK ratio (%d)\n", ret); + return ret; + } + ret = snd_soc_update_bits(codec, R_ADCSR, RM_ADCSR_ABCM, value); + if (ret < 0) { + dev_err(codec->dev, "Failed to set ADC BCLK ratio (%d)\n", ret); + return ret; + } + + mutex_lock(&tscs42xx->audio_params_lock); + + tscs42xx->bclk_ratio = ratio; + + mutex_unlock(&tscs42xx->audio_params_lock); + + return 0; +} + +static int tscs42xx_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; + int ret; + + switch (clk_id) { + case TSCS42XX_PLL_SRC_XTAL: + case TSCS42XX_PLL_SRC_MCLK1: + ret = snd_soc_write(codec, R_PLLREFSEL, + RV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 | + RV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1); + if (ret < 0) { + dev_err(codec->dev, + "Failed to set pll reference input (%d)\n", + ret); + return ret; + } + break; + case TSCS42XX_PLL_SRC_MCLK2: + ret = snd_soc_write(codec, R_PLLREFSEL, + RV_PLLREFSEL_PLL1_REF_SEL_MCLK2 | + RV_PLLREFSEL_PLL2_REF_SEL_MCLK2); + if (ret < 0) { + dev_err(codec->dev, + "Failed to set PLL reference (%d)\n", ret); + return ret; + } + break; + default: + dev_err(codec->dev, "pll src is unsupported\n"); + return -EINVAL; + } + + ret = set_pll_ctl_from_input_freq(codec, freq); + if (ret < 0) { + dev_err(codec->dev, + "Failed to setup PLL input freq (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct snd_soc_dai_ops tscs42xx_dai_ops = { + .hw_params = tscs42xx_hw_params, + .mute_stream = tscs42xx_mute_stream, + .set_fmt = tscs42xx_set_dai_fmt, + .set_bclk_ratio = tscs42xx_set_dai_bclk_ratio, + .set_sysclk = tscs42xx_set_dai_sysclk, +}; + +static int part_is_valid(struct tscs42xx *tscs42xx) +{ + int val; + int ret; + unsigned int reg; + + ret = regmap_read(tscs42xx->regmap, R_DEVIDH, ®); + if (ret < 0) + return ret; + + val = reg << 8; + ret = regmap_read(tscs42xx->regmap, R_DEVIDL, ®); + if (ret < 0) + return ret; + + val |= reg; + + switch (val) { + case 0x4A74: + case 0x4A73: + return true; + default: + return false; + }; +} + +static struct snd_soc_codec_driver soc_codec_dev_tscs42xx = { + .component_driver = { + .dapm_widgets = tscs42xx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tscs42xx_dapm_widgets), + .dapm_routes = tscs42xx_intercon, + .num_dapm_routes = ARRAY_SIZE(tscs42xx_intercon), + .controls = tscs42xx_snd_controls, + .num_controls = ARRAY_SIZE(tscs42xx_snd_controls), + }, +}; + +static inline void init_coeff_ram_cache(struct tscs42xx *tscs42xx) +{ + const u8 norm_addrs[] = { 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, 0x1f, + 0x20, 0x25, 0x2a, 0x2f, 0x34, 0x39, 0x3f, 0x40, 0x45, 0x4a, + 0x4f, 0x54, 0x59, 0x5f, 0x60, 0x65, 0x6a, 0x6f, 0x74, 0x79, + 0x7f, 0x80, 0x85, 0x8c, 0x91, 0x96, 0x97, 0x9c, 0xa3, 0xa8, + 0xad, 0xaf, 0xb0, 0xb5, 0xba, 0xbf, 0xc4, 0xc9, }; + u8 *coeff_ram = tscs42xx->coeff_ram; + int i; + + for (i = 0; i < ARRAY_SIZE(norm_addrs); i++) + coeff_ram[((norm_addrs[i] + 1) * COEFF_SIZE) - 1] = 0x40; +} + +#define TSCS42XX_RATES SNDRV_PCM_RATE_8000_96000 + +#define TSCS42XX_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 tscs42xx_dai = { + .name = "tscs42xx-HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = TSCS42XX_RATES, + .formats = TSCS42XX_FORMATS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 2, + .channels_max = 2, + .rates = TSCS42XX_RATES, + .formats = TSCS42XX_FORMATS,}, + .ops = &tscs42xx_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, +}; + +static const struct reg_sequence tscs42xx_patch[] = { + { R_AIC2, RV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED }, +}; + +static int tscs42xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tscs42xx *tscs42xx; + int ret = 0; + + tscs42xx = devm_kzalloc(&i2c->dev, sizeof(*tscs42xx), GFP_KERNEL); + if (!tscs42xx) { + ret = -ENOMEM; + dev_err(&i2c->dev, + "Failed to allocate memory for data (%d)\n", ret); + return ret; + } + i2c_set_clientdata(i2c, tscs42xx); + tscs42xx->dev = &i2c->dev; + + tscs42xx->regmap = devm_regmap_init_i2c(i2c, &tscs42xx_regmap); + if (IS_ERR(tscs42xx->regmap)) { + ret = PTR_ERR(tscs42xx->regmap); + dev_err(tscs42xx->dev, "Failed to allocate regmap (%d)\n", ret); + return ret; + } + + init_coeff_ram_cache(tscs42xx); + + ret = part_is_valid(tscs42xx); + if (ret <= 0) { + dev_err(tscs42xx->dev, "No valid part (%d)\n", ret); + ret = -ENODEV; + return ret; + } + + ret = regmap_write(tscs42xx->regmap, R_RESET, RV_RESET_ENABLE); + if (ret < 0) { + dev_err(tscs42xx->dev, "Failed to reset device (%d)\n", ret); + return ret; + } + + ret = regmap_register_patch(tscs42xx->regmap, tscs42xx_patch, + ARRAY_SIZE(tscs42xx_patch)); + if (ret < 0) { + dev_err(tscs42xx->dev, "Failed to apply patch (%d)\n", ret); + return ret; + } + + mutex_init(&tscs42xx->audio_params_lock); + mutex_init(&tscs42xx->coeff_ram_lock); + mutex_init(&tscs42xx->pll_lock); + + ret = snd_soc_register_codec(tscs42xx->dev, &soc_codec_dev_tscs42xx, + &tscs42xx_dai, 1); + if (ret) { + dev_err(tscs42xx->dev, "Failed to register codec (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs42xx_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + + return 0; +} + +static const struct i2c_device_id tscs42xx_i2c_id[] = { + { "tscs42A1", 0 }, + { "tscs42A2", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tscs42xx_i2c_id); + +static const struct of_device_id tscs42xx_of_match[] = { + { .compatible = "tempo,tscs42A1", }, + { .compatible = "tempo,tscs42A2", }, + { } +}; +MODULE_DEVICE_TABLE(of, tscs42xx_of_match); + +static struct i2c_driver tscs42xx_i2c_driver = { + .driver = { + .name = "tscs42xx", + .owner = THIS_MODULE, + .of_match_table = tscs42xx_of_match, + }, + .probe = tscs42xx_i2c_probe, + .remove = tscs42xx_i2c_remove, + .id_table = tscs42xx_i2c_id, +}; + +module_i2c_driver(tscs42xx_i2c_driver); + +MODULE_AUTHOR("Tempo Semiconductor <steven.eckhoff.opensource@gmail.com"); +MODULE_DESCRIPTION("ASoC TSCS42xx driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tscs42xx.h b/sound/soc/codecs/tscs42xx.h new file mode 100644 index 000000000000..d4a30bcbf64b --- /dev/null +++ b/sound/soc/codecs/tscs42xx.h @@ -0,0 +1,2693 @@ +// SPDX-License-Identifier: GPL-2.0 +// tscs42xx.h -- TSCS42xx ALSA SoC Audio driver +// Copyright 2017 Tempo Semiconductor, Inc. +// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com> + +#ifndef __WOOKIE_H__ +#define __WOOKIE_H__ + +enum { + TSCS42XX_PLL_SRC_NONE, + TSCS42XX_PLL_SRC_XTAL, + TSCS42XX_PLL_SRC_MCLK1, + TSCS42XX_PLL_SRC_MCLK2, +}; + +#define R_HPVOLL 0x0 +#define R_HPVOLR 0x1 +#define R_SPKVOLL 0x2 +#define R_SPKVOLR 0x3 +#define R_DACVOLL 0x4 +#define R_DACVOLR 0x5 +#define R_ADCVOLL 0x6 +#define R_ADCVOLR 0x7 +#define R_INVOLL 0x8 +#define R_INVOLR 0x9 +#define R_INMODE 0x0B +#define R_INSELL 0x0C +#define R_INSELR 0x0D +#define R_AIC1 0x13 +#define R_AIC2 0x14 +#define R_CNVRTR0 0x16 +#define R_ADCSR 0x17 +#define R_CNVRTR1 0x18 +#define R_DACSR 0x19 +#define R_PWRM1 0x1A +#define R_PWRM2 0x1B +#define R_CONFIG0 0x1F +#define R_CONFIG1 0x20 +#define R_DMICCTL 0x24 +#define R_CLECTL 0x25 +#define R_MUGAIN 0x26 +#define R_COMPTH 0x27 +#define R_CMPRAT 0x28 +#define R_CATKTCL 0x29 +#define R_CATKTCH 0x2A +#define R_CRELTCL 0x2B +#define R_CRELTCH 0x2C +#define R_LIMTH 0x2D +#define R_LIMTGT 0x2E +#define R_LATKTCL 0x2F +#define R_LATKTCH 0x30 +#define R_LRELTCL 0x31 +#define R_LRELTCH 0x32 +#define R_EXPTH 0x33 +#define R_EXPRAT 0x34 +#define R_XATKTCL 0x35 +#define R_XATKTCH 0x36 +#define R_XRELTCL 0x37 +#define R_XRELTCH 0x38 +#define R_FXCTL 0x39 +#define R_DACCRWRL 0x3A +#define R_DACCRWRM 0x3B +#define R_DACCRWRH 0x3C +#define R_DACCRRDL 0x3D +#define R_DACCRRDM 0x3E +#define R_DACCRRDH 0x3F +#define R_DACCRADDR 0x40 +#define R_DCOFSEL 0x41 +#define R_PLLCTL9 0x4E +#define R_PLLCTLA 0x4F +#define R_PLLCTLB 0x50 +#define R_PLLCTLC 0x51 +#define R_PLLCTLD 0x52 +#define R_PLLCTLE 0x53 +#define R_PLLCTLF 0x54 +#define R_PLLCTL10 0x55 +#define R_PLLCTL11 0x56 +#define R_PLLCTL12 0x57 +#define R_PLLCTL1B 0x60 +#define R_PLLCTL1C 0x61 +#define R_TIMEBASE 0x77 +#define R_DEVIDL 0x7D +#define R_DEVIDH 0x7E +#define R_RESET 0x80 +#define R_DACCRSTAT 0x8A +#define R_PLLCTL0 0x8E +#define R_PLLREFSEL 0x8F +#define R_DACMBCEN 0xC7 +#define R_DACMBCCTL 0xC8 +#define R_DACMBCMUG1 0xC9 +#define R_DACMBCTHR1 0xCA +#define R_DACMBCRAT1 0xCB +#define R_DACMBCATK1L 0xCC +#define R_DACMBCATK1H 0xCD +#define R_DACMBCREL1L 0xCE +#define R_DACMBCREL1H 0xCF +#define R_DACMBCMUG2 0xD0 +#define R_DACMBCTHR2 0xD1 +#define R_DACMBCRAT2 0xD2 +#define R_DACMBCATK2L 0xD3 +#define R_DACMBCATK2H 0xD4 +#define R_DACMBCREL2L 0xD5 +#define R_DACMBCREL2H 0xD6 +#define R_DACMBCMUG3 0xD7 +#define R_DACMBCTHR3 0xD8 +#define R_DACMBCRAT3 0xD9 +#define R_DACMBCATK3L 0xDA +#define R_DACMBCATK3H 0xDB +#define R_DACMBCREL3L 0xDC +#define R_DACMBCREL3H 0xDD + +/* Helpers */ +#define RM(m, b) ((m)<<(b)) +#define RV(v, b) ((v)<<(b)) + +/**************************** + * R_HPVOLL (0x0) * + ****************************/ + +/* Field Offsets */ +#define FB_HPVOLL 0 + +/* Field Masks */ +#define FM_HPVOLL 0X7F + +/* Field Values */ +#define FV_HPVOLL_P6DB 0x7F +#define FV_HPVOLL_N88PT5DB 0x1 +#define FV_HPVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_HPVOLL RM(FM_HPVOLL, FB_HPVOLL) + +/* Register Values */ +#define RV_HPVOLL_P6DB RV(FV_HPVOLL_P6DB, FB_HPVOLL) +#define RV_HPVOLL_N88PT5DB RV(FV_HPVOLL_N88PT5DB, FB_HPVOLL) +#define RV_HPVOLL_MUTE RV(FV_HPVOLL_MUTE, FB_HPVOLL) + +/**************************** + * R_HPVOLR (0x1) * + ****************************/ + +/* Field Offsets */ +#define FB_HPVOLR 0 + +/* Field Masks */ +#define FM_HPVOLR 0X7F + +/* Field Values */ +#define FV_HPVOLR_P6DB 0x7F +#define FV_HPVOLR_N88PT5DB 0x1 +#define FV_HPVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_HPVOLR RM(FM_HPVOLR, FB_HPVOLR) + +/* Register Values */ +#define RV_HPVOLR_P6DB RV(FV_HPVOLR_P6DB, FB_HPVOLR) +#define RV_HPVOLR_N88PT5DB RV(FV_HPVOLR_N88PT5DB, FB_HPVOLR) +#define RV_HPVOLR_MUTE RV(FV_HPVOLR_MUTE, FB_HPVOLR) + +/***************************** + * R_SPKVOLL (0x2) * + *****************************/ + +/* Field Offsets */ +#define FB_SPKVOLL 0 + +/* Field Masks */ +#define FM_SPKVOLL 0X7F + +/* Field Values */ +#define FV_SPKVOLL_P12DB 0x7F +#define FV_SPKVOLL_N77PT25DB 0x8 +#define FV_SPKVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_SPKVOLL RM(FM_SPKVOLL, FB_SPKVOLL) + +/* Register Values */ +#define RV_SPKVOLL_P12DB RV(FV_SPKVOLL_P12DB, FB_SPKVOLL) +#define RV_SPKVOLL_N77PT25DB \ + RV(FV_SPKVOLL_N77PT25DB, FB_SPKVOLL) + +#define RV_SPKVOLL_MUTE RV(FV_SPKVOLL_MUTE, FB_SPKVOLL) + +/***************************** + * R_SPKVOLR (0x3) * + *****************************/ + +/* Field Offsets */ +#define FB_SPKVOLR 0 + +/* Field Masks */ +#define FM_SPKVOLR 0X7F + +/* Field Values */ +#define FV_SPKVOLR_P12DB 0x7F +#define FV_SPKVOLR_N77PT25DB 0x8 +#define FV_SPKVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_SPKVOLR RM(FM_SPKVOLR, FB_SPKVOLR) + +/* Register Values */ +#define RV_SPKVOLR_P12DB RV(FV_SPKVOLR_P12DB, FB_SPKVOLR) +#define RV_SPKVOLR_N77PT25DB \ + RV(FV_SPKVOLR_N77PT25DB, FB_SPKVOLR) + +#define RV_SPKVOLR_MUTE RV(FV_SPKVOLR_MUTE, FB_SPKVOLR) + +/***************************** + * R_DACVOLL (0x4) * + *****************************/ + +/* Field Offsets */ +#define FB_DACVOLL 0 + +/* Field Masks */ +#define FM_DACVOLL 0XFF + +/* Field Values */ +#define FV_DACVOLL_0DB 0xFF +#define FV_DACVOLL_N95PT625DB 0x1 +#define FV_DACVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_DACVOLL RM(FM_DACVOLL, FB_DACVOLL) + +/* Register Values */ +#define RV_DACVOLL_0DB RV(FV_DACVOLL_0DB, FB_DACVOLL) +#define RV_DACVOLL_N95PT625DB \ + RV(FV_DACVOLL_N95PT625DB, FB_DACVOLL) + +#define RV_DACVOLL_MUTE RV(FV_DACVOLL_MUTE, FB_DACVOLL) + +/***************************** + * R_DACVOLR (0x5) * + *****************************/ + +/* Field Offsets */ +#define FB_DACVOLR 0 + +/* Field Masks */ +#define FM_DACVOLR 0XFF + +/* Field Values */ +#define FV_DACVOLR_0DB 0xFF +#define FV_DACVOLR_N95PT625DB 0x1 +#define FV_DACVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_DACVOLR RM(FM_DACVOLR, FB_DACVOLR) + +/* Register Values */ +#define RV_DACVOLR_0DB RV(FV_DACVOLR_0DB, FB_DACVOLR) +#define RV_DACVOLR_N95PT625DB \ + RV(FV_DACVOLR_N95PT625DB, FB_DACVOLR) + +#define RV_DACVOLR_MUTE RV(FV_DACVOLR_MUTE, FB_DACVOLR) + +/***************************** + * R_ADCVOLL (0x6) * + *****************************/ + +/* Field Offsets */ +#define FB_ADCVOLL 0 + +/* Field Masks */ +#define FM_ADCVOLL 0XFF + +/* Field Values */ +#define FV_ADCVOLL_P24DB 0xFF +#define FV_ADCVOLL_N71PT25DB 0x1 +#define FV_ADCVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_ADCVOLL RM(FM_ADCVOLL, FB_ADCVOLL) + +/* Register Values */ +#define RV_ADCVOLL_P24DB RV(FV_ADCVOLL_P24DB, FB_ADCVOLL) +#define RV_ADCVOLL_N71PT25DB \ + RV(FV_ADCVOLL_N71PT25DB, FB_ADCVOLL) + +#define RV_ADCVOLL_MUTE RV(FV_ADCVOLL_MUTE, FB_ADCVOLL) + +/***************************** + * R_ADCVOLR (0x7) * + *****************************/ + +/* Field Offsets */ +#define FB_ADCVOLR 0 + +/* Field Masks */ +#define FM_ADCVOLR 0XFF + +/* Field Values */ +#define FV_ADCVOLR_P24DB 0xFF +#define FV_ADCVOLR_N71PT25DB 0x1 +#define FV_ADCVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_ADCVOLR RM(FM_ADCVOLR, FB_ADCVOLR) + +/* Register Values */ +#define RV_ADCVOLR_P24DB RV(FV_ADCVOLR_P24DB, FB_ADCVOLR) +#define RV_ADCVOLR_N71PT25DB \ + RV(FV_ADCVOLR_N71PT25DB, FB_ADCVOLR) + +#define RV_ADCVOLR_MUTE RV(FV_ADCVOLR_MUTE, FB_ADCVOLR) + +/**************************** + * R_INVOLL (0x8) * + ****************************/ + +/* Field Offsets */ +#define FB_INVOLL_INMUTEL 7 +#define FB_INVOLL_IZCL 6 +#define FB_INVOLL 0 + +/* Field Masks */ +#define FM_INVOLL_INMUTEL 0X1 +#define FM_INVOLL_IZCL 0X1 +#define FM_INVOLL 0X3F + +/* Field Values */ +#define FV_INVOLL_INMUTEL_ENABLE 0x1 +#define FV_INVOLL_INMUTEL_DISABLE 0x0 +#define FV_INVOLL_IZCL_ENABLE 0x1 +#define FV_INVOLL_IZCL_DISABLE 0x0 +#define FV_INVOLL_P30DB 0x3F +#define FV_INVOLL_N17PT25DB 0x0 + +/* Register Masks */ +#define RM_INVOLL_INMUTEL \ + RM(FM_INVOLL_INMUTEL, FB_INVOLL_INMUTEL) + +#define RM_INVOLL_IZCL RM(FM_INVOLL_IZCL, FB_INVOLL_IZCL) +#define RM_INVOLL RM(FM_INVOLL, FB_INVOLL) + +/* Register Values */ +#define RV_INVOLL_INMUTEL_ENABLE \ + RV(FV_INVOLL_INMUTEL_ENABLE, FB_INVOLL_INMUTEL) + +#define RV_INVOLL_INMUTEL_DISABLE \ + RV(FV_INVOLL_INMUTEL_DISABLE, FB_INVOLL_INMUTEL) + +#define RV_INVOLL_IZCL_ENABLE \ + RV(FV_INVOLL_IZCL_ENABLE, FB_INVOLL_IZCL) + +#define RV_INVOLL_IZCL_DISABLE \ + RV(FV_INVOLL_IZCL_DISABLE, FB_INVOLL_IZCL) + +#define RV_INVOLL_P30DB RV(FV_INVOLL_P30DB, FB_INVOLL) +#define RV_INVOLL_N17PT25DB RV(FV_INVOLL_N17PT25DB, FB_INVOLL) + +/**************************** + * R_INVOLR (0x9) * + ****************************/ + +/* Field Offsets */ +#define FB_INVOLR_INMUTER 7 +#define FB_INVOLR_IZCR 6 +#define FB_INVOLR 0 + +/* Field Masks */ +#define FM_INVOLR_INMUTER 0X1 +#define FM_INVOLR_IZCR 0X1 +#define FM_INVOLR 0X3F + +/* Field Values */ +#define FV_INVOLR_INMUTER_ENABLE 0x1 +#define FV_INVOLR_INMUTER_DISABLE 0x0 +#define FV_INVOLR_IZCR_ENABLE 0x1 +#define FV_INVOLR_IZCR_DISABLE 0x0 +#define FV_INVOLR_P30DB 0x3F +#define FV_INVOLR_N17PT25DB 0x0 + +/* Register Masks */ +#define RM_INVOLR_INMUTER \ + RM(FM_INVOLR_INMUTER, FB_INVOLR_INMUTER) + +#define RM_INVOLR_IZCR RM(FM_INVOLR_IZCR, FB_INVOLR_IZCR) +#define RM_INVOLR RM(FM_INVOLR, FB_INVOLR) + +/* Register Values */ +#define RV_INVOLR_INMUTER_ENABLE \ + RV(FV_INVOLR_INMUTER_ENABLE, FB_INVOLR_INMUTER) + +#define RV_INVOLR_INMUTER_DISABLE \ + RV(FV_INVOLR_INMUTER_DISABLE, FB_INVOLR_INMUTER) + +#define RV_INVOLR_IZCR_ENABLE \ + RV(FV_INVOLR_IZCR_ENABLE, FB_INVOLR_IZCR) + +#define RV_INVOLR_IZCR_DISABLE \ + RV(FV_INVOLR_IZCR_DISABLE, FB_INVOLR_IZCR) + +#define RV_INVOLR_P30DB RV(FV_INVOLR_P30DB, FB_INVOLR) +#define RV_INVOLR_N17PT25DB RV(FV_INVOLR_N17PT25DB, FB_INVOLR) + +/***************************** + * R_INMODE (0x0B) * + *****************************/ + +/* Field Offsets */ +#define FB_INMODE_DS 0 + +/* Field Masks */ +#define FM_INMODE_DS 0X1 + +/* Field Values */ +#define FV_INMODE_DS_LRIN1 0x0 +#define FV_INMODE_DS_LRIN2 0x1 + +/* Register Masks */ +#define RM_INMODE_DS RM(FM_INMODE_DS, FB_INMODE_DS) + +/* Register Values */ +#define RV_INMODE_DS_LRIN1 \ + RV(FV_INMODE_DS_LRIN1, FB_INMODE_DS) + +#define RV_INMODE_DS_LRIN2 \ + RV(FV_INMODE_DS_LRIN2, FB_INMODE_DS) + + +/***************************** + * R_INSELL (0x0C) * + *****************************/ + +/* Field Offsets */ +#define FB_INSELL 6 +#define FB_INSELL_MICBSTL 4 + +/* Field Masks */ +#define FM_INSELL 0X3 +#define FM_INSELL_MICBSTL 0X3 + +/* Field Values */ +#define FV_INSELL_IN1 0x0 +#define FV_INSELL_IN2 0x1 +#define FV_INSELL_IN3 0x2 +#define FV_INSELL_D2S 0x3 +#define FV_INSELL_MICBSTL_OFF 0x0 +#define FV_INSELL_MICBSTL_10DB 0x1 +#define FV_INSELL_MICBSTL_20DB 0x2 +#define FV_INSELL_MICBSTL_30DB 0x3 + +/* Register Masks */ +#define RM_INSELL RM(FM_INSELL, FB_INSELL) +#define RM_INSELL_MICBSTL \ + RM(FM_INSELL_MICBSTL, FB_INSELL_MICBSTL) + + +/* Register Values */ +#define RV_INSELL_IN1 RV(FV_INSELL_IN1, FB_INSELL) +#define RV_INSELL_IN2 RV(FV_INSELL_IN2, FB_INSELL) +#define RV_INSELL_IN3 RV(FV_INSELL_IN3, FB_INSELL) +#define RV_INSELL_D2S RV(FV_INSELL_D2S, FB_INSELL) +#define RV_INSELL_MICBSTL_OFF \ + RV(FV_INSELL_MICBSTL_OFF, FB_INSELL_MICBSTL) + +#define RV_INSELL_MICBSTL_10DB \ + RV(FV_INSELL_MICBSTL_10DB, FB_INSELL_MICBSTL) + +#define RV_INSELL_MICBSTL_20DB \ + RV(FV_INSELL_MICBSTL_20DB, FB_INSELL_MICBSTL) + +#define RV_INSELL_MICBSTL_30DB \ + RV(FV_INSELL_MICBSTL_30DB, FB_INSELL_MICBSTL) + + +/***************************** + * R_INSELR (0x0D) * + *****************************/ + +/* Field Offsets */ +#define FB_INSELR 6 +#define FB_INSELR_MICBSTR 4 + +/* Field Masks */ +#define FM_INSELR 0X3 +#define FM_INSELR_MICBSTR 0X3 + +/* Field Values */ +#define FV_INSELR_IN1 0x0 +#define FV_INSELR_IN2 0x1 +#define FV_INSELR_IN3 0x2 +#define FV_INSELR_D2S 0x3 +#define FV_INSELR_MICBSTR_OFF 0x0 +#define FV_INSELR_MICBSTR_10DB 0x1 +#define FV_INSELR_MICBSTR_20DB 0x2 +#define FV_INSELR_MICBSTR_30DB 0x3 + +/* Register Masks */ +#define RM_INSELR RM(FM_INSELR, FB_INSELR) +#define RM_INSELR_MICBSTR \ + RM(FM_INSELR_MICBSTR, FB_INSELR_MICBSTR) + + +/* Register Values */ +#define RV_INSELR_IN1 RV(FV_INSELR_IN1, FB_INSELR) +#define RV_INSELR_IN2 RV(FV_INSELR_IN2, FB_INSELR) +#define RV_INSELR_IN3 RV(FV_INSELR_IN3, FB_INSELR) +#define RV_INSELR_D2S RV(FV_INSELR_D2S, FB_INSELR) +#define RV_INSELR_MICBSTR_OFF \ + RV(FV_INSELR_MICBSTR_OFF, FB_INSELR_MICBSTR) + +#define RV_INSELR_MICBSTR_10DB \ + RV(FV_INSELR_MICBSTR_10DB, FB_INSELR_MICBSTR) + +#define RV_INSELR_MICBSTR_20DB \ + RV(FV_INSELR_MICBSTR_20DB, FB_INSELR_MICBSTR) + +#define RV_INSELR_MICBSTR_30DB \ + RV(FV_INSELR_MICBSTR_30DB, FB_INSELR_MICBSTR) + + +/*************************** + * R_AIC1 (0x13) * + ***************************/ + +/* Field Offsets */ +#define FB_AIC1_BCLKINV 6 +#define FB_AIC1_MS 5 +#define FB_AIC1_LRP 4 +#define FB_AIC1_WL 2 +#define FB_AIC1_FORMAT 0 + +/* Field Masks */ +#define FM_AIC1_BCLKINV 0X1 +#define FM_AIC1_MS 0X1 +#define FM_AIC1_LRP 0X1 +#define FM_AIC1_WL 0X3 +#define FM_AIC1_FORMAT 0X3 + +/* Field Values */ +#define FV_AIC1_BCLKINV_ENABLE 0x1 +#define FV_AIC1_BCLKINV_DISABLE 0x0 +#define FV_AIC1_MS_MASTER 0x1 +#define FV_AIC1_MS_SLAVE 0x0 +#define FV_AIC1_LRP_INVERT 0x1 +#define FV_AIC1_LRP_NORMAL 0x0 +#define FV_AIC1_WL_16 0x0 +#define FV_AIC1_WL_20 0x1 +#define FV_AIC1_WL_24 0x2 +#define FV_AIC1_WL_32 0x3 +#define FV_AIC1_FORMAT_RIGHT 0x0 +#define FV_AIC1_FORMAT_LEFT 0x1 +#define FV_AIC1_FORMAT_I2S 0x2 + +/* Register Masks */ +#define RM_AIC1_BCLKINV \ + RM(FM_AIC1_BCLKINV, FB_AIC1_BCLKINV) + +#define RM_AIC1_MS RM(FM_AIC1_MS, FB_AIC1_MS) +#define RM_AIC1_LRP RM(FM_AIC1_LRP, FB_AIC1_LRP) +#define RM_AIC1_WL RM(FM_AIC1_WL, FB_AIC1_WL) +#define RM_AIC1_FORMAT RM(FM_AIC1_FORMAT, FB_AIC1_FORMAT) + +/* Register Values */ +#define RV_AIC1_BCLKINV_ENABLE \ + RV(FV_AIC1_BCLKINV_ENABLE, FB_AIC1_BCLKINV) + +#define RV_AIC1_BCLKINV_DISABLE \ + RV(FV_AIC1_BCLKINV_DISABLE, FB_AIC1_BCLKINV) + +#define RV_AIC1_MS_MASTER RV(FV_AIC1_MS_MASTER, FB_AIC1_MS) +#define RV_AIC1_MS_SLAVE RV(FV_AIC1_MS_SLAVE, FB_AIC1_MS) +#define RV_AIC1_LRP_INVERT \ + RV(FV_AIC1_LRP_INVERT, FB_AIC1_LRP) + +#define RV_AIC1_LRP_NORMAL \ + RV(FV_AIC1_LRP_NORMAL, FB_AIC1_LRP) + +#define RV_AIC1_WL_16 RV(FV_AIC1_WL_16, FB_AIC1_WL) +#define RV_AIC1_WL_20 RV(FV_AIC1_WL_20, FB_AIC1_WL) +#define RV_AIC1_WL_24 RV(FV_AIC1_WL_24, FB_AIC1_WL) +#define RV_AIC1_WL_32 RV(FV_AIC1_WL_32, FB_AIC1_WL) +#define RV_AIC1_FORMAT_RIGHT \ + RV(FV_AIC1_FORMAT_RIGHT, FB_AIC1_FORMAT) + +#define RV_AIC1_FORMAT_LEFT \ + RV(FV_AIC1_FORMAT_LEFT, FB_AIC1_FORMAT) + +#define RV_AIC1_FORMAT_I2S \ + RV(FV_AIC1_FORMAT_I2S, FB_AIC1_FORMAT) + + +/*************************** + * R_AIC2 (0x14) * + ***************************/ + +/* Field Offsets */ +#define FB_AIC2_DACDSEL 6 +#define FB_AIC2_ADCDSEL 4 +#define FB_AIC2_TRI 3 +#define FB_AIC2_BLRCM 0 + +/* Field Masks */ +#define FM_AIC2_DACDSEL 0X3 +#define FM_AIC2_ADCDSEL 0X3 +#define FM_AIC2_TRI 0X1 +#define FM_AIC2_BLRCM 0X7 + +/* Field Values */ +#define FV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED 0x3 + +/* Register Masks */ +#define RM_AIC2_DACDSEL \ + RM(FM_AIC2_DACDSEL, FB_AIC2_DACDSEL) + +#define RM_AIC2_ADCDSEL \ + RM(FM_AIC2_ADCDSEL, FB_AIC2_ADCDSEL) + +#define RM_AIC2_TRI RM(FM_AIC2_TRI, FB_AIC2_TRI) +#define RM_AIC2_BLRCM RM(FM_AIC2_BLRCM, FB_AIC2_BLRCM) + +/* Register Values */ +#define RV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED \ + RV(FV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED, FB_AIC2_BLRCM) + + +/****************************** + * R_CNVRTR0 (0x16) * + ******************************/ + +/* Field Offsets */ +#define FB_CNVRTR0_ADCPOLR 7 +#define FB_CNVRTR0_ADCPOLL 6 +#define FB_CNVRTR0_AMONOMIX 4 +#define FB_CNVRTR0_ADCMU 3 +#define FB_CNVRTR0_HPOR 2 +#define FB_CNVRTR0_ADCHPDR 1 +#define FB_CNVRTR0_ADCHPDL 0 + +/* Field Masks */ +#define FM_CNVRTR0_ADCPOLR 0X1 +#define FM_CNVRTR0_ADCPOLL 0X1 +#define FM_CNVRTR0_AMONOMIX 0X3 +#define FM_CNVRTR0_ADCMU 0X1 +#define FM_CNVRTR0_HPOR 0X1 +#define FM_CNVRTR0_ADCHPDR 0X1 +#define FM_CNVRTR0_ADCHPDL 0X1 + +/* Field Values */ +#define FV_CNVRTR0_ADCPOLR_INVERT 0x1 +#define FV_CNVRTR0_ADCPOLR_NORMAL 0x0 +#define FV_CNVRTR0_ADCPOLL_INVERT 0x1 +#define FV_CNVRTR0_ADCPOLL_NORMAL 0x0 +#define FV_CNVRTR0_ADCMU_ENABLE 0x1 +#define FV_CNVRTR0_ADCMU_DISABLE 0x0 +#define FV_CNVRTR0_ADCHPDR_ENABLE 0x1 +#define FV_CNVRTR0_ADCHPDR_DISABLE 0x0 +#define FV_CNVRTR0_ADCHPDL_ENABLE 0x1 +#define FV_CNVRTR0_ADCHPDL_DISABLE 0x0 + +/* Register Masks */ +#define RM_CNVRTR0_ADCPOLR \ + RM(FM_CNVRTR0_ADCPOLR, FB_CNVRTR0_ADCPOLR) + +#define RM_CNVRTR0_ADCPOLL \ + RM(FM_CNVRTR0_ADCPOLL, FB_CNVRTR0_ADCPOLL) + +#define RM_CNVRTR0_AMONOMIX \ + RM(FM_CNVRTR0_AMONOMIX, FB_CNVRTR0_AMONOMIX) + +#define RM_CNVRTR0_ADCMU \ + RM(FM_CNVRTR0_ADCMU, FB_CNVRTR0_ADCMU) + +#define RM_CNVRTR0_HPOR \ + RM(FM_CNVRTR0_HPOR, FB_CNVRTR0_HPOR) + +#define RM_CNVRTR0_ADCHPDR \ + RM(FM_CNVRTR0_ADCHPDR, FB_CNVRTR0_ADCHPDR) + +#define RM_CNVRTR0_ADCHPDL \ + RM(FM_CNVRTR0_ADCHPDL, FB_CNVRTR0_ADCHPDL) + + +/* Register Values */ +#define RV_CNVRTR0_ADCPOLR_INVERT \ + RV(FV_CNVRTR0_ADCPOLR_INVERT, FB_CNVRTR0_ADCPOLR) + +#define RV_CNVRTR0_ADCPOLR_NORMAL \ + RV(FV_CNVRTR0_ADCPOLR_NORMAL, FB_CNVRTR0_ADCPOLR) + +#define RV_CNVRTR0_ADCPOLL_INVERT \ + RV(FV_CNVRTR0_ADCPOLL_INVERT, FB_CNVRTR0_ADCPOLL) + +#define RV_CNVRTR0_ADCPOLL_NORMAL \ + RV(FV_CNVRTR0_ADCPOLL_NORMAL, FB_CNVRTR0_ADCPOLL) + +#define RV_CNVRTR0_ADCMU_ENABLE \ + RV(FV_CNVRTR0_ADCMU_ENABLE, FB_CNVRTR0_ADCMU) + +#define RV_CNVRTR0_ADCMU_DISABLE \ + RV(FV_CNVRTR0_ADCMU_DISABLE, FB_CNVRTR0_ADCMU) + +#define RV_CNVRTR0_ADCHPDR_ENABLE \ + RV(FV_CNVRTR0_ADCHPDR_ENABLE, FB_CNVRTR0_ADCHPDR) + +#define RV_CNVRTR0_ADCHPDR_DISABLE \ + RV(FV_CNVRTR0_ADCHPDR_DISABLE, FB_CNVRTR0_ADCHPDR) + +#define RV_CNVRTR0_ADCHPDL_ENABLE \ + RV(FV_CNVRTR0_ADCHPDL_ENABLE, FB_CNVRTR0_ADCHPDL) + +#define RV_CNVRTR0_ADCHPDL_DISABLE \ + RV(FV_CNVRTR0_ADCHPDL_DISABLE, FB_CNVRTR0_ADCHPDL) + + +/**************************** + * R_ADCSR (0x17) * + ****************************/ + +/* Field Offsets */ +#define FB_ADCSR_ABCM 6 +#define FB_ADCSR_ABR 3 +#define FB_ADCSR_ABM 0 + +/* Field Masks */ +#define FM_ADCSR_ABCM 0X3 +#define FM_ADCSR_ABR 0X3 +#define FM_ADCSR_ABM 0X7 + +/* Field Values */ +#define FV_ADCSR_ABCM_AUTO 0x0 +#define FV_ADCSR_ABCM_32 0x1 +#define FV_ADCSR_ABCM_40 0x2 +#define FV_ADCSR_ABCM_64 0x3 +#define FV_ADCSR_ABR_32 0x0 +#define FV_ADCSR_ABR_44_1 0x1 +#define FV_ADCSR_ABR_48 0x2 +#define FV_ADCSR_ABM_PT25 0x0 +#define FV_ADCSR_ABM_PT5 0x1 +#define FV_ADCSR_ABM_1 0x2 +#define FV_ADCSR_ABM_2 0x3 + +/* Register Masks */ +#define RM_ADCSR_ABCM RM(FM_ADCSR_ABCM, FB_ADCSR_ABCM) +#define RM_ADCSR_ABR RM(FM_ADCSR_ABR, FB_ADCSR_ABR) +#define RM_ADCSR_ABM RM(FM_ADCSR_ABM, FB_ADCSR_ABM) + +/* Register Values */ +#define RV_ADCSR_ABCM_AUTO \ + RV(FV_ADCSR_ABCM_AUTO, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABCM_32 \ + RV(FV_ADCSR_ABCM_32, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABCM_40 \ + RV(FV_ADCSR_ABCM_40, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABCM_64 \ + RV(FV_ADCSR_ABCM_64, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABR_32 RV(FV_ADCSR_ABR_32, FB_ADCSR_ABR) +#define RV_ADCSR_ABR_44_1 \ + RV(FV_ADCSR_ABR_44_1, FB_ADCSR_ABR) + +#define RV_ADCSR_ABR_48 RV(FV_ADCSR_ABR_48, FB_ADCSR_ABR) +#define RV_ADCSR_ABR_ RV(FV_ADCSR_ABR_, FB_ADCSR_ABR) +#define RV_ADCSR_ABM_PT25 \ + RV(FV_ADCSR_ABM_PT25, FB_ADCSR_ABM) + +#define RV_ADCSR_ABM_PT5 RV(FV_ADCSR_ABM_PT5, FB_ADCSR_ABM) +#define RV_ADCSR_ABM_1 RV(FV_ADCSR_ABM_1, FB_ADCSR_ABM) +#define RV_ADCSR_ABM_2 RV(FV_ADCSR_ABM_2, FB_ADCSR_ABM) + +/****************************** + * R_CNVRTR1 (0x18) * + ******************************/ + +/* Field Offsets */ +#define FB_CNVRTR1_DACPOLR 7 +#define FB_CNVRTR1_DACPOLL 6 +#define FB_CNVRTR1_DMONOMIX 4 +#define FB_CNVRTR1_DACMU 3 +#define FB_CNVRTR1_DEEMPH 2 +#define FB_CNVRTR1_DACDITH 0 + +/* Field Masks */ +#define FM_CNVRTR1_DACPOLR 0X1 +#define FM_CNVRTR1_DACPOLL 0X1 +#define FM_CNVRTR1_DMONOMIX 0X3 +#define FM_CNVRTR1_DACMU 0X1 +#define FM_CNVRTR1_DEEMPH 0X1 +#define FM_CNVRTR1_DACDITH 0X3 + +/* Field Values */ +#define FV_CNVRTR1_DACPOLR_INVERT 0x1 +#define FV_CNVRTR1_DACPOLR_NORMAL 0x0 +#define FV_CNVRTR1_DACPOLL_INVERT 0x1 +#define FV_CNVRTR1_DACPOLL_NORMAL 0x0 +#define FV_CNVRTR1_DMONOMIX_ENABLE 0x1 +#define FV_CNVRTR1_DMONOMIX_DISABLE 0x0 +#define FV_CNVRTR1_DACMU_ENABLE 0x1 +#define FV_CNVRTR1_DACMU_DISABLE 0x0 + +/* Register Masks */ +#define RM_CNVRTR1_DACPOLR \ + RM(FM_CNVRTR1_DACPOLR, FB_CNVRTR1_DACPOLR) + +#define RM_CNVRTR1_DACPOLL \ + RM(FM_CNVRTR1_DACPOLL, FB_CNVRTR1_DACPOLL) + +#define RM_CNVRTR1_DMONOMIX \ + RM(FM_CNVRTR1_DMONOMIX, FB_CNVRTR1_DMONOMIX) + +#define RM_CNVRTR1_DACMU \ + RM(FM_CNVRTR1_DACMU, FB_CNVRTR1_DACMU) + +#define RM_CNVRTR1_DEEMPH \ + RM(FM_CNVRTR1_DEEMPH, FB_CNVRTR1_DEEMPH) + +#define RM_CNVRTR1_DACDITH \ + RM(FM_CNVRTR1_DACDITH, FB_CNVRTR1_DACDITH) + + +/* Register Values */ +#define RV_CNVRTR1_DACPOLR_INVERT \ + RV(FV_CNVRTR1_DACPOLR_INVERT, FB_CNVRTR1_DACPOLR) + +#define RV_CNVRTR1_DACPOLR_NORMAL \ + RV(FV_CNVRTR1_DACPOLR_NORMAL, FB_CNVRTR1_DACPOLR) + +#define RV_CNVRTR1_DACPOLL_INVERT \ + RV(FV_CNVRTR1_DACPOLL_INVERT, FB_CNVRTR1_DACPOLL) + +#define RV_CNVRTR1_DACPOLL_NORMAL \ + RV(FV_CNVRTR1_DACPOLL_NORMAL, FB_CNVRTR1_DACPOLL) + +#define RV_CNVRTR1_DMONOMIX_ENABLE \ + RV(FV_CNVRTR1_DMONOMIX_ENABLE, FB_CNVRTR1_DMONOMIX) + +#define RV_CNVRTR1_DMONOMIX_DISABLE \ + RV(FV_CNVRTR1_DMONOMIX_DISABLE, FB_CNVRTR1_DMONOMIX) + +#define RV_CNVRTR1_DACMU_ENABLE \ + RV(FV_CNVRTR1_DACMU_ENABLE, FB_CNVRTR1_DACMU) + +#define RV_CNVRTR1_DACMU_DISABLE \ + RV(FV_CNVRTR1_DACMU_DISABLE, FB_CNVRTR1_DACMU) + + +/**************************** + * R_DACSR (0x19) * + ****************************/ + +/* Field Offsets */ +#define FB_DACSR_DBCM 6 +#define FB_DACSR_DBR 3 +#define FB_DACSR_DBM 0 + +/* Field Masks */ +#define FM_DACSR_DBCM 0X3 +#define FM_DACSR_DBR 0X3 +#define FM_DACSR_DBM 0X7 + +/* Field Values */ +#define FV_DACSR_DBCM_AUTO 0x0 +#define FV_DACSR_DBCM_32 0x1 +#define FV_DACSR_DBCM_40 0x2 +#define FV_DACSR_DBCM_64 0x3 +#define FV_DACSR_DBR_32 0x0 +#define FV_DACSR_DBR_44_1 0x1 +#define FV_DACSR_DBR_48 0x2 +#define FV_DACSR_DBM_PT25 0x0 +#define FV_DACSR_DBM_PT5 0x1 +#define FV_DACSR_DBM_1 0x2 +#define FV_DACSR_DBM_2 0x3 + +/* Register Masks */ +#define RM_DACSR_DBCM RM(FM_DACSR_DBCM, FB_DACSR_DBCM) +#define RM_DACSR_DBR RM(FM_DACSR_DBR, FB_DACSR_DBR) +#define RM_DACSR_DBM RM(FM_DACSR_DBM, FB_DACSR_DBM) + +/* Register Values */ +#define RV_DACSR_DBCM_AUTO \ + RV(FV_DACSR_DBCM_AUTO, FB_DACSR_DBCM) + +#define RV_DACSR_DBCM_32 \ + RV(FV_DACSR_DBCM_32, FB_DACSR_DBCM) + +#define RV_DACSR_DBCM_40 \ + RV(FV_DACSR_DBCM_40, FB_DACSR_DBCM) + +#define RV_DACSR_DBCM_64 \ + RV(FV_DACSR_DBCM_64, FB_DACSR_DBCM) + +#define RV_DACSR_DBR_32 RV(FV_DACSR_DBR_32, FB_DACSR_DBR) +#define RV_DACSR_DBR_44_1 \ + RV(FV_DACSR_DBR_44_1, FB_DACSR_DBR) + +#define RV_DACSR_DBR_48 RV(FV_DACSR_DBR_48, FB_DACSR_DBR) +#define RV_DACSR_DBM_PT25 \ + RV(FV_DACSR_DBM_PT25, FB_DACSR_DBM) + +#define RV_DACSR_DBM_PT5 RV(FV_DACSR_DBM_PT5, FB_DACSR_DBM) +#define RV_DACSR_DBM_1 RV(FV_DACSR_DBM_1, FB_DACSR_DBM) +#define RV_DACSR_DBM_2 RV(FV_DACSR_DBM_2, FB_DACSR_DBM) + +/**************************** + * R_PWRM1 (0x1A) * + ****************************/ + +/* Field Offsets */ +#define FB_PWRM1_BSTL 7 +#define FB_PWRM1_BSTR 6 +#define FB_PWRM1_PGAL 5 +#define FB_PWRM1_PGAR 4 +#define FB_PWRM1_ADCL 3 +#define FB_PWRM1_ADCR 2 +#define FB_PWRM1_MICB 1 +#define FB_PWRM1_DIGENB 0 + +/* Field Masks */ +#define FM_PWRM1_BSTL 0X1 +#define FM_PWRM1_BSTR 0X1 +#define FM_PWRM1_PGAL 0X1 +#define FM_PWRM1_PGAR 0X1 +#define FM_PWRM1_ADCL 0X1 +#define FM_PWRM1_ADCR 0X1 +#define FM_PWRM1_MICB 0X1 +#define FM_PWRM1_DIGENB 0X1 + +/* Field Values */ +#define FV_PWRM1_BSTL_ENABLE 0x1 +#define FV_PWRM1_BSTL_DISABLE 0x0 +#define FV_PWRM1_BSTR_ENABLE 0x1 +#define FV_PWRM1_BSTR_DISABLE 0x0 +#define FV_PWRM1_PGAL_ENABLE 0x1 +#define FV_PWRM1_PGAL_DISABLE 0x0 +#define FV_PWRM1_PGAR_ENABLE 0x1 +#define FV_PWRM1_PGAR_DISABLE 0x0 +#define FV_PWRM1_ADCL_ENABLE 0x1 +#define FV_PWRM1_ADCL_DISABLE 0x0 +#define FV_PWRM1_ADCR_ENABLE 0x1 +#define FV_PWRM1_ADCR_DISABLE 0x0 +#define FV_PWRM1_MICB_ENABLE 0x1 +#define FV_PWRM1_MICB_DISABLE 0x0 +#define FV_PWRM1_DIGENB_DISABLE 0x1 +#define FV_PWRM1_DIGENB_ENABLE 0x0 + +/* Register Masks */ +#define RM_PWRM1_BSTL RM(FM_PWRM1_BSTL, FB_PWRM1_BSTL) +#define RM_PWRM1_BSTR RM(FM_PWRM1_BSTR, FB_PWRM1_BSTR) +#define RM_PWRM1_PGAL RM(FM_PWRM1_PGAL, FB_PWRM1_PGAL) +#define RM_PWRM1_PGAR RM(FM_PWRM1_PGAR, FB_PWRM1_PGAR) +#define RM_PWRM1_ADCL RM(FM_PWRM1_ADCL, FB_PWRM1_ADCL) +#define RM_PWRM1_ADCR RM(FM_PWRM1_ADCR, FB_PWRM1_ADCR) +#define RM_PWRM1_MICB RM(FM_PWRM1_MICB, FB_PWRM1_MICB) +#define RM_PWRM1_DIGENB \ + RM(FM_PWRM1_DIGENB, FB_PWRM1_DIGENB) + + +/* Register Values */ +#define RV_PWRM1_BSTL_ENABLE \ + RV(FV_PWRM1_BSTL_ENABLE, FB_PWRM1_BSTL) + +#define RV_PWRM1_BSTL_DISABLE \ + RV(FV_PWRM1_BSTL_DISABLE, FB_PWRM1_BSTL) + +#define RV_PWRM1_BSTR_ENABLE \ + RV(FV_PWRM1_BSTR_ENABLE, FB_PWRM1_BSTR) + +#define RV_PWRM1_BSTR_DISABLE \ + RV(FV_PWRM1_BSTR_DISABLE, FB_PWRM1_BSTR) + +#define RV_PWRM1_PGAL_ENABLE \ + RV(FV_PWRM1_PGAL_ENABLE, FB_PWRM1_PGAL) + +#define RV_PWRM1_PGAL_DISABLE \ + RV(FV_PWRM1_PGAL_DISABLE, FB_PWRM1_PGAL) + +#define RV_PWRM1_PGAR_ENABLE \ + RV(FV_PWRM1_PGAR_ENABLE, FB_PWRM1_PGAR) + +#define RV_PWRM1_PGAR_DISABLE \ + RV(FV_PWRM1_PGAR_DISABLE, FB_PWRM1_PGAR) + +#define RV_PWRM1_ADCL_ENABLE \ + RV(FV_PWRM1_ADCL_ENABLE, FB_PWRM1_ADCL) + +#define RV_PWRM1_ADCL_DISABLE \ + RV(FV_PWRM1_ADCL_DISABLE, FB_PWRM1_ADCL) + +#define RV_PWRM1_ADCR_ENABLE \ + RV(FV_PWRM1_ADCR_ENABLE, FB_PWRM1_ADCR) + +#define RV_PWRM1_ADCR_DISABLE \ + RV(FV_PWRM1_ADCR_DISABLE, FB_PWRM1_ADCR) + +#define RV_PWRM1_MICB_ENABLE \ + RV(FV_PWRM1_MICB_ENABLE, FB_PWRM1_MICB) + +#define RV_PWRM1_MICB_DISABLE \ + RV(FV_PWRM1_MICB_DISABLE, FB_PWRM1_MICB) + +#define RV_PWRM1_DIGENB_DISABLE \ + RV(FV_PWRM1_DIGENB_DISABLE, FB_PWRM1_DIGENB) + +#define RV_PWRM1_DIGENB_ENABLE \ + RV(FV_PWRM1_DIGENB_ENABLE, FB_PWRM1_DIGENB) + + +/**************************** + * R_PWRM2 (0x1B) * + ****************************/ + +/* Field Offsets */ +#define FB_PWRM2_D2S 7 +#define FB_PWRM2_HPL 6 +#define FB_PWRM2_HPR 5 +#define FB_PWRM2_SPKL 4 +#define FB_PWRM2_SPKR 3 +#define FB_PWRM2_INSELL 2 +#define FB_PWRM2_INSELR 1 +#define FB_PWRM2_VREF 0 + +/* Field Masks */ +#define FM_PWRM2_D2S 0X1 +#define FM_PWRM2_HPL 0X1 +#define FM_PWRM2_HPR 0X1 +#define FM_PWRM2_SPKL 0X1 +#define FM_PWRM2_SPKR 0X1 +#define FM_PWRM2_INSELL 0X1 +#define FM_PWRM2_INSELR 0X1 +#define FM_PWRM2_VREF 0X1 + +/* Field Values */ +#define FV_PWRM2_D2S_ENABLE 0x1 +#define FV_PWRM2_D2S_DISABLE 0x0 +#define FV_PWRM2_HPL_ENABLE 0x1 +#define FV_PWRM2_HPL_DISABLE 0x0 +#define FV_PWRM2_HPR_ENABLE 0x1 +#define FV_PWRM2_HPR_DISABLE 0x0 +#define FV_PWRM2_SPKL_ENABLE 0x1 +#define FV_PWRM2_SPKL_DISABLE 0x0 +#define FV_PWRM2_SPKR_ENABLE 0x1 +#define FV_PWRM2_SPKR_DISABLE 0x0 +#define FV_PWRM2_INSELL_ENABLE 0x1 +#define FV_PWRM2_INSELL_DISABLE 0x0 +#define FV_PWRM2_INSELR_ENABLE 0x1 +#define FV_PWRM2_INSELR_DISABLE 0x0 +#define FV_PWRM2_VREF_ENABLE 0x1 +#define FV_PWRM2_VREF_DISABLE 0x0 + +/* Register Masks */ +#define RM_PWRM2_D2S RM(FM_PWRM2_D2S, FB_PWRM2_D2S) +#define RM_PWRM2_HPL RM(FM_PWRM2_HPL, FB_PWRM2_HPL) +#define RM_PWRM2_HPR RM(FM_PWRM2_HPR, FB_PWRM2_HPR) +#define RM_PWRM2_SPKL RM(FM_PWRM2_SPKL, FB_PWRM2_SPKL) +#define RM_PWRM2_SPKR RM(FM_PWRM2_SPKR, FB_PWRM2_SPKR) +#define RM_PWRM2_INSELL \ + RM(FM_PWRM2_INSELL, FB_PWRM2_INSELL) + +#define RM_PWRM2_INSELR \ + RM(FM_PWRM2_INSELR, FB_PWRM2_INSELR) + +#define RM_PWRM2_VREF RM(FM_PWRM2_VREF, FB_PWRM2_VREF) + +/* Register Values */ +#define RV_PWRM2_D2S_ENABLE \ + RV(FV_PWRM2_D2S_ENABLE, FB_PWRM2_D2S) + +#define RV_PWRM2_D2S_DISABLE \ + RV(FV_PWRM2_D2S_DISABLE, FB_PWRM2_D2S) + +#define RV_PWRM2_HPL_ENABLE \ + RV(FV_PWRM2_HPL_ENABLE, FB_PWRM2_HPL) + +#define RV_PWRM2_HPL_DISABLE \ + RV(FV_PWRM2_HPL_DISABLE, FB_PWRM2_HPL) + +#define RV_PWRM2_HPR_ENABLE \ + RV(FV_PWRM2_HPR_ENABLE, FB_PWRM2_HPR) + +#define RV_PWRM2_HPR_DISABLE \ + RV(FV_PWRM2_HPR_DISABLE, FB_PWRM2_HPR) + +#define RV_PWRM2_SPKL_ENABLE \ + RV(FV_PWRM2_SPKL_ENABLE, FB_PWRM2_SPKL) + +#define RV_PWRM2_SPKL_DISABLE \ + RV(FV_PWRM2_SPKL_DISABLE, FB_PWRM2_SPKL) + +#define RV_PWRM2_SPKR_ENABLE \ + RV(FV_PWRM2_SPKR_ENABLE, FB_PWRM2_SPKR) + +#define RV_PWRM2_SPKR_DISABLE \ + RV(FV_PWRM2_SPKR_DISABLE, FB_PWRM2_SPKR) + +#define RV_PWRM2_INSELL_ENABLE \ + RV(FV_PWRM2_INSELL_ENABLE, FB_PWRM2_INSELL) + +#define RV_PWRM2_INSELL_DISABLE \ + RV(FV_PWRM2_INSELL_DISABLE, FB_PWRM2_INSELL) + +#define RV_PWRM2_INSELR_ENABLE \ + RV(FV_PWRM2_INSELR_ENABLE, FB_PWRM2_INSELR) + +#define RV_PWRM2_INSELR_DISABLE \ + RV(FV_PWRM2_INSELR_DISABLE, FB_PWRM2_INSELR) + +#define RV_PWRM2_VREF_ENABLE \ + RV(FV_PWRM2_VREF_ENABLE, FB_PWRM2_VREF) + +#define RV_PWRM2_VREF_DISABLE \ + RV(FV_PWRM2_VREF_DISABLE, FB_PWRM2_VREF) + + +/****************************** + * R_CONFIG0 (0x1F) * + ******************************/ + +/* Field Offsets */ +#define FB_CONFIG0_ASDM 6 +#define FB_CONFIG0_DSDM 4 +#define FB_CONFIG0_DC_BYPASS 1 +#define FB_CONFIG0_SD_FORCE_ON 0 + +/* Field Masks */ +#define FM_CONFIG0_ASDM 0X3 +#define FM_CONFIG0_DSDM 0X3 +#define FM_CONFIG0_DC_BYPASS 0X1 +#define FM_CONFIG0_SD_FORCE_ON 0X1 + +/* Field Values */ +#define FV_CONFIG0_ASDM_HALF 0x1 +#define FV_CONFIG0_ASDM_FULL 0x2 +#define FV_CONFIG0_ASDM_AUTO 0x3 +#define FV_CONFIG0_DSDM_HALF 0x1 +#define FV_CONFIG0_DSDM_FULL 0x2 +#define FV_CONFIG0_DSDM_AUTO 0x3 +#define FV_CONFIG0_DC_BYPASS_ENABLE 0x1 +#define FV_CONFIG0_DC_BYPASS_DISABLE 0x0 +#define FV_CONFIG0_SD_FORCE_ON_ENABLE 0x1 +#define FV_CONFIG0_SD_FORCE_ON_DISABLE 0x0 + +/* Register Masks */ +#define RM_CONFIG0_ASDM \ + RM(FM_CONFIG0_ASDM, FB_CONFIG0_ASDM) + +#define RM_CONFIG0_DSDM \ + RM(FM_CONFIG0_DSDM, FB_CONFIG0_DSDM) + +#define RM_CONFIG0_DC_BYPASS \ + RM(FM_CONFIG0_DC_BYPASS, FB_CONFIG0_DC_BYPASS) + +#define RM_CONFIG0_SD_FORCE_ON \ + RM(FM_CONFIG0_SD_FORCE_ON, FB_CONFIG0_SD_FORCE_ON) + + +/* Register Values */ +#define RV_CONFIG0_ASDM_HALF \ + RV(FV_CONFIG0_ASDM_HALF, FB_CONFIG0_ASDM) + +#define RV_CONFIG0_ASDM_FULL \ + RV(FV_CONFIG0_ASDM_FULL, FB_CONFIG0_ASDM) + +#define RV_CONFIG0_ASDM_AUTO \ + RV(FV_CONFIG0_ASDM_AUTO, FB_CONFIG0_ASDM) + +#define RV_CONFIG0_DSDM_HALF \ + RV(FV_CONFIG0_DSDM_HALF, FB_CONFIG0_DSDM) + +#define RV_CONFIG0_DSDM_FULL \ + RV(FV_CONFIG0_DSDM_FULL, FB_CONFIG0_DSDM) + +#define RV_CONFIG0_DSDM_AUTO \ + RV(FV_CONFIG0_DSDM_AUTO, FB_CONFIG0_DSDM) + +#define RV_CONFIG0_DC_BYPASS_ENABLE \ + RV(FV_CONFIG0_DC_BYPASS_ENABLE, FB_CONFIG0_DC_BYPASS) + +#define RV_CONFIG0_DC_BYPASS_DISABLE \ + RV(FV_CONFIG0_DC_BYPASS_DISABLE, FB_CONFIG0_DC_BYPASS) + +#define RV_CONFIG0_SD_FORCE_ON_ENABLE \ + RV(FV_CONFIG0_SD_FORCE_ON_ENABLE, FB_CONFIG0_SD_FORCE_ON) + +#define RV_CONFIG0_SD_FORCE_ON_DISABLE \ + RV(FV_CONFIG0_SD_FORCE_ON_DISABLE, FB_CONFIG0_SD_FORCE_ON) + + +/****************************** + * R_CONFIG1 (0x20) * + ******************************/ + +/* Field Offsets */ +#define FB_CONFIG1_EQ2_EN 7 +#define FB_CONFIG1_EQ2_BE 4 +#define FB_CONFIG1_EQ1_EN 3 +#define FB_CONFIG1_EQ1_BE 0 + +/* Field Masks */ +#define FM_CONFIG1_EQ2_EN 0X1 +#define FM_CONFIG1_EQ2_BE 0X7 +#define FM_CONFIG1_EQ1_EN 0X1 +#define FM_CONFIG1_EQ1_BE 0X7 + +/* Field Values */ +#define FV_CONFIG1_EQ2_EN_ENABLE 0x1 +#define FV_CONFIG1_EQ2_EN_DISABLE 0x0 +#define FV_CONFIG1_EQ2_BE_PRE 0x0 +#define FV_CONFIG1_EQ2_BE_PRE_EQ_0 0x1 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_1 0x2 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_2 0x3 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_3 0x4 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_4 0x5 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_5 0x6 +#define FV_CONFIG1_EQ1_EN_ENABLE 0x1 +#define FV_CONFIG1_EQ1_EN_DISABLE 0x0 +#define FV_CONFIG1_EQ1_BE_PRE 0x0 +#define FV_CONFIG1_EQ1_BE_PRE_EQ_0 0x1 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_1 0x2 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_2 0x3 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_3 0x4 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_4 0x5 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_5 0x6 + +/* Register Masks */ +#define RM_CONFIG1_EQ2_EN \ + RM(FM_CONFIG1_EQ2_EN, FB_CONFIG1_EQ2_EN) + +#define RM_CONFIG1_EQ2_BE \ + RM(FM_CONFIG1_EQ2_BE, FB_CONFIG1_EQ2_BE) + +#define RM_CONFIG1_EQ1_EN \ + RM(FM_CONFIG1_EQ1_EN, FB_CONFIG1_EQ1_EN) + +#define RM_CONFIG1_EQ1_BE \ + RM(FM_CONFIG1_EQ1_BE, FB_CONFIG1_EQ1_BE) + + +/* Register Values */ +#define RV_CONFIG1_EQ2_EN_ENABLE \ + RV(FV_CONFIG1_EQ2_EN_ENABLE, FB_CONFIG1_EQ2_EN) + +#define RV_CONFIG1_EQ2_EN_DISABLE \ + RV(FV_CONFIG1_EQ2_EN_DISABLE, FB_CONFIG1_EQ2_EN) + +#define RV_CONFIG1_EQ2_BE_PRE \ + RV(FV_CONFIG1_EQ2_BE_PRE, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ_0 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ_0, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_1 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_1, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_2 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_2, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_3 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_3, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_4 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_4, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_5 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_5, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ1_EN_ENABLE \ + RV(FV_CONFIG1_EQ1_EN_ENABLE, FB_CONFIG1_EQ1_EN) + +#define RV_CONFIG1_EQ1_EN_DISABLE \ + RV(FV_CONFIG1_EQ1_EN_DISABLE, FB_CONFIG1_EQ1_EN) + +#define RV_CONFIG1_EQ1_BE_PRE \ + RV(FV_CONFIG1_EQ1_BE_PRE, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ_0 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ_0, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_1 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_1, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_2 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_2, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_3 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_3, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_4 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_4, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_5 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_5, FB_CONFIG1_EQ1_BE) + + +/****************************** + * R_DMICCTL (0x24) * + ******************************/ + +/* Field Offsets */ +#define FB_DMICCTL_DMICEN 7 +#define FB_DMICCTL_DMONO 4 +#define FB_DMICCTL_DMPHADJ 2 +#define FB_DMICCTL_DMRATE 0 + +/* Field Masks */ +#define FM_DMICCTL_DMICEN 0X1 +#define FM_DMICCTL_DMONO 0X1 +#define FM_DMICCTL_DMPHADJ 0X3 +#define FM_DMICCTL_DMRATE 0X3 + +/* Field Values */ +#define FV_DMICCTL_DMICEN_ENABLE 0x1 +#define FV_DMICCTL_DMICEN_DISABLE 0x0 +#define FV_DMICCTL_DMONO_STEREO 0x0 +#define FV_DMICCTL_DMONO_MONO 0x1 + +/* Register Masks */ +#define RM_DMICCTL_DMICEN \ + RM(FM_DMICCTL_DMICEN, FB_DMICCTL_DMICEN) + +#define RM_DMICCTL_DMONO \ + RM(FM_DMICCTL_DMONO, FB_DMICCTL_DMONO) + +#define RM_DMICCTL_DMPHADJ \ + RM(FM_DMICCTL_DMPHADJ, FB_DMICCTL_DMPHADJ) + +#define RM_DMICCTL_DMRATE \ + RM(FM_DMICCTL_DMRATE, FB_DMICCTL_DMRATE) + + +/* Register Values */ +#define RV_DMICCTL_DMICEN_ENABLE \ + RV(FV_DMICCTL_DMICEN_ENABLE, FB_DMICCTL_DMICEN) + +#define RV_DMICCTL_DMICEN_DISABLE \ + RV(FV_DMICCTL_DMICEN_DISABLE, FB_DMICCTL_DMICEN) + +#define RV_DMICCTL_DMONO_STEREO \ + RV(FV_DMICCTL_DMONO_STEREO, FB_DMICCTL_DMONO) + +#define RV_DMICCTL_DMONO_MONO \ + RV(FV_DMICCTL_DMONO_MONO, FB_DMICCTL_DMONO) + + +/***************************** + * R_CLECTL (0x25) * + *****************************/ + +/* Field Offsets */ +#define FB_CLECTL_LVL_MODE 4 +#define FB_CLECTL_WINDOWSEL 3 +#define FB_CLECTL_EXP_EN 2 +#define FB_CLECTL_LIMIT_EN 1 +#define FB_CLECTL_COMP_EN 0 + +/* Field Masks */ +#define FM_CLECTL_LVL_MODE 0X1 +#define FM_CLECTL_WINDOWSEL 0X1 +#define FM_CLECTL_EXP_EN 0X1 +#define FM_CLECTL_LIMIT_EN 0X1 +#define FM_CLECTL_COMP_EN 0X1 + +/* Field Values */ +#define FV_CLECTL_LVL_MODE_AVG 0x0 +#define FV_CLECTL_LVL_MODE_PEAK 0x1 +#define FV_CLECTL_WINDOWSEL_512 0x0 +#define FV_CLECTL_WINDOWSEL_64 0x1 +#define FV_CLECTL_EXP_EN_ENABLE 0x1 +#define FV_CLECTL_EXP_EN_DISABLE 0x0 +#define FV_CLECTL_LIMIT_EN_ENABLE 0x1 +#define FV_CLECTL_LIMIT_EN_DISABLE 0x0 +#define FV_CLECTL_COMP_EN_ENABLE 0x1 +#define FV_CLECTL_COMP_EN_DISABLE 0x0 + +/* Register Masks */ +#define RM_CLECTL_LVL_MODE \ + RM(FM_CLECTL_LVL_MODE, FB_CLECTL_LVL_MODE) + +#define RM_CLECTL_WINDOWSEL \ + RM(FM_CLECTL_WINDOWSEL, FB_CLECTL_WINDOWSEL) + +#define RM_CLECTL_EXP_EN \ + RM(FM_CLECTL_EXP_EN, FB_CLECTL_EXP_EN) + +#define RM_CLECTL_LIMIT_EN \ + RM(FM_CLECTL_LIMIT_EN, FB_CLECTL_LIMIT_EN) + +#define RM_CLECTL_COMP_EN \ + RM(FM_CLECTL_COMP_EN, FB_CLECTL_COMP_EN) + + +/* Register Values */ +#define RV_CLECTL_LVL_MODE_AVG \ + RV(FV_CLECTL_LVL_MODE_AVG, FB_CLECTL_LVL_MODE) + +#define RV_CLECTL_LVL_MODE_PEAK \ + RV(FV_CLECTL_LVL_MODE_PEAK, FB_CLECTL_LVL_MODE) + +#define RV_CLECTL_WINDOWSEL_512 \ + RV(FV_CLECTL_WINDOWSEL_512, FB_CLECTL_WINDOWSEL) + +#define RV_CLECTL_WINDOWSEL_64 \ + RV(FV_CLECTL_WINDOWSEL_64, FB_CLECTL_WINDOWSEL) + +#define RV_CLECTL_EXP_EN_ENABLE \ + RV(FV_CLECTL_EXP_EN_ENABLE, FB_CLECTL_EXP_EN) + +#define RV_CLECTL_EXP_EN_DISABLE \ + RV(FV_CLECTL_EXP_EN_DISABLE, FB_CLECTL_EXP_EN) + +#define RV_CLECTL_LIMIT_EN_ENABLE \ + RV(FV_CLECTL_LIMIT_EN_ENABLE, FB_CLECTL_LIMIT_EN) + +#define RV_CLECTL_LIMIT_EN_DISABLE \ + RV(FV_CLECTL_LIMIT_EN_DISABLE, FB_CLECTL_LIMIT_EN) + +#define RV_CLECTL_COMP_EN_ENABLE \ + RV(FV_CLECTL_COMP_EN_ENABLE, FB_CLECTL_COMP_EN) + +#define RV_CLECTL_COMP_EN_DISABLE \ + RV(FV_CLECTL_COMP_EN_DISABLE, FB_CLECTL_COMP_EN) + + +/***************************** + * R_MUGAIN (0x26) * + *****************************/ + +/* Field Offsets */ +#define FB_MUGAIN_CLEMUG 0 + +/* Field Masks */ +#define FM_MUGAIN_CLEMUG 0X1F + +/* Field Values */ +#define FV_MUGAIN_CLEMUG_46PT5DB 0x1F +#define FV_MUGAIN_CLEMUG_0DB 0x0 + +/* Register Masks */ +#define RM_MUGAIN_CLEMUG \ + RM(FM_MUGAIN_CLEMUG, FB_MUGAIN_CLEMUG) + + +/* Register Values */ +#define RV_MUGAIN_CLEMUG_46PT5DB \ + RV(FV_MUGAIN_CLEMUG_46PT5DB, FB_MUGAIN_CLEMUG) + +#define RV_MUGAIN_CLEMUG_0DB \ + RV(FV_MUGAIN_CLEMUG_0DB, FB_MUGAIN_CLEMUG) + + +/***************************** + * R_COMPTH (0x27) * + *****************************/ + +/* Field Offsets */ +#define FB_COMPTH 0 + +/* Field Masks */ +#define FM_COMPTH 0XFF + +/* Field Values */ +#define FV_COMPTH_0DB 0xFF +#define FV_COMPTH_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_COMPTH RM(FM_COMPTH, FB_COMPTH) + +/* Register Values */ +#define RV_COMPTH_0DB RV(FV_COMPTH_0DB, FB_COMPTH) +#define RV_COMPTH_N95PT625DB \ + RV(FV_COMPTH_N95PT625DB, FB_COMPTH) + + +/***************************** + * R_CMPRAT (0x28) * + *****************************/ + +/* Field Offsets */ +#define FB_CMPRAT 0 + +/* Field Masks */ +#define FM_CMPRAT 0X1F + +/* Register Masks */ +#define RM_CMPRAT RM(FM_CMPRAT, FB_CMPRAT) + +/****************************** + * R_CATKTCL (0x29) * + ******************************/ + +/* Field Offsets */ +#define FB_CATKTCL 0 + +/* Field Masks */ +#define FM_CATKTCL 0XFF + +/* Register Masks */ +#define RM_CATKTCL RM(FM_CATKTCL, FB_CATKTCL) + +/****************************** + * R_CATKTCH (0x2A) * + ******************************/ + +/* Field Offsets */ +#define FB_CATKTCH 0 + +/* Field Masks */ +#define FM_CATKTCH 0XFF + +/* Register Masks */ +#define RM_CATKTCH RM(FM_CATKTCH, FB_CATKTCH) + +/****************************** + * R_CRELTCL (0x2B) * + ******************************/ + +/* Field Offsets */ +#define FB_CRELTCL 0 + +/* Field Masks */ +#define FM_CRELTCL 0XFF + +/* Register Masks */ +#define RM_CRELTCL RM(FM_CRELTCL, FB_CRELTCL) + +/****************************** + * R_CRELTCH (0x2C) * + ******************************/ + +/* Field Offsets */ +#define FB_CRELTCH 0 + +/* Field Masks */ +#define FM_CRELTCH 0XFF + +/* Register Masks */ +#define RM_CRELTCH RM(FM_CRELTCH, FB_CRELTCH) + +/**************************** + * R_LIMTH (0x2D) * + ****************************/ + +/* Field Offsets */ +#define FB_LIMTH 0 + +/* Field Masks */ +#define FM_LIMTH 0XFF + +/* Field Values */ +#define FV_LIMTH_0DB 0xFF +#define FV_LIMTH_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_LIMTH RM(FM_LIMTH, FB_LIMTH) + +/* Register Values */ +#define RV_LIMTH_0DB RV(FV_LIMTH_0DB, FB_LIMTH) +#define RV_LIMTH_N95PT625DB RV(FV_LIMTH_N95PT625DB, FB_LIMTH) + +/***************************** + * R_LIMTGT (0x2E) * + *****************************/ + +/* Field Offsets */ +#define FB_LIMTGT 0 + +/* Field Masks */ +#define FM_LIMTGT 0XFF + +/* Field Values */ +#define FV_LIMTGT_0DB 0xFF +#define FV_LIMTGT_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_LIMTGT RM(FM_LIMTGT, FB_LIMTGT) + +/* Register Values */ +#define RV_LIMTGT_0DB RV(FV_LIMTGT_0DB, FB_LIMTGT) +#define RV_LIMTGT_N95PT625DB \ + RV(FV_LIMTGT_N95PT625DB, FB_LIMTGT) + + +/****************************** + * R_LATKTCL (0x2F) * + ******************************/ + +/* Field Offsets */ +#define FB_LATKTCL 0 + +/* Field Masks */ +#define FM_LATKTCL 0XFF + +/* Register Masks */ +#define RM_LATKTCL RM(FM_LATKTCL, FB_LATKTCL) + +/****************************** + * R_LATKTCH (0x30) * + ******************************/ + +/* Field Offsets */ +#define FB_LATKTCH 0 + +/* Field Masks */ +#define FM_LATKTCH 0XFF + +/* Register Masks */ +#define RM_LATKTCH RM(FM_LATKTCH, FB_LATKTCH) + +/****************************** + * R_LRELTCL (0x31) * + ******************************/ + +/* Field Offsets */ +#define FB_LRELTCL 0 + +/* Field Masks */ +#define FM_LRELTCL 0XFF + +/* Register Masks */ +#define RM_LRELTCL RM(FM_LRELTCL, FB_LRELTCL) + +/****************************** + * R_LRELTCH (0x32) * + ******************************/ + +/* Field Offsets */ +#define FB_LRELTCH 0 + +/* Field Masks */ +#define FM_LRELTCH 0XFF + +/* Register Masks */ +#define RM_LRELTCH RM(FM_LRELTCH, FB_LRELTCH) + +/**************************** + * R_EXPTH (0x33) * + ****************************/ + +/* Field Offsets */ +#define FB_EXPTH 0 + +/* Field Masks */ +#define FM_EXPTH 0XFF + +/* Field Values */ +#define FV_EXPTH_0DB 0xFF +#define FV_EXPTH_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_EXPTH RM(FM_EXPTH, FB_EXPTH) + +/* Register Values */ +#define RV_EXPTH_0DB RV(FV_EXPTH_0DB, FB_EXPTH) +#define RV_EXPTH_N95PT625DB RV(FV_EXPTH_N95PT625DB, FB_EXPTH) + +/***************************** + * R_EXPRAT (0x34) * + *****************************/ + +/* Field Offsets */ +#define FB_EXPRAT 0 + +/* Field Masks */ +#define FM_EXPRAT 0X7 + +/* Register Masks */ +#define RM_EXPRAT RM(FM_EXPRAT, FB_EXPRAT) + +/****************************** + * R_XATKTCL (0x35) * + ******************************/ + +/* Field Offsets */ +#define FB_XATKTCL 0 + +/* Field Masks */ +#define FM_XATKTCL 0XFF + +/* Register Masks */ +#define RM_XATKTCL RM(FM_XATKTCL, FB_XATKTCL) + +/****************************** + * R_XATKTCH (0x36) * + ******************************/ + +/* Field Offsets */ +#define FB_XATKTCH 0 + +/* Field Masks */ +#define FM_XATKTCH 0XFF + +/* Register Masks */ +#define RM_XATKTCH RM(FM_XATKTCH, FB_XATKTCH) + +/****************************** + * R_XRELTCL (0x37) * + ******************************/ + +/* Field Offsets */ +#define FB_XRELTCL 0 + +/* Field Masks */ +#define FM_XRELTCL 0XFF + +/* Register Masks */ +#define RM_XRELTCL RM(FM_XRELTCL, FB_XRELTCL) + +/****************************** + * R_XRELTCH (0x38) * + ******************************/ + +/* Field Offsets */ +#define FB_XRELTCH 0 + +/* Field Masks */ +#define FM_XRELTCH 0XFF + +/* Register Masks */ +#define RM_XRELTCH RM(FM_XRELTCH, FB_XRELTCH) + +/**************************** + * R_FXCTL (0x39) * + ****************************/ + +/* Field Offsets */ +#define FB_FXCTL_3DEN 4 +#define FB_FXCTL_TEEN 3 +#define FB_FXCTL_TNLFBYPASS 2 +#define FB_FXCTL_BEEN 1 +#define FB_FXCTL_BNLFBYPASS 0 + +/* Field Masks */ +#define FM_FXCTL_3DEN 0X1 +#define FM_FXCTL_TEEN 0X1 +#define FM_FXCTL_TNLFBYPASS 0X1 +#define FM_FXCTL_BEEN 0X1 +#define FM_FXCTL_BNLFBYPASS 0X1 + +/* Field Values */ +#define FV_FXCTL_3DEN_ENABLE 0x1 +#define FV_FXCTL_3DEN_DISABLE 0x0 +#define FV_FXCTL_TEEN_ENABLE 0x1 +#define FV_FXCTL_TEEN_DISABLE 0x0 +#define FV_FXCTL_TNLFBYPASS_ENABLE 0x1 +#define FV_FXCTL_TNLFBYPASS_DISABLE 0x0 +#define FV_FXCTL_BEEN_ENABLE 0x1 +#define FV_FXCTL_BEEN_DISABLE 0x0 +#define FV_FXCTL_BNLFBYPASS_ENABLE 0x1 +#define FV_FXCTL_BNLFBYPASS_DISABLE 0x0 + +/* Register Masks */ +#define RM_FXCTL_3DEN RM(FM_FXCTL_3DEN, FB_FXCTL_3DEN) +#define RM_FXCTL_TEEN RM(FM_FXCTL_TEEN, FB_FXCTL_TEEN) +#define RM_FXCTL_TNLFBYPASS \ + RM(FM_FXCTL_TNLFBYPASS, FB_FXCTL_TNLFBYPASS) + +#define RM_FXCTL_BEEN RM(FM_FXCTL_BEEN, FB_FXCTL_BEEN) +#define RM_FXCTL_BNLFBYPASS \ + RM(FM_FXCTL_BNLFBYPASS, FB_FXCTL_BNLFBYPASS) + + +/* Register Values */ +#define RV_FXCTL_3DEN_ENABLE \ + RV(FV_FXCTL_3DEN_ENABLE, FB_FXCTL_3DEN) + +#define RV_FXCTL_3DEN_DISABLE \ + RV(FV_FXCTL_3DEN_DISABLE, FB_FXCTL_3DEN) + +#define RV_FXCTL_TEEN_ENABLE \ + RV(FV_FXCTL_TEEN_ENABLE, FB_FXCTL_TEEN) + +#define RV_FXCTL_TEEN_DISABLE \ + RV(FV_FXCTL_TEEN_DISABLE, FB_FXCTL_TEEN) + +#define RV_FXCTL_TNLFBYPASS_ENABLE \ + RV(FV_FXCTL_TNLFBYPASS_ENABLE, FB_FXCTL_TNLFBYPASS) + +#define RV_FXCTL_TNLFBYPASS_DISABLE \ + RV(FV_FXCTL_TNLFBYPASS_DISABLE, FB_FXCTL_TNLFBYPASS) + +#define RV_FXCTL_BEEN_ENABLE \ + RV(FV_FXCTL_BEEN_ENABLE, FB_FXCTL_BEEN) + +#define RV_FXCTL_BEEN_DISABLE \ + RV(FV_FXCTL_BEEN_DISABLE, FB_FXCTL_BEEN) + +#define RV_FXCTL_BNLFBYPASS_ENABLE \ + RV(FV_FXCTL_BNLFBYPASS_ENABLE, FB_FXCTL_BNLFBYPASS) + +#define RV_FXCTL_BNLFBYPASS_DISABLE \ + RV(FV_FXCTL_BNLFBYPASS_DISABLE, FB_FXCTL_BNLFBYPASS) + + +/******************************* + * R_DACCRWRL (0x3A) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRWRL_DACCRWDL 0 + +/* Field Masks */ +#define FM_DACCRWRL_DACCRWDL 0XFF + +/* Register Masks */ +#define RM_DACCRWRL_DACCRWDL \ + RM(FM_DACCRWRL_DACCRWDL, FB_DACCRWRL_DACCRWDL) + + +/******************************* + * R_DACCRWRM (0x3B) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRWRM_DACCRWDM 0 + +/* Field Masks */ +#define FM_DACCRWRM_DACCRWDM 0XFF + +/* Register Masks */ +#define RM_DACCRWRM_DACCRWDM \ + RM(FM_DACCRWRM_DACCRWDM, FB_DACCRWRM_DACCRWDM) + + +/******************************* + * R_DACCRWRH (0x3C) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRWRH_DACCRWDH 0 + +/* Field Masks */ +#define FM_DACCRWRH_DACCRWDH 0XFF + +/* Register Masks */ +#define RM_DACCRWRH_DACCRWDH \ + RM(FM_DACCRWRH_DACCRWDH, FB_DACCRWRH_DACCRWDH) + + +/******************************* + * R_DACCRRDL (0x3D) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRRDL 0 + +/* Field Masks */ +#define FM_DACCRRDL 0XFF + +/* Register Masks */ +#define RM_DACCRRDL RM(FM_DACCRRDL, FB_DACCRRDL) + +/******************************* + * R_DACCRRDM (0x3E) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRRDM 0 + +/* Field Masks */ +#define FM_DACCRRDM 0XFF + +/* Register Masks */ +#define RM_DACCRRDM RM(FM_DACCRRDM, FB_DACCRRDM) + +/******************************* + * R_DACCRRDH (0x3F) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRRDH 0 + +/* Field Masks */ +#define FM_DACCRRDH 0XFF + +/* Register Masks */ +#define RM_DACCRRDH RM(FM_DACCRRDH, FB_DACCRRDH) + +/******************************** + * R_DACCRADDR (0x40) * + ********************************/ + +/* Field Offsets */ +#define FB_DACCRADDR_DACCRADD 0 + +/* Field Masks */ +#define FM_DACCRADDR_DACCRADD 0XFF + +/* Register Masks */ +#define RM_DACCRADDR_DACCRADD \ + RM(FM_DACCRADDR_DACCRADD, FB_DACCRADDR_DACCRADD) + + +/****************************** + * R_DCOFSEL (0x41) * + ******************************/ + +/* Field Offsets */ +#define FB_DCOFSEL_DC_COEF_SEL 0 + +/* Field Masks */ +#define FM_DCOFSEL_DC_COEF_SEL 0X7 + +/* Field Values */ +#define FV_DCOFSEL_DC_COEF_SEL_2_N8 0x0 +#define FV_DCOFSEL_DC_COEF_SEL_2_N9 0x1 +#define FV_DCOFSEL_DC_COEF_SEL_2_N10 0x2 +#define FV_DCOFSEL_DC_COEF_SEL_2_N11 0x3 +#define FV_DCOFSEL_DC_COEF_SEL_2_N12 0x4 +#define FV_DCOFSEL_DC_COEF_SEL_2_N13 0x5 +#define FV_DCOFSEL_DC_COEF_SEL_2_N14 0x6 +#define FV_DCOFSEL_DC_COEF_SEL_2_N15 0x7 + +/* Register Masks */ +#define RM_DCOFSEL_DC_COEF_SEL \ + RM(FM_DCOFSEL_DC_COEF_SEL, FB_DCOFSEL_DC_COEF_SEL) + + +/* Register Values */ +#define RV_DCOFSEL_DC_COEF_SEL_2_N8 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N8, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N9 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N9, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N10 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N10, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N11 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N11, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N12 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N12, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N13 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N13, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N14 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N14, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N15 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N15, FB_DCOFSEL_DC_COEF_SEL) + + +/****************************** + * R_PLLCTL9 (0x4E) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTL9_REFDIV_PLL1 0 + +/* Field Masks */ +#define FM_PLLCTL9_REFDIV_PLL1 0XFF + +/* Register Masks */ +#define RM_PLLCTL9_REFDIV_PLL1 \ + RM(FM_PLLCTL9_REFDIV_PLL1, FB_PLLCTL9_REFDIV_PLL1) + + +/****************************** + * R_PLLCTLA (0x4F) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLA_OUTDIV_PLL1 0 + +/* Field Masks */ +#define FM_PLLCTLA_OUTDIV_PLL1 0XFF + +/* Register Masks */ +#define RM_PLLCTLA_OUTDIV_PLL1 \ + RM(FM_PLLCTLA_OUTDIV_PLL1, FB_PLLCTLA_OUTDIV_PLL1) + + +/****************************** + * R_PLLCTLB (0x50) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLB_FBDIV_PLL1L 0 + +/* Field Masks */ +#define FM_PLLCTLB_FBDIV_PLL1L 0XFF + +/* Register Masks */ +#define RM_PLLCTLB_FBDIV_PLL1L \ + RM(FM_PLLCTLB_FBDIV_PLL1L, FB_PLLCTLB_FBDIV_PLL1L) + + +/****************************** + * R_PLLCTLC (0x51) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLC_FBDIV_PLL1H 0 + +/* Field Masks */ +#define FM_PLLCTLC_FBDIV_PLL1H 0X7 + +/* Register Masks */ +#define RM_PLLCTLC_FBDIV_PLL1H \ + RM(FM_PLLCTLC_FBDIV_PLL1H, FB_PLLCTLC_FBDIV_PLL1H) + + +/****************************** + * R_PLLCTLD (0x52) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLD_RZ_PLL1 3 +#define FB_PLLCTLD_CP_PLL1 0 + +/* Field Masks */ +#define FM_PLLCTLD_RZ_PLL1 0X7 +#define FM_PLLCTLD_CP_PLL1 0X7 + +/* Register Masks */ +#define RM_PLLCTLD_RZ_PLL1 \ + RM(FM_PLLCTLD_RZ_PLL1, FB_PLLCTLD_RZ_PLL1) + +#define RM_PLLCTLD_CP_PLL1 \ + RM(FM_PLLCTLD_CP_PLL1, FB_PLLCTLD_CP_PLL1) + + +/****************************** + * R_PLLCTLE (0x53) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLE_REFDIV_PLL2 0 + +/* Field Masks */ +#define FM_PLLCTLE_REFDIV_PLL2 0XFF + +/* Register Masks */ +#define RM_PLLCTLE_REFDIV_PLL2 \ + RM(FM_PLLCTLE_REFDIV_PLL2, FB_PLLCTLE_REFDIV_PLL2) + + +/****************************** + * R_PLLCTLF (0x54) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLF_OUTDIV_PLL2 0 + +/* Field Masks */ +#define FM_PLLCTLF_OUTDIV_PLL2 0XFF + +/* Register Masks */ +#define RM_PLLCTLF_OUTDIV_PLL2 \ + RM(FM_PLLCTLF_OUTDIV_PLL2, FB_PLLCTLF_OUTDIV_PLL2) + + +/******************************* + * R_PLLCTL10 (0x55) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL10_FBDIV_PLL2L 0 + +/* Field Masks */ +#define FM_PLLCTL10_FBDIV_PLL2L 0XFF + +/* Register Masks */ +#define RM_PLLCTL10_FBDIV_PLL2L \ + RM(FM_PLLCTL10_FBDIV_PLL2L, FB_PLLCTL10_FBDIV_PLL2L) + + +/******************************* + * R_PLLCTL11 (0x56) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL11_FBDIV_PLL2H 0 + +/* Field Masks */ +#define FM_PLLCTL11_FBDIV_PLL2H 0X7 + +/* Register Masks */ +#define RM_PLLCTL11_FBDIV_PLL2H \ + RM(FM_PLLCTL11_FBDIV_PLL2H, FB_PLLCTL11_FBDIV_PLL2H) + + +/******************************* + * R_PLLCTL12 (0x57) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL12_RZ_PLL2 3 +#define FB_PLLCTL12_CP_PLL2 0 + +/* Field Masks */ +#define FM_PLLCTL12_RZ_PLL2 0X7 +#define FM_PLLCTL12_CP_PLL2 0X7 + +/* Register Masks */ +#define RM_PLLCTL12_RZ_PLL2 \ + RM(FM_PLLCTL12_RZ_PLL2, FB_PLLCTL12_RZ_PLL2) + +#define RM_PLLCTL12_CP_PLL2 \ + RM(FM_PLLCTL12_CP_PLL2, FB_PLLCTL12_CP_PLL2) + + +/******************************* + * R_PLLCTL1B (0x60) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL1B_VCOI_PLL2 4 +#define FB_PLLCTL1B_VCOI_PLL1 2 + +/* Field Masks */ +#define FM_PLLCTL1B_VCOI_PLL2 0X3 +#define FM_PLLCTL1B_VCOI_PLL1 0X3 + +/* Register Masks */ +#define RM_PLLCTL1B_VCOI_PLL2 \ + RM(FM_PLLCTL1B_VCOI_PLL2, FB_PLLCTL1B_VCOI_PLL2) + +#define RM_PLLCTL1B_VCOI_PLL1 \ + RM(FM_PLLCTL1B_VCOI_PLL1, FB_PLLCTL1B_VCOI_PLL1) + + +/******************************* + * R_PLLCTL1C (0x61) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL1C_PDB_PLL2 2 +#define FB_PLLCTL1C_PDB_PLL1 1 + +/* Field Masks */ +#define FM_PLLCTL1C_PDB_PLL2 0X1 +#define FM_PLLCTL1C_PDB_PLL1 0X1 + +/* Field Values */ +#define FV_PLLCTL1C_PDB_PLL2_ENABLE 0x1 +#define FV_PLLCTL1C_PDB_PLL2_DISABLE 0x0 +#define FV_PLLCTL1C_PDB_PLL1_ENABLE 0x1 +#define FV_PLLCTL1C_PDB_PLL1_DISABLE 0x0 + +/* Register Masks */ +#define RM_PLLCTL1C_PDB_PLL2 \ + RM(FM_PLLCTL1C_PDB_PLL2, FB_PLLCTL1C_PDB_PLL2) + +#define RM_PLLCTL1C_PDB_PLL1 \ + RM(FM_PLLCTL1C_PDB_PLL1, FB_PLLCTL1C_PDB_PLL1) + + +/* Register Values */ +#define RV_PLLCTL1C_PDB_PLL2_ENABLE \ + RV(FV_PLLCTL1C_PDB_PLL2_ENABLE, FB_PLLCTL1C_PDB_PLL2) + +#define RV_PLLCTL1C_PDB_PLL2_DISABLE \ + RV(FV_PLLCTL1C_PDB_PLL2_DISABLE, FB_PLLCTL1C_PDB_PLL2) + +#define RV_PLLCTL1C_PDB_PLL1_ENABLE \ + RV(FV_PLLCTL1C_PDB_PLL1_ENABLE, FB_PLLCTL1C_PDB_PLL1) + +#define RV_PLLCTL1C_PDB_PLL1_DISABLE \ + RV(FV_PLLCTL1C_PDB_PLL1_DISABLE, FB_PLLCTL1C_PDB_PLL1) + + +/******************************* + * R_TIMEBASE (0x77) * + *******************************/ + +/* Field Offsets */ +#define FB_TIMEBASE_DIVIDER 0 + +/* Field Masks */ +#define FM_TIMEBASE_DIVIDER 0XFF + +/* Register Masks */ +#define RM_TIMEBASE_DIVIDER \ + RM(FM_TIMEBASE_DIVIDER, FB_TIMEBASE_DIVIDER) + + +/***************************** + * R_DEVIDL (0x7D) * + *****************************/ + +/* Field Offsets */ +#define FB_DEVIDL_DIDL 0 + +/* Field Masks */ +#define FM_DEVIDL_DIDL 0XFF + +/* Register Masks */ +#define RM_DEVIDL_DIDL RM(FM_DEVIDL_DIDL, FB_DEVIDL_DIDL) + +/***************************** + * R_DEVIDH (0x7E) * + *****************************/ + +/* Field Offsets */ +#define FB_DEVIDH_DIDH 0 + +/* Field Masks */ +#define FM_DEVIDH_DIDH 0XFF + +/* Register Masks */ +#define RM_DEVIDH_DIDH RM(FM_DEVIDH_DIDH, FB_DEVIDH_DIDH) + +/**************************** + * R_RESET (0x80) * + ****************************/ + +/* Field Offsets */ +#define FB_RESET 0 + +/* Field Masks */ +#define FM_RESET 0XFF + +/* Field Values */ +#define FV_RESET_ENABLE 0x85 + +/* Register Masks */ +#define RM_RESET RM(FM_RESET, FB_RESET) + +/* Register Values */ +#define RV_RESET_ENABLE RV(FV_RESET_ENABLE, FB_RESET) + +/******************************** + * R_DACCRSTAT (0x8A) * + ********************************/ + +/* Field Offsets */ +#define FB_DACCRSTAT_DACCR_BUSY 7 + +/* Field Masks */ +#define FM_DACCRSTAT_DACCR_BUSY 0X1 + +/* Register Masks */ +#define RM_DACCRSTAT_DACCR_BUSY \ + RM(FM_DACCRSTAT_DACCR_BUSY, FB_DACCRSTAT_DACCR_BUSY) + + +/****************************** + * R_PLLCTL0 (0x8E) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTL0_PLL2_LOCK 1 +#define FB_PLLCTL0_PLL1_LOCK 0 + +/* Field Masks */ +#define FM_PLLCTL0_PLL2_LOCK 0X1 +#define FM_PLLCTL0_PLL1_LOCK 0X1 + +/* Register Masks */ +#define RM_PLLCTL0_PLL2_LOCK \ + RM(FM_PLLCTL0_PLL2_LOCK, FB_PLLCTL0_PLL2_LOCK) + +#define RM_PLLCTL0_PLL1_LOCK \ + RM(FM_PLLCTL0_PLL1_LOCK, FB_PLLCTL0_PLL1_LOCK) + + +/******************************** + * R_PLLREFSEL (0x8F) * + ********************************/ + +/* Field Offsets */ +#define FB_PLLREFSEL_PLL2_REF_SEL 4 +#define FB_PLLREFSEL_PLL1_REF_SEL 0 + +/* Field Masks */ +#define FM_PLLREFSEL_PLL2_REF_SEL 0X7 +#define FM_PLLREFSEL_PLL1_REF_SEL 0X7 + +/* Field Values */ +#define FV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1 0x0 +#define FV_PLLREFSEL_PLL2_REF_SEL_MCLK2 0x1 +#define FV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 0x0 +#define FV_PLLREFSEL_PLL1_REF_SEL_MCLK2 0x1 + +/* Register Masks */ +#define RM_PLLREFSEL_PLL2_REF_SEL \ + RM(FM_PLLREFSEL_PLL2_REF_SEL, FB_PLLREFSEL_PLL2_REF_SEL) + +#define RM_PLLREFSEL_PLL1_REF_SEL \ + RM(FM_PLLREFSEL_PLL1_REF_SEL, FB_PLLREFSEL_PLL1_REF_SEL) + + +/* Register Values */ +#define RV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1 \ + RV(FV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1, FB_PLLREFSEL_PLL2_REF_SEL) + +#define RV_PLLREFSEL_PLL2_REF_SEL_MCLK2 \ + RV(FV_PLLREFSEL_PLL2_REF_SEL_MCLK2, FB_PLLREFSEL_PLL2_REF_SEL) + +#define RV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 \ + RV(FV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1, FB_PLLREFSEL_PLL1_REF_SEL) + +#define RV_PLLREFSEL_PLL1_REF_SEL_MCLK2 \ + RV(FV_PLLREFSEL_PLL1_REF_SEL_MCLK2, FB_PLLREFSEL_PLL1_REF_SEL) + + +/******************************* + * R_DACMBCEN (0xC7) * + *******************************/ + +/* Field Offsets */ +#define FB_DACMBCEN_MBCEN3 2 +#define FB_DACMBCEN_MBCEN2 1 +#define FB_DACMBCEN_MBCEN1 0 + +/* Field Masks */ +#define FM_DACMBCEN_MBCEN3 0X1 +#define FM_DACMBCEN_MBCEN2 0X1 +#define FM_DACMBCEN_MBCEN1 0X1 + +/* Register Masks */ +#define RM_DACMBCEN_MBCEN3 \ + RM(FM_DACMBCEN_MBCEN3, FB_DACMBCEN_MBCEN3) + +#define RM_DACMBCEN_MBCEN2 \ + RM(FM_DACMBCEN_MBCEN2, FB_DACMBCEN_MBCEN2) + +#define RM_DACMBCEN_MBCEN1 \ + RM(FM_DACMBCEN_MBCEN1, FB_DACMBCEN_MBCEN1) + + +/******************************** + * R_DACMBCCTL (0xC8) * + ********************************/ + +/* Field Offsets */ +#define FB_DACMBCCTL_LVLMODE3 5 +#define FB_DACMBCCTL_WINSEL3 4 +#define FB_DACMBCCTL_LVLMODE2 3 +#define FB_DACMBCCTL_WINSEL2 2 +#define FB_DACMBCCTL_LVLMODE1 1 +#define FB_DACMBCCTL_WINSEL1 0 + +/* Field Masks */ +#define FM_DACMBCCTL_LVLMODE3 0X1 +#define FM_DACMBCCTL_WINSEL3 0X1 +#define FM_DACMBCCTL_LVLMODE2 0X1 +#define FM_DACMBCCTL_WINSEL2 0X1 +#define FM_DACMBCCTL_LVLMODE1 0X1 +#define FM_DACMBCCTL_WINSEL1 0X1 + +/* Register Masks */ +#define RM_DACMBCCTL_LVLMODE3 \ + RM(FM_DACMBCCTL_LVLMODE3, FB_DACMBCCTL_LVLMODE3) + +#define RM_DACMBCCTL_WINSEL3 \ + RM(FM_DACMBCCTL_WINSEL3, FB_DACMBCCTL_WINSEL3) + +#define RM_DACMBCCTL_LVLMODE2 \ + RM(FM_DACMBCCTL_LVLMODE2, FB_DACMBCCTL_LVLMODE2) + +#define RM_DACMBCCTL_WINSEL2 \ + RM(FM_DACMBCCTL_WINSEL2, FB_DACMBCCTL_WINSEL2) + +#define RM_DACMBCCTL_LVLMODE1 \ + RM(FM_DACMBCCTL_LVLMODE1, FB_DACMBCCTL_LVLMODE1) + +#define RM_DACMBCCTL_WINSEL1 \ + RM(FM_DACMBCCTL_WINSEL1, FB_DACMBCCTL_WINSEL1) + + +/********************************* + * R_DACMBCMUG1 (0xC9) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCMUG1_PHASE 5 +#define FB_DACMBCMUG1_MUGAIN 0 + +/* Field Masks */ +#define FM_DACMBCMUG1_PHASE 0X1 +#define FM_DACMBCMUG1_MUGAIN 0X1F + +/* Register Masks */ +#define RM_DACMBCMUG1_PHASE \ + RM(FM_DACMBCMUG1_PHASE, FB_DACMBCMUG1_PHASE) + +#define RM_DACMBCMUG1_MUGAIN \ + RM(FM_DACMBCMUG1_MUGAIN, FB_DACMBCMUG1_MUGAIN) + + +/********************************* + * R_DACMBCTHR1 (0xCA) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCTHR1_THRESH 0 + +/* Field Masks */ +#define FM_DACMBCTHR1_THRESH 0XFF + +/* Register Masks */ +#define RM_DACMBCTHR1_THRESH \ + RM(FM_DACMBCTHR1_THRESH, FB_DACMBCTHR1_THRESH) + + +/********************************* + * R_DACMBCRAT1 (0xCB) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCRAT1_RATIO 0 + +/* Field Masks */ +#define FM_DACMBCRAT1_RATIO 0X1F + +/* Register Masks */ +#define RM_DACMBCRAT1_RATIO \ + RM(FM_DACMBCRAT1_RATIO, FB_DACMBCRAT1_RATIO) + + +/********************************** + * R_DACMBCATK1L (0xCC) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK1L_TCATKL 0 + +/* Field Masks */ +#define FM_DACMBCATK1L_TCATKL 0XFF + +/* Register Masks */ +#define RM_DACMBCATK1L_TCATKL \ + RM(FM_DACMBCATK1L_TCATKL, FB_DACMBCATK1L_TCATKL) + + +/********************************** + * R_DACMBCATK1H (0xCD) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK1H_TCATKH 0 + +/* Field Masks */ +#define FM_DACMBCATK1H_TCATKH 0XFF + +/* Register Masks */ +#define RM_DACMBCATK1H_TCATKH \ + RM(FM_DACMBCATK1H_TCATKH, FB_DACMBCATK1H_TCATKH) + + +/********************************** + * R_DACMBCREL1L (0xCE) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL1L_TCRELL 0 + +/* Field Masks */ +#define FM_DACMBCREL1L_TCRELL 0XFF + +/* Register Masks */ +#define RM_DACMBCREL1L_TCRELL \ + RM(FM_DACMBCREL1L_TCRELL, FB_DACMBCREL1L_TCRELL) + + +/********************************** + * R_DACMBCREL1H (0xCF) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL1H_TCRELH 0 + +/* Field Masks */ +#define FM_DACMBCREL1H_TCRELH 0XFF + +/* Register Masks */ +#define RM_DACMBCREL1H_TCRELH \ + RM(FM_DACMBCREL1H_TCRELH, FB_DACMBCREL1H_TCRELH) + + +/********************************* + * R_DACMBCMUG2 (0xD0) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCMUG2_PHASE 5 +#define FB_DACMBCMUG2_MUGAIN 0 + +/* Field Masks */ +#define FM_DACMBCMUG2_PHASE 0X1 +#define FM_DACMBCMUG2_MUGAIN 0X1F + +/* Register Masks */ +#define RM_DACMBCMUG2_PHASE \ + RM(FM_DACMBCMUG2_PHASE, FB_DACMBCMUG2_PHASE) + +#define RM_DACMBCMUG2_MUGAIN \ + RM(FM_DACMBCMUG2_MUGAIN, FB_DACMBCMUG2_MUGAIN) + + +/********************************* + * R_DACMBCTHR2 (0xD1) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCTHR2_THRESH 0 + +/* Field Masks */ +#define FM_DACMBCTHR2_THRESH 0XFF + +/* Register Masks */ +#define RM_DACMBCTHR2_THRESH \ + RM(FM_DACMBCTHR2_THRESH, FB_DACMBCTHR2_THRESH) + + +/********************************* + * R_DACMBCRAT2 (0xD2) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCRAT2_RATIO 0 + +/* Field Masks */ +#define FM_DACMBCRAT2_RATIO 0X1F + +/* Register Masks */ +#define RM_DACMBCRAT2_RATIO \ + RM(FM_DACMBCRAT2_RATIO, FB_DACMBCRAT2_RATIO) + + +/********************************** + * R_DACMBCATK2L (0xD3) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK2L_TCATKL 0 + +/* Field Masks */ +#define FM_DACMBCATK2L_TCATKL 0XFF + +/* Register Masks */ +#define RM_DACMBCATK2L_TCATKL \ + RM(FM_DACMBCATK2L_TCATKL, FB_DACMBCATK2L_TCATKL) + + +/********************************** + * R_DACMBCATK2H (0xD4) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK2H_TCATKH 0 + +/* Field Masks */ +#define FM_DACMBCATK2H_TCATKH 0XFF + +/* Register Masks */ +#define RM_DACMBCATK2H_TCATKH \ + RM(FM_DACMBCATK2H_TCATKH, FB_DACMBCATK2H_TCATKH) + + +/********************************** + * R_DACMBCREL2L (0xD5) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL2L_TCRELL 0 + +/* Field Masks */ +#define FM_DACMBCREL2L_TCRELL 0XFF + +/* Register Masks */ +#define RM_DACMBCREL2L_TCRELL \ + RM(FM_DACMBCREL2L_TCRELL, FB_DACMBCREL2L_TCRELL) + + +/********************************** + * R_DACMBCREL2H (0xD6) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL2H_TCRELH 0 + +/* Field Masks */ +#define FM_DACMBCREL2H_TCRELH 0XFF + +/* Register Masks */ +#define RM_DACMBCREL2H_TCRELH \ + RM(FM_DACMBCREL2H_TCRELH, FB_DACMBCREL2H_TCRELH) + + +/********************************* + * R_DACMBCMUG3 (0xD7) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCMUG3_PHASE 5 +#define FB_DACMBCMUG3_MUGAIN 0 + +/* Field Masks */ +#define FM_DACMBCMUG3_PHASE 0X1 +#define FM_DACMBCMUG3_MUGAIN 0X1F + +/* Register Masks */ +#define RM_DACMBCMUG3_PHASE \ + RM(FM_DACMBCMUG3_PHASE, FB_DACMBCMUG3_PHASE) + +#define RM_DACMBCMUG3_MUGAIN \ + RM(FM_DACMBCMUG3_MUGAIN, FB_DACMBCMUG3_MUGAIN) + + +/********************************* + * R_DACMBCTHR3 (0xD8) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCTHR3_THRESH 0 + +/* Field Masks */ +#define FM_DACMBCTHR3_THRESH 0XFF + +/* Register Masks */ +#define RM_DACMBCTHR3_THRESH \ + RM(FM_DACMBCTHR3_THRESH, FB_DACMBCTHR3_THRESH) + + +/********************************* + * R_DACMBCRAT3 (0xD9) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCRAT3_RATIO 0 + +/* Field Masks */ +#define FM_DACMBCRAT3_RATIO 0X1F + +/* Register Masks */ +#define RM_DACMBCRAT3_RATIO \ + RM(FM_DACMBCRAT3_RATIO, FB_DACMBCRAT3_RATIO) + + +/********************************** + * R_DACMBCATK3L (0xDA) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK3L_TCATKL 0 + +/* Field Masks */ +#define FM_DACMBCATK3L_TCATKL 0XFF + +/* Register Masks */ +#define RM_DACMBCATK3L_TCATKL \ + RM(FM_DACMBCATK3L_TCATKL, FB_DACMBCATK3L_TCATKL) + + +/********************************** + * R_DACMBCATK3H (0xDB) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK3H_TCATKH 0 + +/* Field Masks */ +#define FM_DACMBCATK3H_TCATKH 0XFF + +/* Register Masks */ +#define RM_DACMBCATK3H_TCATKH \ + RM(FM_DACMBCATK3H_TCATKH, FB_DACMBCATK3H_TCATKH) + + +/********************************** + * R_DACMBCREL3L (0xDC) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL3L_TCRELL 0 + +/* Field Masks */ +#define FM_DACMBCREL3L_TCRELL 0XFF + +/* Register Masks */ +#define RM_DACMBCREL3L_TCRELL \ + RM(FM_DACMBCREL3L_TCRELL, FB_DACMBCREL3L_TCRELL) + + +/********************************** + * R_DACMBCREL3H (0xDD) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL3H_TCRELH 0 + +/* Field Masks */ +#define FM_DACMBCREL3H_TCRELH 0XFF + +/* Register Masks */ +#define RM_DACMBCREL3H_TCRELH \ + RM(FM_DACMBCREL3H_TCRELH, FB_DACMBCREL3H_TCRELH) + + +#endif /* __WOOKIE_H__ */ diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index c482b2e7a7d2..e4d7f397d361 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -232,7 +232,7 @@ static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec) struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); struct device_node *twl4030_codec_node = NULL; - twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node, + twl4030_codec_node = of_get_child_by_name(codec->dev->parent->of_node, "codec"); if (!pdata && twl4030_codec_node) { @@ -240,10 +240,11 @@ static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec) sizeof(struct twl4030_codec_data), GFP_KERNEL); if (!pdata) { - dev_err(codec->dev, "Can not allocate memory\n"); + of_node_put(twl4030_codec_node); return NULL; } twl4030_setup_pdata_of(pdata, twl4030_codec_node); + of_node_put(twl4030_codec_node); } return pdata; @@ -849,14 +850,14 @@ static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, int mask = (1 << fls(max)) - 1; ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + (twl4030_read(codec, reg) >> shift) & mask; if (ucontrol->value.integer.value[0]) ucontrol->value.integer.value[0] = max + 1 - ucontrol->value.integer.value[0]; if (shift != rshift) { ucontrol->value.integer.value[1] = - (snd_soc_read(codec, reg) >> rshift) & mask; + (twl4030_read(codec, reg) >> rshift) & mask; if (ucontrol->value.integer.value[1]) ucontrol->value.integer.value[1] = max + 1 - ucontrol->value.integer.value[1]; @@ -907,9 +908,9 @@ static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, int mask = (1<<fls(max))-1; ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + (twl4030_read(codec, reg) >> shift) & mask; ucontrol->value.integer.value[1] = - (snd_soc_read(codec, reg2) >> shift) & mask; + (twl4030_read(codec, reg2) >> shift) & mask; if (ucontrol->value.integer.value[0]) ucontrol->value.integer.value[0] = diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 1773ff84ee3b..573a523ed0b3 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -106,10 +106,12 @@ static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = { { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, }, }; +#define to_twl6040(codec) dev_get_drvdata((codec)->dev->parent) + static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - struct twl6040 *twl6040 = codec->control_data; + struct twl6040 *twl6040 = to_twl6040(codec); u8 value; if (reg >= TWL6040_CACHEREGNUM) @@ -171,7 +173,7 @@ static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec, static int twl6040_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - struct twl6040 *twl6040 = codec->control_data; + struct twl6040 *twl6040 = to_twl6040(codec); if (reg >= TWL6040_CACHEREGNUM) return -EIO; @@ -541,7 +543,7 @@ int twl6040_get_dl1_gain(struct snd_soc_codec *codec) if (snd_soc_dapm_get_pin_status(dapm, "HSOR") || snd_soc_dapm_get_pin_status(dapm, "HSOL")) { - u8 val = snd_soc_read(codec, TWL6040_REG_HSLCTL); + u8 val = twl6040_read(codec, TWL6040_REG_HSLCTL); if (val & TWL6040_HSDACMODE) /* HSDACL in LP mode */ return -8; /* -8dB */ @@ -572,7 +574,7 @@ EXPORT_SYMBOL_GPL(twl6040_get_trim_value); int twl6040_get_hs_step_size(struct snd_soc_codec *codec) { - struct twl6040 *twl6040 = codec->control_data; + struct twl6040 *twl6040 = to_twl6040(codec); if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3) /* For ES under ES_1.3 HS step is 2 mV */ @@ -830,7 +832,7 @@ static const struct snd_soc_dapm_route intercon[] = { static int twl6040_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct twl6040 *twl6040 = codec->control_data; + struct twl6040 *twl6040 = to_twl6040(codec); struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int ret = 0; @@ -922,7 +924,7 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - struct twl6040 *twl6040 = codec->control_data; + struct twl6040 *twl6040 = to_twl6040(codec); struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int ret; @@ -964,7 +966,7 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id, int mute) { - struct twl6040 *twl6040 = codec->control_data; + struct twl6040 *twl6040 = to_twl6040(codec); struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int hslctl, hsrctl, earctl; int hflctl, hfrctl; @@ -1108,7 +1110,6 @@ static struct snd_soc_dai_driver twl6040_dai[] = { static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; - struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent); struct platform_device *pdev = to_platform_device(codec->dev); int ret = 0; @@ -1119,7 +1120,6 @@ static int twl6040_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; - codec->control_data = twl6040; priv->plug_irq = platform_get_irq(pdev, 0); if (priv->plug_irq < 0) { diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 926c81ae8185..46a495b4da8d 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -37,7 +37,8 @@ struct uda1380_priv { struct snd_soc_codec *codec; unsigned int dac_clk; struct work_struct work; - void *control_data; + struct i2c_client *i2c; + u16 *reg_cache; }; /* @@ -63,7 +64,9 @@ static unsigned long uda1380_cache_dirty; static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec, unsigned int reg) { - u16 *cache = codec->reg_cache; + struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); + u16 *cache = uda1380->reg_cache; + if (reg == UDA1380_RESET) return 0; if (reg >= UDA1380_CACHEREGNUM) @@ -77,7 +80,8 @@ static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec, static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec, u16 reg, unsigned int value) { - u16 *cache = codec->reg_cache; + struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); + u16 *cache = uda1380->reg_cache; if (reg >= UDA1380_CACHEREGNUM) return; @@ -92,6 +96,7 @@ static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec, static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { + struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); u8 data[3]; /* data is @@ -111,10 +116,10 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL)) return 0; pr_debug("uda1380: hw write %x val %x\n", reg, value); - if (codec->hw_write(codec->control_data, data, 3) == 3) { + if (i2c_master_send(uda1380->i2c, data, 3) == 3) { unsigned int val; - i2c_master_send(codec->control_data, data, 1); - i2c_master_recv(codec->control_data, data, 2); + i2c_master_send(uda1380->i2c, data, 1); + i2c_master_recv(uda1380->i2c, data, 2); val = (data[0]<<8) | data[1]; if (val != value) { pr_debug("uda1380: READ BACK VAL %x\n", @@ -130,16 +135,17 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, static void uda1380_sync_cache(struct snd_soc_codec *codec) { + struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); int reg; u8 data[3]; - u16 *cache = codec->reg_cache; + u16 *cache = uda1380->reg_cache; /* Sync reg_cache with the hardware */ for (reg = 0; reg < UDA1380_MVOL; reg++) { data[0] = reg; data[1] = (cache[reg] & 0xff00) >> 8; data[2] = cache[reg] & 0x00ff; - if (codec->hw_write(codec->control_data, data, 3) != 3) + if (i2c_master_send(uda1380->i2c, data, 3) != 3) dev_err(codec->dev, "%s: write to reg 0x%x failed\n", __func__, reg); } @@ -148,6 +154,7 @@ static void uda1380_sync_cache(struct snd_soc_codec *codec) static int uda1380_reset(struct snd_soc_codec *codec) { struct uda1380_platform_data *pdata = codec->dev->platform_data; + struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); if (gpio_is_valid(pdata->gpio_reset)) { gpio_set_value(pdata->gpio_reset, 1); @@ -160,7 +167,7 @@ static int uda1380_reset(struct snd_soc_codec *codec) data[1] = 0; data[2] = 0; - if (codec->hw_write(codec->control_data, data, 3) != 3) { + if (i2c_master_send(uda1380->i2c, data, 3) != 3) { dev_err(codec->dev, "%s: failed\n", __func__); return -EIO; } @@ -695,9 +702,6 @@ static int uda1380_probe(struct snd_soc_codec *codec) uda1380->codec = codec; - codec->hw_write = (hw_write_t)i2c_master_send; - codec->control_data = uda1380->control_data; - if (!gpio_is_valid(pdata->gpio_power)) { ret = uda1380_reset(codec); if (ret) @@ -722,16 +726,9 @@ static int uda1380_probe(struct snd_soc_codec *codec) static const struct snd_soc_codec_driver soc_codec_dev_uda1380 = { .probe = uda1380_probe, - .read = uda1380_read_reg_cache, - .write = uda1380_write, .set_bias_level = uda1380_set_bias_level, .suspend_bias_off = true, - .reg_cache_size = ARRAY_SIZE(uda1380_reg), - .reg_word_size = sizeof(u16), - .reg_cache_default = uda1380_reg, - .reg_cache_step = 1, - .component_driver = { .controls = uda1380_snd_controls, .num_controls = ARRAY_SIZE(uda1380_snd_controls), @@ -771,8 +768,15 @@ static int uda1380_i2c_probe(struct i2c_client *i2c, return ret; } + uda1380->reg_cache = devm_kmemdup(&i2c->dev, + uda1380_reg, + ARRAY_SIZE(uda1380_reg) * sizeof(u16), + GFP_KERNEL); + if (!uda1380->reg_cache) + return -ENOMEM; + i2c_set_clientdata(i2c, uda1380); - uda1380->control_data = i2c; + uda1380->i2c = i2c; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai)); diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 4f5f5710b569..0147d2fb7b0a 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -655,11 +655,8 @@ static int wm0010_boot(struct snd_soc_codec *codec) ret = -ENOMEM; len = pll_rec.length + 8; out = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!out) { - dev_err(codec->dev, - "Failed to allocate RX buffer\n"); + if (!out) goto abort; - } img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img_swap) diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 23cde3a0dc11..abfa052c07d8 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -13,7 +13,7 @@ * 'wm2000_anc.bin' by default (overridable via platform data) at * runtime and is expected to be in flat binary format. This is * generated by Wolfson configuration tools and includes - * system-specific callibration information. If supplied as a + * system-specific calibration information. If supplied as a * sequence of ASCII-encoded hexidecimal bytes this can be converted * into a flat binary with a command such as this on the command line: * @@ -826,8 +826,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, int reg; u16 id; - wm2000 = devm_kzalloc(&i2c->dev, sizeof(struct wm2000_priv), - GFP_KERNEL); + wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL); if (!wm2000) return -ENOMEM; @@ -902,7 +901,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, wm2000->anc_download_size, GFP_KERNEL); if (wm2000->anc_download == NULL) { - dev_err(&i2c->dev, "Out of memory\n"); ret = -ENOMEM; goto err_supplies; } diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index d83dab57a1d1..5c2f5727244d 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -98,6 +98,8 @@ struct wm2200_priv { int rev; int sysclk; + + unsigned int symmetric_rates:1; }; #define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1) @@ -1550,7 +1552,7 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = { static int wm2200_probe(struct snd_soc_codec *codec) { - struct wm2200_priv *wm2200 = dev_get_drvdata(codec->dev); + struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec); int ret; wm2200->codec = codec; @@ -1758,7 +1760,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream, lrclk = bclk_rates[bclk] / params_rate(params); dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || - dai->symmetric_rates) + wm2200->symmetric_rates) snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_7, WM2200_AIF1RX_BCPF_MASK, lrclk); else @@ -2059,13 +2061,14 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, static int wm2200_dai_probe(struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; + struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec); unsigned int val = 0; int ret; ret = snd_soc_read(codec, WM2200_GPIO_CTRL_1); if (ret >= 0) { if ((ret & WM2200_GP1_FN_MASK) != 0) { - dai->symmetric_rates = true; + wm2200->symmetric_rates = true; val = WM2200_AIF1TX_LRCLK_SRC; } } else { diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 4f0481d3c7a7..fc066caa1918 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1935,8 +1935,11 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int ret; + snd_soc_codec_init_regmap(codec, arizona->regmap); + ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec); if (ret) return ret; @@ -1989,17 +1992,9 @@ static unsigned int wm5102_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_5R, }; -static struct regmap *wm5102_get_regmap(struct device *dev) -{ - struct wm5102_priv *priv = dev_get_drvdata(dev); - - return priv->core.arizona->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_wm5102 = { .probe = wm5102_codec_probe, .remove = wm5102_codec_remove, - .get_regmap = wm5102_get_regmap, .idle_bias_off = true, diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 6ed1e1f9ce51..fb0cf9c61f48 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2280,9 +2280,11 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int i, ret; - priv->core.arizona->dapm = dapm; + arizona->dapm = dapm; + snd_soc_codec_init_regmap(codec, arizona->regmap); ret = arizona_init_spk(codec); if (ret < 0) @@ -2344,17 +2346,9 @@ static unsigned int wm5110_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_6R, }; -static struct regmap *wm5110_get_regmap(struct device *dev) -{ - struct wm5110_priv *priv = dev_get_drvdata(dev); - - return priv->core.arizona->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_wm5110 = { .probe = wm5110_codec_probe, .remove = wm5110_codec_remove, - .get_regmap = wm5110_get_regmap, .idle_bias_off = true, diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 2efc5b41ad0f..fc79c6725d06 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1472,6 +1472,8 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) GFP_KERNEL); if (priv == NULL) return -ENOMEM; + + snd_soc_codec_init_regmap(codec, wm8350->regmap); snd_soc_codec_set_drvdata(codec, priv); priv->wm8350 = wm8350; @@ -1580,17 +1582,9 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) return 0; } -static struct regmap *wm8350_get_regmap(struct device *dev) -{ - struct wm8350 *wm8350 = dev_get_platdata(dev); - - return wm8350->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_wm8350 = { .probe = wm8350_codec_probe, .remove = wm8350_codec_remove, - .get_regmap = wm8350_get_regmap, .set_bias_level = wm8350_set_bias_level, .suspend_bias_off = true, diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 6c59fb933bd6..a36adf881bca 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1285,6 +1285,7 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec) if (priv == NULL) return -ENOMEM; + snd_soc_codec_init_regmap(codec, wm8400->regmap); snd_soc_codec_set_drvdata(codec, priv); priv->wm8400 = wm8400; @@ -1325,17 +1326,9 @@ static int wm8400_codec_remove(struct snd_soc_codec *codec) return 0; } -static struct regmap *wm8400_get_regmap(struct device *dev) -{ - struct wm8400 *wm8400 = dev_get_platdata(dev); - - return wm8400->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_wm8400 = { .probe = wm8400_codec_probe, .remove = wm8400_codec_remove, - .get_regmap = wm8400_get_regmap, .set_bias_level = wm8400_set_bias_level, .suspend_bias_off = true, diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 237eeb9a8b97..cba90f21161f 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1995,8 +1995,7 @@ static int wm8903_i2c_probe(struct i2c_client *i2c, unsigned int val, irq_pol; int ret, i; - wm8903 = devm_kzalloc(&i2c->dev, sizeof(struct wm8903_priv), - GFP_KERNEL); + wm8903 = devm_kzalloc(&i2c->dev, sizeof(*wm8903), GFP_KERNEL); if (wm8903 == NULL) return -ENOMEM; @@ -2017,13 +2016,10 @@ static int wm8903_i2c_probe(struct i2c_client *i2c, if (pdata) { wm8903->pdata = pdata; } else { - wm8903->pdata = devm_kzalloc(&i2c->dev, - sizeof(struct wm8903_platform_data), - GFP_KERNEL); - if (wm8903->pdata == NULL) { - dev_err(&i2c->dev, "Failed to allocate pdata\n"); + wm8903->pdata = devm_kzalloc(&i2c->dev, sizeof(*wm8903->pdata), + GFP_KERNEL); + if (!wm8903->pdata) return -ENOMEM; - } if (i2c->irq) { ret = wm8903_set_pdata_irq_trigger(i2c, wm8903->pdata); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index f91b49e1ece3..21ffd6403173 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3993,6 +3993,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) unsigned int reg; int ret, i; + snd_soc_codec_init_regmap(codec, control->regmap); + wm8994->hubs.codec = codec; mutex_init(&wm8994->accdet_lock); @@ -4434,19 +4436,11 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) return 0; } -static struct regmap *wm8994_get_regmap(struct device *dev) -{ - struct wm8994 *control = dev_get_drvdata(dev->parent); - - return control->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_wm8994 = { .probe = wm8994_codec_probe, .remove = wm8994_codec_remove, .suspend = wm8994_codec_suspend, .resume = wm8994_codec_resume, - .get_regmap = wm8994_get_regmap, .set_bias_level = wm8994_set_bias_level, }; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 77f512767273..cac9b3e7e15d 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1062,8 +1062,11 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int ret; + snd_soc_codec_init_regmap(codec, arizona->regmap); + ret = arizona_init_spk(codec); if (ret < 0) return ret; @@ -1095,17 +1098,9 @@ static unsigned int wm8997_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_5R, }; -static struct regmap *wm8997_get_regmap(struct device *dev) -{ - struct wm8997_priv *priv = dev_get_drvdata(dev); - - return priv->core.arizona->regmap; -} - static const struct snd_soc_codec_driver soc_codec_dev_wm8997 = { .probe = wm8997_codec_probe, .remove = wm8997_codec_remove, - .get_regmap = wm8997_get_regmap, .idle_bias_off = true, diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 2d211dbe7422..1288e1f67dcf 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -1275,9 +1275,11 @@ 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); struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct arizona *arizona = priv->core.arizona; int ret; - priv->core.arizona->dapm = dapm; + arizona->dapm = dapm; + snd_soc_codec_init_regmap(codec, arizona->regmap); ret = arizona_init_spk(codec); if (ret < 0) @@ -1313,17 +1315,9 @@ static unsigned int wm8998_digital_vu[] = { 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 const 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, diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 65c059b5ffd7..66e32f5d2917 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1733,7 +1733,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) le64_to_cpu(footer->timestamp)); while (pos < firmware->size && - pos - firmware->size > sizeof(*region)) { + sizeof(*region) < firmware->size - pos) { region = (void *)&(firmware->data[pos]); region_name = "Unknown"; reg = 0; @@ -1782,8 +1782,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) regions, le32_to_cpu(region->len), offset, region_name); - if ((pos + le32_to_cpu(region->len) + sizeof(*region)) > - firmware->size) { + if (le32_to_cpu(region->len) > + firmware->size - pos - sizeof(*region)) { adsp_err(dsp, "%s.%d: %s region len %d bytes exceeds file length %zu\n", file, regions, region_name, @@ -2253,7 +2253,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) blocks = 0; while (pos < firmware->size && - pos - firmware->size > sizeof(*blk)) { + sizeof(*blk) < firmware->size - pos) { blk = (void *)(&firmware->data[pos]); type = le16_to_cpu(blk->type); @@ -2327,8 +2327,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } if (reg) { - if ((pos + le32_to_cpu(blk->len) + sizeof(*blk)) > - firmware->size) { + if (le32_to_cpu(blk->len) > + firmware->size - pos - sizeof(*blk)) { adsp_err(dsp, "%s.%d: %s region len %d bytes exceeds file length %zu\n", file, blocks, region_name, diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 804c6f2bcf21..03ba218160ca 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1242,6 +1242,20 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, return snd_mask_refine(fmt, &nfmt); } +static int davinci_mcasp_hw_rule_min_periodsize( + struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *period_size = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + struct snd_interval frames; + + snd_interval_any(&frames); + frames.min = 64; + frames.integer = 1; + + return snd_interval_refine(period_size, &frames); +} + static int davinci_mcasp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { @@ -1333,6 +1347,11 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, return ret; } + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + davinci_mcasp_hw_rule_min_periodsize, NULL, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + return 0; } diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 84ef6385736c..191426a6d9ad 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -29,7 +29,6 @@ #include "../codecs/tlv320aic23.h" #include "imx-ssi.h" -#include "fsl_ssi.h" #include "imx-audmux.h" #define CODEC_CLOCK 12000000 diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 1225e0399de8..989be518c4ed 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -442,8 +442,8 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) if (fsl_asoc_card_is_ac97(priv)) { #if IS_ENABLED(CONFIG_SND_AC97_CODEC) - struct snd_soc_codec *codec = rtd->codec; - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); /* * Use slots 3/4 for S/PDIF so SSI won't try to enable diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 0f163abe4ba3..2c5856ac5bc3 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -57,7 +57,7 @@ #define REG_ASRDOC 0x74 #define REG_ASRDI(i) (REG_ASRDIA + (i << 3)) #define REG_ASRDO(i) (REG_ASRDOA + (i << 3)) -#define REG_ASRDx(x, i) (x == IN ? REG_ASRDI(i) : REG_ASRDO(i)) +#define REG_ASRDx(x, i) ((x) == IN ? REG_ASRDI(i) : REG_ASRDO(i)) #define REG_ASRIDRHA 0x80 #define REG_ASRIDRLA 0x84 @@ -260,8 +260,8 @@ #define ASRFSTi_OUTPUT_FIFO_SHIFT 12 #define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT) #define ASRFSTi_IAEi_SHIFT 11 -#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_OAFi_SHIFT) -#define ASRFSTi_IAEi (1 << ASRFSTi_OAFi_SHIFT) +#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_IAEi_SHIFT) +#define ASRFSTi_IAEi (1 << ASRFSTi_IAEi_SHIFT) #define ASRFSTi_INPUT_FIFO_WIDTH 7 #define ASRFSTi_INPUT_FIFO_SHIFT 0 #define ASRFSTi_INPUT_FIFO_MASK ((1 << ASRFSTi_INPUT_FIFO_WIDTH) - 1) diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 0c11f434a374..8c2981b70f64 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -913,8 +913,8 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) dma->dai.pcm_free = fsl_dma_free_dma_buffers; /* Store the SSI-specific information that we need */ - dma->ssi_stx_phys = res.start + CCSR_SSI_STX0; - dma->ssi_srx_phys = res.start + CCSR_SSI_SRX0; + dma->ssi_stx_phys = res.start + REG_SSI_STX0; + dma->ssi_srx_phys = res.start + REG_SSI_SRX0; iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); if (iprop) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index f2f51e06e22c..aecd00f7929d 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -38,6 +38,7 @@ #include <linux/ctype.h> #include <linux/device.h> #include <linux/delay.h> +#include <linux/mutex.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/of.h> @@ -68,21 +69,35 @@ * samples will be written to STX properly. */ #ifdef __BIG_ENDIAN -#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \ - SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE) +#define FSLSSI_I2S_FORMATS \ + (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S18_3BE | \ + SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_BE) #else -#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) +#define FSLSSI_I2S_FORMATS \ + (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) #endif -#define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \ - CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \ - CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN) -#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \ - CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \ - CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN) +#define FSLSSI_SIER_DBG_RX_FLAGS \ + (SSI_SIER_RFF0_EN | \ + SSI_SIER_RLS_EN | \ + SSI_SIER_RFS_EN | \ + SSI_SIER_ROE0_EN | \ + SSI_SIER_RFRC_EN) +#define FSLSSI_SIER_DBG_TX_FLAGS \ + (SSI_SIER_TFE0_EN | \ + SSI_SIER_TLS_EN | \ + SSI_SIER_TFS_EN | \ + SSI_SIER_TUE0_EN | \ + SSI_SIER_TFRC_EN) enum fsl_ssi_type { FSL_SSI_MCP8610, @@ -91,23 +106,18 @@ enum fsl_ssi_type { FSL_SSI_MX51, }; -struct fsl_ssi_reg_val { +struct fsl_ssi_regvals { u32 sier; u32 srcr; u32 stcr; u32 scr; }; -struct fsl_ssi_rxtx_reg_val { - struct fsl_ssi_reg_val rx; - struct fsl_ssi_reg_val tx; -}; - static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { - case CCSR_SSI_SACCEN: - case CCSR_SSI_SACCDIS: + case REG_SSI_SACCEN: + case REG_SSI_SACCDIS: return false; default: return true; @@ -117,18 +127,18 @@ static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) 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_SACNT: - case CCSR_SSI_SACADD: - case CCSR_SSI_SACDAT: - case CCSR_SSI_SATAG: - case CCSR_SSI_SACCST: - case CCSR_SSI_SOR: + case REG_SSI_STX0: + case REG_SSI_STX1: + case REG_SSI_SRX0: + case REG_SSI_SRX1: + case REG_SSI_SISR: + case REG_SSI_SFCSR: + case REG_SSI_SACNT: + case REG_SSI_SACADD: + case REG_SSI_SACDAT: + case REG_SSI_SATAG: + case REG_SSI_SACCST: + case REG_SSI_SOR: return true; default: return false; @@ -138,12 +148,12 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { - case CCSR_SSI_SRX0: - case CCSR_SSI_SRX1: - case CCSR_SSI_SISR: - case CCSR_SSI_SACADD: - case CCSR_SSI_SACDAT: - case CCSR_SSI_SATAG: + case REG_SSI_SRX0: + case REG_SSI_SRX1: + case REG_SSI_SISR: + case REG_SSI_SACADD: + case REG_SSI_SACDAT: + case REG_SSI_SATAG: return true; default: return false; @@ -153,9 +163,9 @@ static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg) 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: + case REG_SSI_SRX0: + case REG_SSI_SRX1: + case REG_SSI_SACCST: return false; default: return true; @@ -163,12 +173,12 @@ static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) } static const struct regmap_config fsl_ssi_regconfig = { - .max_register = CCSR_SSI_SACCDIS, + .max_register = REG_SSI_SACCDIS, .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .val_format_endian = REGMAP_ENDIAN_NATIVE, - .num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1, + .num_reg_defaults_raw = REG_SSI_SACCDIS / sizeof(uint32_t) + 1, .readable_reg = fsl_ssi_readable_reg, .volatile_reg = fsl_ssi_volatile_reg, .precious_reg = fsl_ssi_precious_reg, @@ -184,78 +194,79 @@ struct fsl_ssi_soc_data { }; /** - * fsl_ssi_private: per-SSI private data + * fsl_ssi: per-SSI private data * - * @reg: Pointer to the regmap registers + * @regs: Pointer to the regmap registers * @irq: IRQ of this SSI * @cpu_dai_drv: CPU DAI driver for this device * * @dai_fmt: DAI configuration this device is currently used with - * @i2s_mode: i2s and network mode configuration of the device. Is used to - * switch between normal and i2s/network mode - * mode depending on the number of channels + * @i2s_net: I2S and Network mode configurations of SCR register * @use_dma: DMA is used or FIQ with stream filter - * @use_dual_fifo: DMA with support for both FIFOs used - * @fifo_deph: Depth of the SSI FIFOs - * @slot_width: width of each DAI slot - * @slots: number of slots - * @rxtx_reg_val: Specific register settings for receive/transmit configuration + * @use_dual_fifo: DMA with support for dual FIFO mode + * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree + * @fifo_depth: Depth of the SSI FIFOs + * @slot_width: Width of each DAI slot + * @slots: Number of slots + * @regvals: Specific RX/TX register settings * - * @clk: SSI clock - * @baudclk: SSI baud clock for master mode + * @clk: Clock source to access register + * @baudclk: Clock source to generate bit and frame-sync clocks * @baudclk_streams: Active streams that are using baudclk * + * @regcache_sfcsr: Cache sfcsr register value during suspend and resume + * @regcache_sacnt: Cache sacnt register value during suspend and resume + * * @dma_params_tx: DMA transmit parameters * @dma_params_rx: DMA receive parameters * @ssi_phys: physical address of the SSI registers * * @fiq_params: FIQ stream filtering parameters * - * @pdev: Pointer to pdev used for deprecated fsl-ssi sound card + * @pdev: Pointer to pdev when using fsl-ssi as sound card (ppc only) + * TODO: Should be replaced with simple-sound-card * * @dbg_stats: Debugging statistics * * @soc: SoC specific data + * @dev: Pointer to &pdev->dev + * + * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are + * @fifo_watermark or fewer words in TX fifo or + * @fifo_watermark or more empty words in RX fifo. + * @dma_maxburst: Max number of words to transfer in one go. So far, + * this is always the same as fifo_watermark. * - * @fifo_watermark: the FIFO watermark setting. Notifies DMA when - * there are @fifo_watermark or fewer words in TX fifo or - * @fifo_watermark or more empty words in RX fifo. - * @dma_maxburst: max number of words to transfer in one go. So far, - * this is always the same as fifo_watermark. + * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations */ -struct fsl_ssi_private { +struct fsl_ssi { struct regmap *regs; int irq; struct snd_soc_dai_driver cpu_dai_drv; unsigned int dai_fmt; - u8 i2s_mode; + u8 i2s_net; bool use_dma; bool use_dual_fifo; bool has_ipg_clk_name; unsigned int fifo_depth; unsigned int slot_width; unsigned int slots; - struct fsl_ssi_rxtx_reg_val rxtx_reg_val; + struct fsl_ssi_regvals regvals[2]; struct clk *clk; struct clk *baudclk; unsigned int baudclk_streams; - /* regcache for volatile regs */ u32 regcache_sfcsr; u32 regcache_sacnt; - /* DMA params */ struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; dma_addr_t ssi_phys; - /* params for non-dma FIQ stream filtered mode */ struct imx_pcm_fiq_params fiq_params; - /* Used when using fsl-ssi as sound-card. This is only used by ppc and - * should be replaced with simple-sound-card. */ struct platform_device *pdev; struct fsl_ssi_dbg dbg_stats; @@ -265,30 +276,32 @@ struct fsl_ssi_private { u32 fifo_watermark; u32 dma_maxburst; + + struct mutex ac97_reg_lock; }; /* - * imx51 and later SoCs have a slightly different IP that allows the - * SSI configuration while the SSI unit is running. - * - * More important, it is necessary on those SoCs to configure the - * sperate TX/RX DMA bits just before starting the stream - * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi - * sends any DMA requests to the SDMA unit, otherwise it is not defined - * how the SDMA unit handles the DMA request. + * SoC specific data * - * SDMA units are present on devices starting at imx35 but the imx35 - * reference manual states that the DMA bits should not be changed - * while the SSI unit is running (SSIEN). So we support the necessary - * online configuration of fsl-ssi starting at imx51. + * Notes: + * 1) SSI in earlier SoCS has critical bits in control registers that + * cannot be changed after SSI starts running -- a software reset + * (set SSIEN to 0) is required to change their values. So adding + * an offline_config flag for these SoCs. + * 2) SDMA is available since imx35. However, imx35 does not support + * DMA bits changing when SSI is running, so set offline_config. + * 3) imx51 and later versions support register configurations when + * SSI is running (SSIEN); For these versions, DMA needs to be + * configured before SSI sends DMA request to avoid an undefined + * DMA request on the SDMA side. */ static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = { .imx = false, .offline_config = true, - .sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC | - CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | - CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1, + .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC | + SSI_SISR_ROE0 | SSI_SISR_ROE1 | + SSI_SISR_TUE0 | SSI_SISR_TUE1, }; static struct fsl_ssi_soc_data fsl_ssi_imx21 = { @@ -301,16 +314,16 @@ static struct fsl_ssi_soc_data fsl_ssi_imx21 = { static struct fsl_ssi_soc_data fsl_ssi_imx35 = { .imx = true, .offline_config = true, - .sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC | - CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | - CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1, + .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC | + SSI_SISR_ROE0 | SSI_SISR_ROE1 | + SSI_SISR_TUE0 | SSI_SISR_TUE1, }; static struct fsl_ssi_soc_data fsl_ssi_imx51 = { .imx = true, .offline_config = false, - .sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | - CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1, + .sisr_write_mask = SSI_SISR_ROE0 | SSI_SISR_ROE1 | + SSI_SISR_TUE0 | SSI_SISR_TUE1, }; static const struct of_device_id fsl_ssi_ids[] = { @@ -322,108 +335,86 @@ static const struct of_device_id fsl_ssi_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_ssi_ids); -static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private) +static bool fsl_ssi_is_ac97(struct fsl_ssi *ssi) { - return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == + return (ssi->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97; } -static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private) +static bool fsl_ssi_is_i2s_master(struct fsl_ssi *ssi) { - return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == + return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFS; } -static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi_private *ssi_private) +static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi *ssi) { - return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == + return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFS; } + /** - * fsl_ssi_isr: SSI interrupt handler - * - * Although it's possible to use the interrupt handler to send and receive - * data to/from the SSI, we use the DMA instead. Programming is more - * complicated, but the performance is much better. - * - * This interrupt handler is used only to gather statistics. - * - * @irq: IRQ of the SSI device - * @dev_id: pointer to the ssi_private structure for this SSI device + * Interrupt handler to gather states */ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) { - struct fsl_ssi_private *ssi_private = dev_id; - struct regmap *regs = ssi_private->regs; + struct fsl_ssi *ssi = dev_id; + struct regmap *regs = ssi->regs; __be32 sisr; __be32 sisr2; - /* We got an interrupt, so read the status register to see what we - were interrupted for. We mask it with the Interrupt Enable register - so that we only check for events that we're interested in. - */ - regmap_read(regs, CCSR_SSI_SISR, &sisr); + regmap_read(regs, REG_SSI_SISR, &sisr); - sisr2 = sisr & ssi_private->soc->sisr_write_mask; + sisr2 = sisr & ssi->soc->sisr_write_mask; /* Clear the bits that we set */ if (sisr2) - regmap_write(regs, CCSR_SSI_SISR, sisr2); + regmap_write(regs, REG_SSI_SISR, sisr2); - fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr); + fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr); return IRQ_HANDLED; } -/* - * Enable/Disable all rx/tx config flags at once. +/** + * Enable or disable all rx/tx config flags at once */ -static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, - bool enable) +static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) { - struct regmap *regs = ssi_private->regs; - struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val; + struct regmap *regs = ssi->regs; + struct fsl_ssi_regvals *vals = ssi->regvals; if (enable) { - regmap_update_bits(regs, CCSR_SSI_SIER, - vals->rx.sier | vals->tx.sier, - vals->rx.sier | vals->tx.sier); - regmap_update_bits(regs, CCSR_SSI_SRCR, - vals->rx.srcr | vals->tx.srcr, - vals->rx.srcr | vals->tx.srcr); - regmap_update_bits(regs, CCSR_SSI_STCR, - vals->rx.stcr | vals->tx.stcr, - vals->rx.stcr | vals->tx.stcr); + regmap_update_bits(regs, REG_SSI_SIER, + vals[RX].sier | vals[TX].sier, + vals[RX].sier | vals[TX].sier); + regmap_update_bits(regs, REG_SSI_SRCR, + vals[RX].srcr | vals[TX].srcr, + vals[RX].srcr | vals[TX].srcr); + regmap_update_bits(regs, REG_SSI_STCR, + vals[RX].stcr | vals[TX].stcr, + vals[RX].stcr | vals[TX].stcr); } else { - regmap_update_bits(regs, CCSR_SSI_SRCR, - vals->rx.srcr | vals->tx.srcr, 0); - regmap_update_bits(regs, CCSR_SSI_STCR, - vals->rx.stcr | vals->tx.stcr, 0); - regmap_update_bits(regs, CCSR_SSI_SIER, - vals->rx.sier | vals->tx.sier, 0); + regmap_update_bits(regs, REG_SSI_SRCR, + vals[RX].srcr | vals[TX].srcr, 0); + regmap_update_bits(regs, REG_SSI_STCR, + vals[RX].stcr | vals[TX].stcr, 0); + regmap_update_bits(regs, REG_SSI_SIER, + vals[RX].sier | vals[TX].sier, 0); } } -/* - * Clear RX or TX FIFO to remove samples from the previous - * stream session which may be still present in the FIFO and - * may introduce bad samples and/or channel slipping. - * - * Note: The SOR is not documented in recent IMX datasheet, but - * is described in IMX51 reference manual at section 56.3.3.15. +/** + * Clear remaining data in the FIFO to avoid dirty data or channel slipping */ -static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private, - bool is_rx) +static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) { - if (is_rx) { - regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR, - CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR); - } else { - regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR, - CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR); - } + bool tx = !is_rx; + + regmap_update_bits(ssi->regs, REG_SSI_SOR, + SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); } -/* +/** * Calculate the bits that have to be disabled for the current stream that is * getting disabled. This keeps the bits enabled that are necessary for the * second stream to work if 'stream_active' is true. @@ -443,261 +434,239 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private, ((vals_disable) & \ ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active)))) -/* - * Enable/Disable a ssi configuration. You have to pass either - * ssi_private->rxtx_reg_val.rx or tx as vals parameter. +/** + * Enable or disable SSI configuration. */ -static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, - struct fsl_ssi_reg_val *vals) +static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, + struct fsl_ssi_regvals *vals) { - struct regmap *regs = ssi_private->regs; - struct fsl_ssi_reg_val *avals; + struct regmap *regs = ssi->regs; + struct fsl_ssi_regvals *avals; int nr_active_streams; - u32 scr_val; + u32 scr; int keep_active; - regmap_read(regs, CCSR_SSI_SCR, &scr_val); + regmap_read(regs, REG_SSI_SCR, &scr); - nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) + - !!(scr_val & CCSR_SSI_SCR_RE); + nr_active_streams = !!(scr & SSI_SCR_TE) + !!(scr & SSI_SCR_RE); if (nr_active_streams - 1 > 0) keep_active = 1; else keep_active = 0; - /* Find the other direction values rx or tx which we do not want to - * modify */ - if (&ssi_private->rxtx_reg_val.rx == vals) - avals = &ssi_private->rxtx_reg_val.tx; + /* Get the opposite direction to keep its values untouched */ + if (&ssi->regvals[RX] == vals) + avals = &ssi->regvals[TX]; else - avals = &ssi_private->rxtx_reg_val.rx; + avals = &ssi->regvals[RX]; - /* If vals should be disabled, start with disabling the unit */ if (!enable) { + /* + * To keep the other stream safe, exclude shared bits between + * both streams, and get safe bits to disable current stream + */ u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr, - keep_active); - regmap_update_bits(regs, CCSR_SSI_SCR, scr, 0); + keep_active); + /* Safely disable SCR register for the stream */ + regmap_update_bits(regs, REG_SSI_SCR, scr, 0); } /* - * We are running on a SoC which does not support online SSI - * reconfiguration, so we have to enable all necessary flags at once - * even if we do not use them later (capture and playback configuration) + * For cases where online configuration is not supported, + * 1) Enable all necessary bits of both streams when 1st stream starts + * even if the opposite stream will not start + * 2) Disable all remaining bits of both streams when last stream ends */ - if (ssi_private->soc->offline_config) { - if ((enable && !nr_active_streams) || - (!enable && !keep_active)) - fsl_ssi_rxtx_config(ssi_private, enable); + if (ssi->soc->offline_config) { + if ((enable && !nr_active_streams) || (!enable && !keep_active)) + fsl_ssi_rxtx_config(ssi, enable); goto config_done; } - /* - * Configure single direction units while the SSI unit is running - * (online configuration) - */ + /* Online configure single direction while SSI is running */ if (enable) { - fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE); + fsl_ssi_fifo_clear(ssi, vals->scr & SSI_SCR_RE); - regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr); - regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr); - regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); + regmap_update_bits(regs, REG_SSI_SRCR, vals->srcr, vals->srcr); + regmap_update_bits(regs, REG_SSI_STCR, vals->stcr, vals->stcr); + regmap_update_bits(regs, REG_SSI_SIER, vals->sier, vals->sier); } else { u32 sier; u32 srcr; u32 stcr; /* - * Disabling the necessary flags for one of rx/tx while the - * other stream is active is a little bit more difficult. We - * have to disable only those flags that differ between both - * streams (rx XOR tx) and that are set in the stream that is - * disabled now. Otherwise we could alter flags of the other - * stream + * To keep the other stream safe, exclude shared bits between + * both streams, and get safe bits to disable current stream */ - - /* These assignments are simply vals without bits set in avals*/ sier = fsl_ssi_disable_val(vals->sier, avals->sier, - keep_active); + keep_active); srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr, - keep_active); + keep_active); stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr, - keep_active); + keep_active); - regmap_update_bits(regs, CCSR_SSI_SRCR, srcr, 0); - regmap_update_bits(regs, CCSR_SSI_STCR, stcr, 0); - regmap_update_bits(regs, CCSR_SSI_SIER, sier, 0); + /* Safely disable other control registers for the stream */ + regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0); + regmap_update_bits(regs, REG_SSI_STCR, stcr, 0); + regmap_update_bits(regs, REG_SSI_SIER, sier, 0); } config_done: /* Enabling of subunits is done after configuration */ if (enable) { - if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) { - /* - * Be sure the Tx FIFO is filled when TE is set. - * Otherwise, there are some chances to start the - * playback with some void samples inserted first, - * generating a channel slip. - * - * First, SSIEN must be set, to let the FIFO be filled. - * - * Notes: - * - Limit this fix to the DMA case until FIQ cases can - * be tested. - * - Limit the length of the busy loop to not lock the - * system too long, even if 1-2 loops are sufficient - * in general. - */ + /* + * Start DMA before setting TE to avoid FIFO underrun + * which may cause a channel slip or a channel swap + * + * TODO: FIQ cases might also need this upon testing + */ + if (ssi->use_dma && (vals->scr & SSI_SCR_TE)) { int i; int max_loop = 100; - regmap_update_bits(regs, CCSR_SSI_SCR, - CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN); + + /* Enable SSI first to send TX DMA request */ + regmap_update_bits(regs, REG_SSI_SCR, + SSI_SCR_SSIEN, SSI_SCR_SSIEN); + + /* Busy wait until TX FIFO not empty -- DMA working */ for (i = 0; i < max_loop; i++) { u32 sfcsr; - regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr); - if (CCSR_SSI_SFCSR_TFCNT0(sfcsr)) + regmap_read(regs, REG_SSI_SFCSR, &sfcsr); + if (SSI_SFCSR_TFCNT0(sfcsr)) break; } if (i == max_loop) { - dev_err(ssi_private->dev, + dev_err(ssi->dev, "Timeout waiting TX FIFO filling\n"); } } - regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr); + /* Enable all remaining bits */ + regmap_update_bits(regs, REG_SSI_SCR, vals->scr, vals->scr); } } +static void fsl_ssi_rx_config(struct fsl_ssi *ssi, bool enable) +{ + fsl_ssi_config(ssi, enable, &ssi->regvals[RX]); +} -static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable) +static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi) { - fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx); + struct regmap *regs = ssi->regs; + + /* no SACC{ST,EN,DIS} regs on imx21-class SSI */ + if (!ssi->soc->imx21regs) { + /* Disable all channel slots */ + regmap_write(regs, REG_SSI_SACCDIS, 0xff); + /* Enable slots 3 & 4 -- PCM Playback Left & Right channels */ + regmap_write(regs, REG_SSI_SACCEN, 0x300); + } } -static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable) +static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable) { - fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx); + /* + * SACCST might be modified via AC Link by a CODEC if it sends + * extra bits in their SLOTREQ requests, which'll accidentally + * send valid data to slots other than normal playback slots. + * + * To be safe, configure SACCST right before TX starts. + */ + if (enable && fsl_ssi_is_ac97(ssi)) + fsl_ssi_tx_ac97_saccst_setup(ssi); + + fsl_ssi_config(ssi, enable, &ssi->regvals[TX]); } -/* - * Setup rx/tx register values used to enable/disable the streams. These will - * be used later in fsl_ssi_config to setup the streams without the need to - * check for all different SSI modes. +/** + * Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely */ -static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private) +static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) { - struct fsl_ssi_rxtx_reg_val *reg = &ssi_private->rxtx_reg_val; - - reg->rx.sier = CCSR_SSI_SIER_RFF0_EN; - reg->rx.srcr = CCSR_SSI_SRCR_RFEN0; - reg->rx.scr = 0; - reg->tx.sier = CCSR_SSI_SIER_TFE0_EN; - reg->tx.stcr = CCSR_SSI_STCR_TFEN0; - reg->tx.scr = 0; - - if (!fsl_ssi_is_ac97(ssi_private)) { - reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE; - reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN; - reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE; - reg->tx.sier |= CCSR_SSI_SIER_TFE0_EN; + struct fsl_ssi_regvals *vals = ssi->regvals; + + vals[RX].sier = SSI_SIER_RFF0_EN; + vals[RX].srcr = SSI_SRCR_RFEN0; + vals[RX].scr = 0; + vals[TX].sier = SSI_SIER_TFE0_EN; + vals[TX].stcr = SSI_STCR_TFEN0; + vals[TX].scr = 0; + + /* AC97 has already enabled SSIEN, RE and TE, so ignore them */ + if (!fsl_ssi_is_ac97(ssi)) { + vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE; + vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE; } - if (ssi_private->use_dma) { - reg->rx.sier |= CCSR_SSI_SIER_RDMAE; - reg->tx.sier |= CCSR_SSI_SIER_TDMAE; + if (ssi->use_dma) { + vals[RX].sier |= SSI_SIER_RDMAE; + vals[TX].sier |= SSI_SIER_TDMAE; } else { - reg->rx.sier |= CCSR_SSI_SIER_RIE; - reg->tx.sier |= CCSR_SSI_SIER_TIE; + vals[RX].sier |= SSI_SIER_RIE; + vals[TX].sier |= SSI_SIER_TIE; } - reg->rx.sier |= FSLSSI_SIER_DBG_RX_FLAGS; - reg->tx.sier |= FSLSSI_SIER_DBG_TX_FLAGS; + vals[RX].sier |= FSLSSI_SIER_DBG_RX_FLAGS; + vals[TX].sier |= FSLSSI_SIER_DBG_TX_FLAGS; } -static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) +static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi) { - struct regmap *regs = ssi_private->regs; + struct regmap *regs = ssi->regs; - /* - * Setup the clock control register - */ - regmap_write(regs, CCSR_SSI_STCCR, - CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13)); - regmap_write(regs, CCSR_SSI_SRCCR, - CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13)); + /* Setup the clock control register */ + regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13)); + regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13)); - /* - * Enable AC97 mode and startup the SSI - */ - regmap_write(regs, CCSR_SSI_SACNT, - CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV); - - /* no SACC{ST,EN,DIS} regs on imx21-class SSI */ - if (!ssi_private->soc->imx21regs) { - regmap_write(regs, CCSR_SSI_SACCDIS, 0xff); - regmap_write(regs, CCSR_SSI_SACCEN, 0x300); - } + /* Enable AC97 mode and startup the SSI */ + regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV); - /* - * Enable SSI, Transmit and Receive. AC97 has to communicate with the - * codec before a stream is started. - */ - regmap_update_bits(regs, CCSR_SSI_SCR, - CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE, - CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + /* AC97 has to communicate with codec before starting a stream */ + regmap_update_bits(regs, REG_SSI_SCR, + SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE, + SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE); - regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_WAIT(3)); + regmap_write(regs, REG_SSI_SOR, SSI_SOR_WAIT(3)); } -/** - * fsl_ssi_startup: create a new substream - * - * This is the first function called when a stream is opened. - * - * If this is the first stream open, then grab the IRQ and program most of - * the SSI registers. - */ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); int ret; - ret = clk_prepare_enable(ssi_private->clk); + ret = clk_prepare_enable(ssi->clk); if (ret) return ret; - /* When using dual fifo mode, it is safer to ensure an even period + /* + * When using dual fifo mode, it is safer to ensure an even period * size. If appearing to an odd number while DMA always starts its * task from fifo0, fifo1 would be neglected at the end of each * period. But SSI would still access fifo1 with an invalid data. */ - if (ssi_private->use_dual_fifo) + if (ssi->use_dual_fifo) snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); return 0; } -/** - * fsl_ssi_shutdown: shutdown the SSI - * - */ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = - snd_soc_dai_get_drvdata(rtd->cpu_dai); - - clk_disable_unprepare(ssi_private->clk); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); + clk_disable_unprepare(ssi->clk); } /** - * fsl_ssi_set_bclk - configure Digital Audio Interface bit clock + * Configure Digital Audio Interface bit clock * * Note: This function can be only called when using SSI as DAI master * @@ -706,12 +675,13 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, * (In 2-channel I2S Master mode, slot_width is fixed 32) */ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai, - struct snd_pcm_hw_params *hw_params) + struct snd_soc_dai *dai, + struct snd_pcm_hw_params *hw_params) { - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); - struct regmap *regs = ssi_private->regs; - int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret; + bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); + struct regmap *regs = ssi->regs; + int synchronous = ssi->cpu_dai_drv.symmetric_rates, ret; u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; unsigned long clkrate, baudrate, tmprate; unsigned int slots = params_channels(hw_params); @@ -721,29 +691,29 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, bool baudclk_is_used; /* Override slots and slot_width if being specifically set... */ - if (ssi_private->slots) - slots = ssi_private->slots; + if (ssi->slots) + slots = ssi->slots; /* ...but keep 32 bits if slots is 2 -- I2S Master mode */ - if (ssi_private->slot_width && slots != 2) - slot_width = ssi_private->slot_width; + if (ssi->slot_width && slots != 2) + slot_width = ssi->slot_width; /* Generate bit clock based on the slot number and slot width */ freq = slots * slot_width * params_rate(hw_params); /* Don't apply it to any non-baudclk circumstance */ - if (IS_ERR(ssi_private->baudclk)) + if (IS_ERR(ssi->baudclk)) return -EINVAL; /* * Hardware limitation: The bclk rate must be * never greater than 1/5 IPG clock rate */ - if (freq * 5 > clk_get_rate(ssi_private->clk)) { - dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n"); + if (freq * 5 > clk_get_rate(ssi->clk)) { + dev_err(dai->dev, "bitclk > ipgclk / 5\n"); return -EINVAL; } - baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream)); + baudclk_is_used = ssi->baudclk_streams & ~(BIT(substream->stream)); /* It should be already enough to divide clock by setting pm alone */ psr = 0; @@ -755,9 +725,9 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, tmprate = freq * factor * (i + 1); if (baudclk_is_used) - clkrate = clk_get_rate(ssi_private->baudclk); + clkrate = clk_get_rate(ssi->baudclk); else - clkrate = clk_round_rate(ssi_private->baudclk, tmprate); + clkrate = clk_round_rate(ssi->baudclk, tmprate); clkrate /= factor; afreq = clkrate / (i + 1); @@ -788,24 +758,22 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, /* No proper pm found if it is still remaining the initial value */ if (pm == 999) { - dev_err(cpu_dai->dev, "failed to handle the required sysclk\n"); + dev_err(dai->dev, "failed to handle the required sysclk\n"); return -EINVAL; } - stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) | - (psr ? CCSR_SSI_SxCCR_PSR : 0); - mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | - CCSR_SSI_SxCCR_PSR; + stccr = SSI_SxCCR_PM(pm + 1) | (div2 ? SSI_SxCCR_DIV2 : 0) | + (psr ? SSI_SxCCR_PSR : 0); + mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || synchronous) - regmap_update_bits(regs, CCSR_SSI_STCCR, mask, stccr); - else - regmap_update_bits(regs, CCSR_SSI_SRCCR, mask, stccr); + /* STCCR is used for RX in synchronous mode */ + tx2 = tx || synchronous; + regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr); if (!baudclk_is_used) { - ret = clk_set_rate(ssi_private->baudclk, baudrate); + ret = clk_set_rate(ssi->baudclk, baudrate); if (ret) { - dev_err(cpu_dai->dev, "failed to set baudclk rate\n"); + dev_err(dai->dev, "failed to set baudclk rate\n"); return -EINVAL; } } @@ -814,185 +782,165 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, } /** - * fsl_ssi_hw_params - program the sample size - * - * Most of the SSI registers have been programmed in the startup function, - * but the word length must be programmed here. Unfortunately, programming - * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can - * cause a problem with supporting simultaneous playback and capture. If - * the SSI is already playing a stream, then that stream may be temporarily - * stopped when you start capture. + * Configure SSI based on PCM hardware parameters * - * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the - * clock master. + * Notes: + * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily + * disabled on offline_config SoCs. Even for online configurable SoCs + * running in synchronous mode (both TX and RX use STCCR), it is not + * safe to re-configure them when both two streams start running. + * 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the + * fsl_ssi_set_bclk() if SSI is the DAI clock master. */ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *dai) { - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); - struct regmap *regs = ssi_private->regs; + bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); + struct regmap *regs = ssi->regs; unsigned int channels = params_channels(hw_params); unsigned int sample_size = params_width(hw_params); - u32 wl = CCSR_SSI_SxCCR_WL(sample_size); + u32 wl = SSI_SxCCR_WL(sample_size); int ret; - u32 scr_val; + u32 scr; int enabled; - regmap_read(regs, CCSR_SSI_SCR, &scr_val); - enabled = scr_val & CCSR_SSI_SCR_SSIEN; + regmap_read(regs, REG_SSI_SCR, &scr); + enabled = scr & SSI_SCR_SSIEN; /* - * If we're in synchronous mode, and the SSI is already enabled, - * then STCCR is already set properly. + * SSI is properly configured if it is enabled and running in + * the synchronous mode; Note that AC97 mode is an exception + * that should set separate configurations for STCCR and SRCCR + * despite running in the synchronous mode. */ - if (enabled && ssi_private->cpu_dai_drv.symmetric_rates) + if (enabled && ssi->cpu_dai_drv.symmetric_rates) return 0; - if (fsl_ssi_is_i2s_master(ssi_private)) { - ret = fsl_ssi_set_bclk(substream, cpu_dai, hw_params); + if (fsl_ssi_is_i2s_master(ssi)) { + ret = fsl_ssi_set_bclk(substream, dai, hw_params); if (ret) return ret; /* Do not enable the clock if it is already enabled */ - if (!(ssi_private->baudclk_streams & BIT(substream->stream))) { - ret = clk_prepare_enable(ssi_private->baudclk); + if (!(ssi->baudclk_streams & BIT(substream->stream))) { + ret = clk_prepare_enable(ssi->baudclk); if (ret) return ret; - ssi_private->baudclk_streams |= BIT(substream->stream); + ssi->baudclk_streams |= BIT(substream->stream); } } - if (!fsl_ssi_is_ac97(ssi_private)) { - u8 i2smode; - /* - * Switch to normal net mode in order to have a frame sync - * signal every 32 bits instead of 16 bits - */ - if (fsl_ssi_is_i2s_cbm_cfs(ssi_private) && sample_size == 16) - i2smode = CCSR_SSI_SCR_I2S_MODE_NORMAL | - CCSR_SSI_SCR_NET; + if (!fsl_ssi_is_ac97(ssi)) { + u8 i2s_net; + /* Normal + Network mode to send 16-bit data in 32-bit frames */ + if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16) + i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; else - i2smode = ssi_private->i2s_mode; + i2s_net = ssi->i2s_net; - regmap_update_bits(regs, CCSR_SSI_SCR, - CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK, - channels == 1 ? 0 : i2smode); + regmap_update_bits(regs, REG_SSI_SCR, + SSI_SCR_I2S_NET_MASK, + channels == 1 ? 0 : i2s_net); } - /* - * FIXME: The documentation says that SxCCR[WL] should not be - * modified while the SSI is enabled. The only time this can - * happen is if we're trying to do simultaneous playback and - * capture in asynchronous mode. Unfortunately, I have been enable - * to get that to work at all on the P1022DS. Therefore, we don't - * bother to disable/enable the SSI when setting SxCCR[WL], because - * the SSI will stop anyway. Maybe one day, this will get fixed. - */ - /* In synchronous mode, the SSI uses STCCR for capture */ - if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || - ssi_private->cpu_dai_drv.symmetric_rates) - regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_WL_MASK, - wl); - else - regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_WL_MASK, - wl); + tx2 = tx || ssi->cpu_dai_drv.symmetric_rates; + regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl); return 0; } static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) + struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = - snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); - if (fsl_ssi_is_i2s_master(ssi_private) && - ssi_private->baudclk_streams & BIT(substream->stream)) { - clk_disable_unprepare(ssi_private->baudclk); - ssi_private->baudclk_streams &= ~BIT(substream->stream); + if (fsl_ssi_is_i2s_master(ssi) && + ssi->baudclk_streams & BIT(substream->stream)) { + clk_disable_unprepare(ssi->baudclk); + ssi->baudclk_streams &= ~BIT(substream->stream); } return 0; } static int _fsl_ssi_set_dai_fmt(struct device *dev, - struct fsl_ssi_private *ssi_private, - unsigned int fmt) + struct fsl_ssi *ssi, unsigned int fmt) { - struct regmap *regs = ssi_private->regs; + struct regmap *regs = ssi->regs; u32 strcr = 0, stcr, srcr, scr, mask; u8 wm; - ssi_private->dai_fmt = fmt; + ssi->dai_fmt = fmt; - if (fsl_ssi_is_i2s_master(ssi_private) && IS_ERR(ssi_private->baudclk)) { - dev_err(dev, "baudclk is missing which is necessary for master mode\n"); + if (fsl_ssi_is_i2s_master(ssi) && IS_ERR(ssi->baudclk)) { + dev_err(dev, "missing baudclk for master mode\n"); return -EINVAL; } - fsl_ssi_setup_reg_vals(ssi_private); + fsl_ssi_setup_regvals(ssi); - regmap_read(regs, CCSR_SSI_SCR, &scr); - scr &= ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK); - scr |= CCSR_SSI_SCR_SYNC_TX_FS; + regmap_read(regs, REG_SSI_SCR, &scr); + scr &= ~(SSI_SCR_SYN | SSI_SCR_I2S_MODE_MASK); + /* Synchronize frame sync clock for TE to avoid data slipping */ + scr |= SSI_SCR_SYNC_TX_FS; - mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR | - CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL | - CCSR_SSI_STCR_TEFS; - regmap_read(regs, CCSR_SSI_STCR, &stcr); - regmap_read(regs, CCSR_SSI_SRCR, &srcr); + mask = SSI_STCR_TXBIT0 | SSI_STCR_TFDIR | SSI_STCR_TXDIR | + SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TFSL | SSI_STCR_TEFS; + regmap_read(regs, REG_SSI_STCR, &stcr); + regmap_read(regs, REG_SSI_SRCR, &srcr); stcr &= ~mask; srcr &= ~mask; - ssi_private->i2s_mode = CCSR_SSI_SCR_NET; + /* Use Network mode as default */ + ssi->i2s_net = SSI_SCR_NET; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - regmap_update_bits(regs, CCSR_SSI_STCCR, - CCSR_SSI_SxCCR_DC_MASK, - CCSR_SSI_SxCCR_DC(2)); - regmap_update_bits(regs, CCSR_SSI_SRCCR, - CCSR_SSI_SxCCR_DC_MASK, - CCSR_SSI_SxCCR_DC(2)); + regmap_update_bits(regs, REG_SSI_STCCR, + SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); + regmap_update_bits(regs, REG_SSI_SRCCR, + SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBS_CFS: - ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER; + ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER; break; case SND_SOC_DAIFMT_CBM_CFM: - ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE; + ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE; break; default: return -EINVAL; } /* Data on rising edge of bclk, frame low, 1clk before data */ - strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP | - CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS; + strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | + SSI_STCR_TXBIT0 | SSI_STCR_TEFS; break; case SND_SOC_DAIFMT_LEFT_J: /* Data on rising edge of bclk, frame high */ - strcr |= CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TSCKP; + strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_DSP_A: /* Data on rising edge of bclk, frame high, 1clk before data */ - strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP | - CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS; + strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | + SSI_STCR_TXBIT0 | SSI_STCR_TEFS; break; case SND_SOC_DAIFMT_DSP_B: /* Data on rising edge of bclk, frame high */ - strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP | - CCSR_SSI_STCR_TXBIT0; + strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TXBIT0; break; case SND_SOC_DAIFMT_AC97: - ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL; + /* Data on falling edge of bclk, frame high, 1clk before data */ + ssi->i2s_net |= SSI_SCR_I2S_MODE_NORMAL; break; default: return -EINVAL; } - scr |= ssi_private->i2s_mode; + scr |= ssi->i2s_net; /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -1001,16 +949,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, break; case SND_SOC_DAIFMT_IB_NF: /* Invert bit clock */ - strcr ^= CCSR_SSI_STCR_TSCKP; + strcr ^= SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_NB_IF: /* Invert frame clock */ - strcr ^= CCSR_SSI_STCR_TFSI; + strcr ^= SSI_STCR_TFSI; break; case SND_SOC_DAIFMT_IB_IF: /* Invert both clocks */ - strcr ^= CCSR_SSI_STCR_TSCKP; - strcr ^= CCSR_SSI_STCR_TFSI; + strcr ^= SSI_STCR_TSCKP; + strcr ^= SSI_STCR_TFSI; break; default: return -EINVAL; @@ -1019,123 +967,122 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: - strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR; - scr |= CCSR_SSI_SCR_SYS_CLK_EN; + /* Output bit and frame sync clocks */ + strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; + scr |= SSI_SCR_SYS_CLK_EN; break; case SND_SOC_DAIFMT_CBM_CFM: - scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; + /* Input bit or frame sync clocks */ + scr &= ~SSI_SCR_SYS_CLK_EN; break; case SND_SOC_DAIFMT_CBM_CFS: - strcr &= ~CCSR_SSI_STCR_TXDIR; - strcr |= CCSR_SSI_STCR_TFDIR; - scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; + /* Input bit clock but output frame sync clock */ + strcr &= ~SSI_STCR_TXDIR; + strcr |= SSI_STCR_TFDIR; + scr &= ~SSI_SCR_SYS_CLK_EN; break; default: - if (!fsl_ssi_is_ac97(ssi_private)) + if (!fsl_ssi_is_ac97(ssi)) return -EINVAL; } stcr |= strcr; srcr |= strcr; - if (ssi_private->cpu_dai_drv.symmetric_rates - || fsl_ssi_is_ac97(ssi_private)) { - /* Need to clear RXDIR when using SYNC or AC97 mode */ - srcr &= ~CCSR_SSI_SRCR_RXDIR; - scr |= CCSR_SSI_SCR_SYN; + /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */ + if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) { + srcr &= ~SSI_SRCR_RXDIR; + scr |= SSI_SCR_SYN; } - regmap_write(regs, CCSR_SSI_STCR, stcr); - regmap_write(regs, CCSR_SSI_SRCR, srcr); - regmap_write(regs, CCSR_SSI_SCR, scr); + regmap_write(regs, REG_SSI_STCR, stcr); + regmap_write(regs, REG_SSI_SRCR, srcr); + regmap_write(regs, REG_SSI_SCR, scr); - wm = ssi_private->fifo_watermark; + wm = ssi->fifo_watermark; - regmap_write(regs, CCSR_SSI_SFCSR, - CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | - CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm)); + regmap_write(regs, REG_SSI_SFCSR, + SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) | + SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm)); - if (ssi_private->use_dual_fifo) { - regmap_update_bits(regs, CCSR_SSI_SRCR, CCSR_SSI_SRCR_RFEN1, - CCSR_SSI_SRCR_RFEN1); - regmap_update_bits(regs, CCSR_SSI_STCR, CCSR_SSI_STCR_TFEN1, - CCSR_SSI_STCR_TFEN1); - regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_TCH_EN, - CCSR_SSI_SCR_TCH_EN); + if (ssi->use_dual_fifo) { + regmap_update_bits(regs, REG_SSI_SRCR, + SSI_SRCR_RFEN1, SSI_SRCR_RFEN1); + regmap_update_bits(regs, REG_SSI_STCR, + SSI_STCR_TFEN1, SSI_STCR_TFEN1); + regmap_update_bits(regs, REG_SSI_SCR, + SSI_SCR_TCH_EN, SSI_SCR_TCH_EN); } if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) - fsl_ssi_setup_ac97(ssi_private); + fsl_ssi_setup_ac97(ssi); return 0; - } /** - * fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format. + * Configure Digital Audio Interface (DAI) Format */ -static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); + + /* AC97 configured DAIFMT earlier in the probe() */ + if (fsl_ssi_is_ac97(ssi)) + return 0; - return _fsl_ssi_set_dai_fmt(cpu_dai->dev, ssi_private, fmt); + return _fsl_ssi_set_dai_fmt(dai->dev, ssi, fmt); } /** - * fsl_ssi_set_dai_tdm_slot - set TDM slot number - * - * Note: This function can be only called when using SSI as DAI master + * Set TDM slot number and slot width */ -static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, - u32 rx_mask, int slots, int slot_width) +static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) { - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); - struct regmap *regs = ssi_private->regs; + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); + struct regmap *regs = ssi->regs; u32 val; /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */ if (slot_width & 1 || slot_width < 8 || slot_width > 24) { - dev_err(cpu_dai->dev, "invalid slot width: %d\n", slot_width); + dev_err(dai->dev, "invalid slot width: %d\n", slot_width); return -EINVAL; } /* The slot number should be >= 2 if using Network mode or I2S mode */ - regmap_read(regs, CCSR_SSI_SCR, &val); - val &= CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET; + regmap_read(regs, REG_SSI_SCR, &val); + val &= SSI_SCR_I2S_MODE_MASK | SSI_SCR_NET; if (val && slots < 2) { - dev_err(cpu_dai->dev, "slot number should be >= 2 in I2S or NET\n"); + dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n"); return -EINVAL; } - regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_DC_MASK, - CCSR_SSI_SxCCR_DC(slots)); - regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_DC_MASK, - CCSR_SSI_SxCCR_DC(slots)); + regmap_update_bits(regs, REG_SSI_STCCR, + SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); + regmap_update_bits(regs, REG_SSI_SRCCR, + SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); - /* The register SxMSKs needs SSI to provide essential clock due to - * hardware design. So we here temporarily enable SSI to set them. - */ - regmap_read(regs, CCSR_SSI_SCR, &val); - val &= CCSR_SSI_SCR_SSIEN; - regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, - CCSR_SSI_SCR_SSIEN); + /* Save SSIEN bit of the SCR register */ + regmap_read(regs, REG_SSI_SCR, &val); + val &= SSI_SCR_SSIEN; + /* Temporarily enable SSI to allow SxMSKs to be configurable */ + regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN); - regmap_write(regs, CCSR_SSI_STMSK, ~tx_mask); - regmap_write(regs, CCSR_SSI_SRMSK, ~rx_mask); + regmap_write(regs, REG_SSI_STMSK, ~tx_mask); + regmap_write(regs, REG_SSI_SRMSK, ~rx_mask); - regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val); + /* Restore the value of SSIEN bit */ + regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, val); - ssi_private->slot_width = slot_width; - ssi_private->slots = slots; + ssi->slot_width = slot_width; + ssi->slots = slots; return 0; } /** - * fsl_ssi_trigger: start and stop the DMA transfer. - * - * This function is called by ALSA to start, stop, pause, and resume the DMA - * transfer of data. + * Start or stop SSI and corresponding DMA transaction. * * The DMA channel is in external master start and pause mode, which * means the SSI completely controls the flow of data. @@ -1144,37 +1091,38 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); - struct regmap *regs = ssi_private->regs; + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct regmap *regs = ssi->regs; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fsl_ssi_tx_config(ssi_private, true); + fsl_ssi_tx_config(ssi, true); else - fsl_ssi_rx_config(ssi_private, true); + fsl_ssi_rx_config(ssi, true); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fsl_ssi_tx_config(ssi_private, false); + fsl_ssi_tx_config(ssi, false); else - fsl_ssi_rx_config(ssi_private, false); + fsl_ssi_rx_config(ssi, false); break; default: return -EINVAL; } - if (fsl_ssi_is_ac97(ssi_private)) { + /* Clear corresponding FIFO */ + if (fsl_ssi_is_ac97(ssi)) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_TX_CLR); + regmap_write(regs, REG_SSI_SOR, SSI_SOR_TX_CLR); else - regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_RX_CLR); + regmap_write(regs, REG_SSI_SOR, SSI_SOR_RX_CLR); } return 0; @@ -1182,27 +1130,26 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) { - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai); + struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); - if (ssi_private->soc->imx && ssi_private->use_dma) { - dai->playback_dma_data = &ssi_private->dma_params_tx; - dai->capture_dma_data = &ssi_private->dma_params_rx; + if (ssi->soc->imx && ssi->use_dma) { + dai->playback_dma_data = &ssi->dma_params_tx; + dai->capture_dma_data = &ssi->dma_params_rx; } return 0; } static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { - .startup = fsl_ssi_startup, - .shutdown = fsl_ssi_shutdown, - .hw_params = fsl_ssi_hw_params, - .hw_free = fsl_ssi_hw_free, - .set_fmt = fsl_ssi_set_dai_fmt, - .set_tdm_slot = fsl_ssi_set_dai_tdm_slot, - .trigger = fsl_ssi_trigger, + .startup = fsl_ssi_startup, + .shutdown = fsl_ssi_shutdown, + .hw_params = fsl_ssi_hw_params, + .hw_free = fsl_ssi_hw_free, + .set_fmt = fsl_ssi_set_dai_fmt, + .set_tdm_slot = fsl_ssi_set_dai_tdm_slot, + .trigger = fsl_ssi_trigger, }; -/* Template for the CPU dai driver structure */ static struct snd_soc_dai_driver fsl_ssi_dai_template = { .probe = fsl_ssi_dai_probe, .playback = { @@ -1223,7 +1170,7 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = { }; static const struct snd_soc_component_driver fsl_ssi_component = { - .name = "fsl-ssi", + .name = "fsl-ssi", }; static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { @@ -1234,23 +1181,23 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20, }, .capture = { .stream_name = "AC97 Capture", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + /* 16-bit capture is broken (errata ERR003778) */ + .formats = SNDRV_PCM_FMTBIT_S20, }, .ops = &fsl_ssi_dai_ops, }; - -static struct fsl_ssi_private *fsl_ac97_data; +static struct fsl_ssi *fsl_ac97_data; static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) + unsigned short val) { struct regmap *regs = fsl_ac97_data->regs; unsigned int lreg; @@ -1260,61 +1207,68 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, if (reg > 0x7f) return; + mutex_lock(&fsl_ac97_data->ac97_reg_lock); + ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { pr_err("ac97 write clk_prepare_enable failed: %d\n", ret); - return; + goto ret_unlock; } lreg = reg << 12; - regmap_write(regs, CCSR_SSI_SACADD, lreg); + regmap_write(regs, REG_SSI_SACADD, lreg); lval = val << 4; - regmap_write(regs, CCSR_SSI_SACDAT, lval); + regmap_write(regs, REG_SSI_SACDAT, lval); - regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK, - CCSR_SSI_SACNT_WR); + regmap_update_bits(regs, REG_SSI_SACNT, + SSI_SACNT_RDWR_MASK, SSI_SACNT_WR); udelay(100); clk_disable_unprepare(fsl_ac97_data->clk); + +ret_unlock: + mutex_unlock(&fsl_ac97_data->ac97_reg_lock); } static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, - unsigned short reg) + unsigned short reg) { struct regmap *regs = fsl_ac97_data->regs; - - unsigned short val = -1; + unsigned short val = 0; u32 reg_val; unsigned int lreg; int ret; + mutex_lock(&fsl_ac97_data->ac97_reg_lock); + ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { - pr_err("ac97 read clk_prepare_enable failed: %d\n", - ret); - return -1; + pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); + goto ret_unlock; } lreg = (reg & 0x7f) << 12; - regmap_write(regs, CCSR_SSI_SACADD, lreg); - regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK, - CCSR_SSI_SACNT_RD); + regmap_write(regs, REG_SSI_SACADD, lreg); + regmap_update_bits(regs, REG_SSI_SACNT, + SSI_SACNT_RDWR_MASK, SSI_SACNT_RD); udelay(100); - regmap_read(regs, CCSR_SSI_SACDAT, ®_val); + regmap_read(regs, REG_SSI_SACDAT, ®_val); val = (reg_val >> 4) & 0xffff; clk_disable_unprepare(fsl_ac97_data->clk); +ret_unlock: + mutex_unlock(&fsl_ac97_data->ac97_reg_lock); return val; } static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { - .read = fsl_ssi_ac97_read, - .write = fsl_ssi_ac97_write, + .read = fsl_ssi_ac97_read, + .write = fsl_ssi_ac97_write, }; /** @@ -1329,70 +1283,67 @@ static void make_lowercase(char *s) } static int fsl_ssi_imx_probe(struct platform_device *pdev, - struct fsl_ssi_private *ssi_private, void __iomem *iomem) + struct fsl_ssi *ssi, void __iomem *iomem) { struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; u32 dmas[4]; int ret; - if (ssi_private->has_ipg_clk_name) - ssi_private->clk = devm_clk_get(&pdev->dev, "ipg"); + /* Backward compatible for a DT without ipg clock name assigned */ + if (ssi->has_ipg_clk_name) + ssi->clk = devm_clk_get(dev, "ipg"); else - ssi_private->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(ssi_private->clk)) { - ret = PTR_ERR(ssi_private->clk); - dev_err(&pdev->dev, "could not get clock: %d\n", ret); + ssi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ssi->clk)) { + ret = PTR_ERR(ssi->clk); + dev_err(dev, "failed to get clock: %d\n", ret); return ret; } - if (!ssi_private->has_ipg_clk_name) { - ret = clk_prepare_enable(ssi_private->clk); + /* Enable the clock since regmap will not handle it in this case */ + if (!ssi->has_ipg_clk_name) { + ret = clk_prepare_enable(ssi->clk); if (ret) { - dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); + dev_err(dev, "clk_prepare_enable failed: %d\n", ret); return ret; } } - /* For those SLAVE implementations, we ignore non-baudclk cases - * and, instead, abandon MASTER mode that needs baud clock. - */ - ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud"); - if (IS_ERR(ssi_private->baudclk)) - dev_dbg(&pdev->dev, "could not get baud clock: %ld\n", - PTR_ERR(ssi_private->baudclk)); + /* Do not error out for slave cases that live without a baud clock */ + ssi->baudclk = devm_clk_get(dev, "baud"); + if (IS_ERR(ssi->baudclk)) + dev_dbg(dev, "failed to get baud clock: %ld\n", + PTR_ERR(ssi->baudclk)); - ssi_private->dma_params_tx.maxburst = ssi_private->dma_maxburst; - ssi_private->dma_params_rx.maxburst = ssi_private->dma_maxburst; - ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0; - ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0; + ssi->dma_params_tx.maxburst = ssi->dma_maxburst; + ssi->dma_params_rx.maxburst = ssi->dma_maxburst; + ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0; + ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0; + /* Set to dual FIFO mode according to the SDMA sciprt */ ret = of_property_read_u32_array(np, "dmas", dmas, 4); - if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) { - ssi_private->use_dual_fifo = true; - /* When using dual fifo mode, we need to keep watermark - * as even numbers due to dma script limitation. + if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) { + ssi->use_dual_fifo = true; + /* + * Use even numbers to avoid channel swap due to SDMA + * script design */ - ssi_private->dma_params_tx.maxburst &= ~0x1; - ssi_private->dma_params_rx.maxburst &= ~0x1; + ssi->dma_params_tx.maxburst &= ~0x1; + ssi->dma_params_rx.maxburst &= ~0x1; } - if (!ssi_private->use_dma) { - + if (!ssi->use_dma) { /* - * Some boards use an incompatible codec. To get it - * working, we are using imx-fiq-pcm-audio, that - * can handle those codecs. DMA is not possible in this - * situation. + * Some boards use an incompatible codec. Use imx-fiq-pcm-audio + * to get it working, as DMA is not possible in this situation. */ + ssi->fiq_params.irq = ssi->irq; + ssi->fiq_params.base = iomem; + ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; + ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; - ssi_private->fiq_params.irq = ssi_private->irq; - ssi_private->fiq_params.base = iomem; - ssi_private->fiq_params.dma_params_rx = - &ssi_private->dma_params_rx; - ssi_private->fiq_params.dma_params_tx = - &ssi_private->dma_params_tx; - - ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params); + ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params); if (ret) goto error_pcm; } else { @@ -1404,26 +1355,26 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, return 0; error_pcm: + if (!ssi->has_ipg_clk_name) + clk_disable_unprepare(ssi->clk); - if (!ssi_private->has_ipg_clk_name) - clk_disable_unprepare(ssi_private->clk); return ret; } -static void fsl_ssi_imx_clean(struct platform_device *pdev, - struct fsl_ssi_private *ssi_private) +static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi) { - if (!ssi_private->use_dma) + if (!ssi->use_dma) imx_pcm_fiq_exit(pdev); - if (!ssi_private->has_ipg_clk_name) - clk_disable_unprepare(ssi_private->clk); + if (!ssi->has_ipg_clk_name) + clk_disable_unprepare(ssi->clk); } static int fsl_ssi_probe(struct platform_device *pdev) { - struct fsl_ssi_private *ssi_private; + struct fsl_ssi *ssi; int ret = 0; struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; const struct of_device_id *of_id; const char *p, *sprop; const uint32_t *iprop; @@ -1432,182 +1383,159 @@ static int fsl_ssi_probe(struct platform_device *pdev) char name[64]; struct regmap_config regconfig = fsl_ssi_regconfig; - of_id = of_match_device(fsl_ssi_ids, &pdev->dev); + of_id = of_match_device(fsl_ssi_ids, dev); if (!of_id || !of_id->data) return -EINVAL; - ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private), - GFP_KERNEL); - if (!ssi_private) + ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL); + if (!ssi) return -ENOMEM; - ssi_private->soc = of_id->data; - ssi_private->dev = &pdev->dev; + ssi->soc = of_id->data; + ssi->dev = dev; + /* Check if being used in AC97 mode */ sprop = of_get_property(np, "fsl,mode", NULL); if (sprop) { if (!strcmp(sprop, "ac97-slave")) - ssi_private->dai_fmt = SND_SOC_DAIFMT_AC97; + ssi->dai_fmt = SND_SOC_DAIFMT_AC97; } - ssi_private->use_dma = !of_property_read_bool(np, - "fsl,fiq-stream-filter"); - - if (fsl_ssi_is_ac97(ssi_private)) { - memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai, - sizeof(fsl_ssi_ac97_dai)); - - fsl_ac97_data = ssi_private; + /* Select DMA or FIQ */ + ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter"); - ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); - if (ret) { - dev_err(&pdev->dev, "could not set AC'97 ops\n"); - return ret; - } + if (fsl_ssi_is_ac97(ssi)) { + memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai, + sizeof(fsl_ssi_ac97_dai)); + fsl_ac97_data = ssi; } else { - /* Initialize this copy of the CPU DAI driver structure */ - memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, + memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template, sizeof(fsl_ssi_dai_template)); } - ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev); + ssi->cpu_dai_drv.name = dev_name(dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iomem = devm_ioremap_resource(&pdev->dev, res); + iomem = devm_ioremap_resource(dev, res); if (IS_ERR(iomem)) return PTR_ERR(iomem); - ssi_private->ssi_phys = res->start; + ssi->ssi_phys = res->start; - if (ssi_private->soc->imx21regs) { - /* - * According to datasheet imx21-class SSI - * don't have SACC{ST,EN,DIS} regs. - */ - regconfig.max_register = CCSR_SSI_SRMSK; + if (ssi->soc->imx21regs) { + /* No SACC{ST,EN,DIS} regs in imx21-class SSI */ + regconfig.max_register = REG_SSI_SRMSK; regconfig.num_reg_defaults_raw = - CCSR_SSI_SRMSK / sizeof(uint32_t) + 1; + REG_SSI_SRMSK / sizeof(uint32_t) + 1; } ret = of_property_match_string(np, "clock-names", "ipg"); if (ret < 0) { - ssi_private->has_ipg_clk_name = false; - ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem, - ®config); + ssi->has_ipg_clk_name = false; + ssi->regs = devm_regmap_init_mmio(dev, iomem, ®config); } else { - ssi_private->has_ipg_clk_name = true; - ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev, - "ipg", iomem, ®config); + ssi->has_ipg_clk_name = true; + ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem, + ®config); } - if (IS_ERR(ssi_private->regs)) { - dev_err(&pdev->dev, "Failed to init register map\n"); - return PTR_ERR(ssi_private->regs); + if (IS_ERR(ssi->regs)) { + dev_err(dev, "failed to init register map\n"); + return PTR_ERR(ssi->regs); } - ssi_private->irq = platform_get_irq(pdev, 0); - if (ssi_private->irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); - return ssi_private->irq; + ssi->irq = platform_get_irq(pdev, 0); + if (ssi->irq < 0) { + dev_err(dev, "no irq for node %s\n", pdev->name); + return ssi->irq; } - /* Are the RX and the TX clocks locked? */ + /* Set software limitations for synchronous mode */ if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { - if (!fsl_ssi_is_ac97(ssi_private)) - ssi_private->cpu_dai_drv.symmetric_rates = 1; + if (!fsl_ssi_is_ac97(ssi)) { + ssi->cpu_dai_drv.symmetric_rates = 1; + ssi->cpu_dai_drv.symmetric_samplebits = 1; + } - ssi_private->cpu_dai_drv.symmetric_channels = 1; - ssi_private->cpu_dai_drv.symmetric_samplebits = 1; + ssi->cpu_dai_drv.symmetric_channels = 1; } - /* Determine the FIFO depth. */ + /* Fetch FIFO depth; Set to 8 for older DT without this property */ iprop = of_get_property(np, "fsl,fifo-depth", NULL); if (iprop) - ssi_private->fifo_depth = be32_to_cpup(iprop); + ssi->fifo_depth = be32_to_cpup(iprop); else - /* Older 8610 DTs didn't have the fifo-depth property */ - ssi_private->fifo_depth = 8; + ssi->fifo_depth = 8; /* - * Set the watermark for transmit FIFO 0 and receive FIFO 0. We don't - * use FIFO 1 but set the watermark appropriately nontheless. - * We program the transmit water to signal a DMA transfer - * if there are N elements left in the FIFO. For chips with 15-deep - * FIFOs, set watermark to 8. This allows the SSI to operate at a - * high data rate without channel slipping. Behavior is unchanged - * for the older chips with a fifo depth of only 8. A value of 4 - * might be appropriate for the older chips, but is left at - * fifo_depth-2 until sombody has a chance to test. + * Configure TX and RX DMA watermarks -- when to send a DMA request * - * We set the watermark on the same level as the DMA burstsize. For - * fiq it is probably better to use the biggest possible watermark - * size. + * Values should be tested to avoid FIFO under/over run. Set maxburst + * to fifo_watermark to maxiumize DMA transaction to reduce overhead. */ - switch (ssi_private->fifo_depth) { + switch (ssi->fifo_depth) { case 15: /* - * 2 samples is not enough when running at high data - * rates (like 48kHz @ 16 bits/channel, 16 channels) - * 8 seems to split things evenly and leave enough time - * for the DMA to fill the FIFO before it's over/under - * run. + * Set to 8 as a balanced configuration -- When TX FIFO has 8 + * empty slots, send a DMA request to fill these 8 slots. The + * remaining 7 slots should be able to allow DMA to finish the + * transaction before TX FIFO underruns; Same applies to RX. + * + * Tested with cases running at 48kHz @ 16 bits x 16 channels */ - ssi_private->fifo_watermark = 8; - ssi_private->dma_maxburst = 8; + ssi->fifo_watermark = 8; + ssi->dma_maxburst = 8; break; case 8: default: - /* - * maintain old behavior for older chips. - * Keeping it the same because I don't have an older - * board to test with. - * I suspect this could be changed to be something to - * leave some more space in the fifo. - */ - ssi_private->fifo_watermark = ssi_private->fifo_depth - 2; - ssi_private->dma_maxburst = ssi_private->fifo_depth - 2; + /* Safely use old watermark configurations for older chips */ + ssi->fifo_watermark = ssi->fifo_depth - 2; + ssi->dma_maxburst = ssi->fifo_depth - 2; break; } - dev_set_drvdata(&pdev->dev, ssi_private); + dev_set_drvdata(dev, ssi); - if (ssi_private->soc->imx) { - ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem); + if (ssi->soc->imx) { + ret = fsl_ssi_imx_probe(pdev, ssi, iomem); if (ret) return ret; } - ret = devm_snd_soc_register_component(&pdev->dev, &fsl_ssi_component, - &ssi_private->cpu_dai_drv, 1); + if (fsl_ssi_is_ac97(ssi)) { + mutex_init(&ssi->ac97_reg_lock); + ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); + if (ret) { + dev_err(dev, "failed to set AC'97 ops\n"); + goto error_ac97_ops; + } + } + + ret = devm_snd_soc_register_component(dev, &fsl_ssi_component, + &ssi->cpu_dai_drv, 1); if (ret) { - dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); + dev_err(dev, "failed to register DAI: %d\n", ret); goto error_asoc_register; } - if (ssi_private->use_dma) { - ret = devm_request_irq(&pdev->dev, ssi_private->irq, - fsl_ssi_isr, 0, dev_name(&pdev->dev), - ssi_private); + if (ssi->use_dma) { + ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0, + dev_name(dev), ssi); if (ret < 0) { - dev_err(&pdev->dev, "could not claim irq %u\n", - ssi_private->irq); + dev_err(dev, "failed to claim irq %u\n", ssi->irq); goto error_asoc_register; } } - ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev); + ret = fsl_ssi_debugfs_create(&ssi->dbg_stats, dev); if (ret) goto error_asoc_register; - /* - * If codec-handle property is missing from SSI node, we assume - * that the machine driver uses new binding which does not require - * SSI driver to trigger machine driver's probe. - */ + /* Bypass it if using newer DT bindings of ASoC machine drivers */ if (!of_get_property(np, "codec-handle", NULL)) goto done; - /* Trigger the machine driver's probe function. The platform driver - * name of the machine driver is taken from /compatible property of the - * device tree. We also pass the address of the CPU DAI driver - * structure. + /* + * Backward compatible for older bindings by manually triggering the + * machine driver's probe(). Use /compatible property, including the + * address of CPU DAI driver structure, as the name of machine driver. */ sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL); /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */ @@ -1617,34 +1545,31 @@ static int fsl_ssi_probe(struct platform_device *pdev) snprintf(name, sizeof(name), "snd-soc-%s", sprop); make_lowercase(name); - ssi_private->pdev = - platform_device_register_data(&pdev->dev, name, 0, NULL, 0); - if (IS_ERR(ssi_private->pdev)) { - ret = PTR_ERR(ssi_private->pdev); - dev_err(&pdev->dev, "failed to register platform: %d\n", ret); + ssi->pdev = platform_device_register_data(dev, name, 0, NULL, 0); + if (IS_ERR(ssi->pdev)) { + ret = PTR_ERR(ssi->pdev); + dev_err(dev, "failed to register platform: %d\n", ret); goto error_sound_card; } done: - if (ssi_private->dai_fmt) - _fsl_ssi_set_dai_fmt(&pdev->dev, ssi_private, - ssi_private->dai_fmt); + if (ssi->dai_fmt) + _fsl_ssi_set_dai_fmt(dev, ssi, ssi->dai_fmt); - if (fsl_ssi_is_ac97(ssi_private)) { + if (fsl_ssi_is_ac97(ssi)) { u32 ssi_idx; ret = of_property_read_u32(np, "cell-index", &ssi_idx); if (ret) { - dev_err(&pdev->dev, "cannot get SSI index property\n"); + dev_err(dev, "failed to get SSI index property\n"); goto error_sound_card; } - ssi_private->pdev = - platform_device_register_data(NULL, - "ac97-codec", ssi_idx, NULL, 0); - if (IS_ERR(ssi_private->pdev)) { - ret = PTR_ERR(ssi_private->pdev); - dev_err(&pdev->dev, + ssi->pdev = platform_device_register_data(NULL, "ac97-codec", + ssi_idx, NULL, 0); + if (IS_ERR(ssi->pdev)) { + ret = PTR_ERR(ssi->pdev); + dev_err(dev, "failed to register AC97 codec platform: %d\n", ret); goto error_sound_card; @@ -1654,29 +1579,36 @@ done: return 0; error_sound_card: - fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); - + fsl_ssi_debugfs_remove(&ssi->dbg_stats); error_asoc_register: - if (ssi_private->soc->imx) - fsl_ssi_imx_clean(pdev, ssi_private); + if (fsl_ssi_is_ac97(ssi)) + snd_soc_set_ac97_ops(NULL); +error_ac97_ops: + if (fsl_ssi_is_ac97(ssi)) + mutex_destroy(&ssi->ac97_reg_lock); + + if (ssi->soc->imx) + fsl_ssi_imx_clean(pdev, ssi); return ret; } static int fsl_ssi_remove(struct platform_device *pdev) { - struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); + struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev); - fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); + fsl_ssi_debugfs_remove(&ssi->dbg_stats); - if (ssi_private->pdev) - platform_device_unregister(ssi_private->pdev); + if (ssi->pdev) + platform_device_unregister(ssi->pdev); - if (ssi_private->soc->imx) - fsl_ssi_imx_clean(pdev, ssi_private); + if (ssi->soc->imx) + fsl_ssi_imx_clean(pdev, ssi); - if (fsl_ssi_is_ac97(ssi_private)) + if (fsl_ssi_is_ac97(ssi)) { snd_soc_set_ac97_ops(NULL); + mutex_destroy(&ssi->ac97_reg_lock); + } return 0; } @@ -1684,13 +1616,11 @@ static int fsl_ssi_remove(struct platform_device *pdev) #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; + struct fsl_ssi *ssi = dev_get_drvdata(dev); + struct regmap *regs = ssi->regs; - regmap_read(regs, CCSR_SSI_SFCSR, - &ssi_private->regcache_sfcsr); - regmap_read(regs, CCSR_SSI_SACNT, - &ssi_private->regcache_sacnt); + regmap_read(regs, REG_SSI_SFCSR, &ssi->regcache_sfcsr); + regmap_read(regs, REG_SSI_SACNT, &ssi->regcache_sacnt); regcache_cache_only(regs, true); regcache_mark_dirty(regs); @@ -1700,17 +1630,16 @@ static int fsl_ssi_suspend(struct device *dev) static int fsl_ssi_resume(struct device *dev) { - struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); - struct regmap *regs = ssi_private->regs; + struct fsl_ssi *ssi = dev_get_drvdata(dev); + struct regmap *regs = ssi->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); - regmap_write(regs, CCSR_SSI_SACNT, - ssi_private->regcache_sacnt); + regmap_update_bits(regs, REG_SSI_SFCSR, + SSI_SFCSR_RFWM1_MASK | SSI_SFCSR_TFWM1_MASK | + SSI_SFCSR_RFWM0_MASK | SSI_SFCSR_TFWM0_MASK, + ssi->regcache_sfcsr); + regmap_write(regs, REG_SSI_SACNT, ssi->regcache_sacnt); return regcache_sync(regs); } diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index 506510540d0a..de2fdc5db726 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h @@ -1,5 +1,5 @@ /* - * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 SoC + * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 and i.MX SoC * * Author: Timur Tabi <timur@freescale.com> * @@ -12,198 +12,261 @@ #ifndef _MPC8610_I2S_H #define _MPC8610_I2S_H -/* SSI registers */ -#define CCSR_SSI_STX0 0x00 -#define CCSR_SSI_STX1 0x04 -#define CCSR_SSI_SRX0 0x08 -#define CCSR_SSI_SRX1 0x0c -#define CCSR_SSI_SCR 0x10 -#define CCSR_SSI_SISR 0x14 -#define CCSR_SSI_SIER 0x18 -#define CCSR_SSI_STCR 0x1c -#define CCSR_SSI_SRCR 0x20 -#define CCSR_SSI_STCCR 0x24 -#define CCSR_SSI_SRCCR 0x28 -#define CCSR_SSI_SFCSR 0x2c -#define CCSR_SSI_STR 0x30 -#define CCSR_SSI_SOR 0x34 -#define CCSR_SSI_SACNT 0x38 -#define CCSR_SSI_SACADD 0x3c -#define CCSR_SSI_SACDAT 0x40 -#define CCSR_SSI_SATAG 0x44 -#define CCSR_SSI_STMSK 0x48 -#define CCSR_SSI_SRMSK 0x4c -#define CCSR_SSI_SACCST 0x50 -#define CCSR_SSI_SACCEN 0x54 -#define CCSR_SSI_SACCDIS 0x58 +#define RX 0 +#define TX 1 -#define CCSR_SSI_SCR_SYNC_TX_FS 0x00001000 -#define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800 -#define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400 -#define CCSR_SSI_SCR_TCH_EN 0x00000100 -#define CCSR_SSI_SCR_SYS_CLK_EN 0x00000080 -#define CCSR_SSI_SCR_I2S_MODE_MASK 0x00000060 -#define CCSR_SSI_SCR_I2S_MODE_NORMAL 0x00000000 -#define CCSR_SSI_SCR_I2S_MODE_MASTER 0x00000020 -#define CCSR_SSI_SCR_I2S_MODE_SLAVE 0x00000040 -#define CCSR_SSI_SCR_SYN 0x00000010 -#define CCSR_SSI_SCR_NET 0x00000008 -#define CCSR_SSI_SCR_RE 0x00000004 -#define CCSR_SSI_SCR_TE 0x00000002 -#define CCSR_SSI_SCR_SSIEN 0x00000001 +/* -- SSI Register Map -- */ -#define CCSR_SSI_SISR_RFRC 0x01000000 -#define CCSR_SSI_SISR_TFRC 0x00800000 -#define CCSR_SSI_SISR_CMDAU 0x00040000 -#define CCSR_SSI_SISR_CMDDU 0x00020000 -#define CCSR_SSI_SISR_RXT 0x00010000 -#define CCSR_SSI_SISR_RDR1 0x00008000 -#define CCSR_SSI_SISR_RDR0 0x00004000 -#define CCSR_SSI_SISR_TDE1 0x00002000 -#define CCSR_SSI_SISR_TDE0 0x00001000 -#define CCSR_SSI_SISR_ROE1 0x00000800 -#define CCSR_SSI_SISR_ROE0 0x00000400 -#define CCSR_SSI_SISR_TUE1 0x00000200 -#define CCSR_SSI_SISR_TUE0 0x00000100 -#define CCSR_SSI_SISR_TFS 0x00000080 -#define CCSR_SSI_SISR_RFS 0x00000040 -#define CCSR_SSI_SISR_TLS 0x00000020 -#define CCSR_SSI_SISR_RLS 0x00000010 -#define CCSR_SSI_SISR_RFF1 0x00000008 -#define CCSR_SSI_SISR_RFF0 0x00000004 -#define CCSR_SSI_SISR_TFE1 0x00000002 -#define CCSR_SSI_SISR_TFE0 0x00000001 +/* SSI Transmit Data Register 0 */ +#define REG_SSI_STX0 0x00 +/* SSI Transmit Data Register 1 */ +#define REG_SSI_STX1 0x04 +/* SSI Receive Data Register 0 */ +#define REG_SSI_SRX0 0x08 +/* SSI Receive Data Register 1 */ +#define REG_SSI_SRX1 0x0c +/* SSI Control Register */ +#define REG_SSI_SCR 0x10 +/* SSI Interrupt Status Register */ +#define REG_SSI_SISR 0x14 +/* SSI Interrupt Enable Register */ +#define REG_SSI_SIER 0x18 +/* SSI Transmit Configuration Register */ +#define REG_SSI_STCR 0x1c +/* SSI Receive Configuration Register */ +#define REG_SSI_SRCR 0x20 +#define REG_SSI_SxCR(tx) ((tx) ? REG_SSI_STCR : REG_SSI_SRCR) +/* SSI Transmit Clock Control Register */ +#define REG_SSI_STCCR 0x24 +/* SSI Receive Clock Control Register */ +#define REG_SSI_SRCCR 0x28 +#define REG_SSI_SxCCR(tx) ((tx) ? REG_SSI_STCCR : REG_SSI_SRCCR) +/* SSI FIFO Control/Status Register */ +#define REG_SSI_SFCSR 0x2c +/* + * SSI Test Register (Intended for debugging purposes only) + * + * Note: STR is not documented in recent IMX datasheet, but + * is described in IMX51 reference manual at section 56.3.3.14 + */ +#define REG_SSI_STR 0x30 +/* + * SSI Option Register (Intended for internal use only) + * + * Note: SOR is not documented in recent IMX datasheet, but + * is described in IMX51 reference manual at section 56.3.3.15 + */ +#define REG_SSI_SOR 0x34 +/* SSI AC97 Control Register */ +#define REG_SSI_SACNT 0x38 +/* SSI AC97 Command Address Register */ +#define REG_SSI_SACADD 0x3c +/* SSI AC97 Command Data Register */ +#define REG_SSI_SACDAT 0x40 +/* SSI AC97 Tag Register */ +#define REG_SSI_SATAG 0x44 +/* SSI Transmit Time Slot Mask Register */ +#define REG_SSI_STMSK 0x48 +/* SSI Receive Time Slot Mask Register */ +#define REG_SSI_SRMSK 0x4c +#define REG_SSI_SxMSK(tx) ((tx) ? REG_SSI_STMSK : REG_SSI_SRMSK) +/* + * SSI AC97 Channel Status Register + * + * The status could be changed by: + * 1) Writing a '1' bit at some position in SACCEN sets relevant bit in SACCST + * 2) Writing a '1' bit at some position in SACCDIS unsets the relevant bit + * 3) Receivng a '1' in SLOTREQ bit from external CODEC via AC Link + */ +#define REG_SSI_SACCST 0x50 +/* SSI AC97 Channel Enable Register -- Set bits in SACCST */ +#define REG_SSI_SACCEN 0x54 +/* SSI AC97 Channel Disable Register -- Clear bits in SACCST */ +#define REG_SSI_SACCDIS 0x58 + +/* -- SSI Register Field Maps -- */ -#define CCSR_SSI_SIER_RFRC_EN 0x01000000 -#define CCSR_SSI_SIER_TFRC_EN 0x00800000 -#define CCSR_SSI_SIER_RDMAE 0x00400000 -#define CCSR_SSI_SIER_RIE 0x00200000 -#define CCSR_SSI_SIER_TDMAE 0x00100000 -#define CCSR_SSI_SIER_TIE 0x00080000 -#define CCSR_SSI_SIER_CMDAU_EN 0x00040000 -#define CCSR_SSI_SIER_CMDDU_EN 0x00020000 -#define CCSR_SSI_SIER_RXT_EN 0x00010000 -#define CCSR_SSI_SIER_RDR1_EN 0x00008000 -#define CCSR_SSI_SIER_RDR0_EN 0x00004000 -#define CCSR_SSI_SIER_TDE1_EN 0x00002000 -#define CCSR_SSI_SIER_TDE0_EN 0x00001000 -#define CCSR_SSI_SIER_ROE1_EN 0x00000800 -#define CCSR_SSI_SIER_ROE0_EN 0x00000400 -#define CCSR_SSI_SIER_TUE1_EN 0x00000200 -#define CCSR_SSI_SIER_TUE0_EN 0x00000100 -#define CCSR_SSI_SIER_TFS_EN 0x00000080 -#define CCSR_SSI_SIER_RFS_EN 0x00000040 -#define CCSR_SSI_SIER_TLS_EN 0x00000020 -#define CCSR_SSI_SIER_RLS_EN 0x00000010 -#define CCSR_SSI_SIER_RFF1_EN 0x00000008 -#define CCSR_SSI_SIER_RFF0_EN 0x00000004 -#define CCSR_SSI_SIER_TFE1_EN 0x00000002 -#define CCSR_SSI_SIER_TFE0_EN 0x00000001 +/* SSI Control Register -- REG_SSI_SCR 0x10 */ +#define SSI_SCR_SYNC_TX_FS 0x00001000 +#define SSI_SCR_RFR_CLK_DIS 0x00000800 +#define SSI_SCR_TFR_CLK_DIS 0x00000400 +#define SSI_SCR_TCH_EN 0x00000100 +#define SSI_SCR_SYS_CLK_EN 0x00000080 +#define SSI_SCR_I2S_MODE_MASK 0x00000060 +#define SSI_SCR_I2S_MODE_NORMAL 0x00000000 +#define SSI_SCR_I2S_MODE_MASTER 0x00000020 +#define SSI_SCR_I2S_MODE_SLAVE 0x00000040 +#define SSI_SCR_SYN 0x00000010 +#define SSI_SCR_NET 0x00000008 +#define SSI_SCR_I2S_NET_MASK (SSI_SCR_NET | SSI_SCR_I2S_MODE_MASK) +#define SSI_SCR_RE 0x00000004 +#define SSI_SCR_TE 0x00000002 +#define SSI_SCR_SSIEN 0x00000001 -#define CCSR_SSI_STCR_TXBIT0 0x00000200 -#define CCSR_SSI_STCR_TFEN1 0x00000100 -#define CCSR_SSI_STCR_TFEN0 0x00000080 -#define CCSR_SSI_STCR_TFDIR 0x00000040 -#define CCSR_SSI_STCR_TXDIR 0x00000020 -#define CCSR_SSI_STCR_TSHFD 0x00000010 -#define CCSR_SSI_STCR_TSCKP 0x00000008 -#define CCSR_SSI_STCR_TFSI 0x00000004 -#define CCSR_SSI_STCR_TFSL 0x00000002 -#define CCSR_SSI_STCR_TEFS 0x00000001 +/* SSI Interrupt Status Register -- REG_SSI_SISR 0x14 */ +#define SSI_SISR_RFRC 0x01000000 +#define SSI_SISR_TFRC 0x00800000 +#define SSI_SISR_CMDAU 0x00040000 +#define SSI_SISR_CMDDU 0x00020000 +#define SSI_SISR_RXT 0x00010000 +#define SSI_SISR_RDR1 0x00008000 +#define SSI_SISR_RDR0 0x00004000 +#define SSI_SISR_TDE1 0x00002000 +#define SSI_SISR_TDE0 0x00001000 +#define SSI_SISR_ROE1 0x00000800 +#define SSI_SISR_ROE0 0x00000400 +#define SSI_SISR_TUE1 0x00000200 +#define SSI_SISR_TUE0 0x00000100 +#define SSI_SISR_TFS 0x00000080 +#define SSI_SISR_RFS 0x00000040 +#define SSI_SISR_TLS 0x00000020 +#define SSI_SISR_RLS 0x00000010 +#define SSI_SISR_RFF1 0x00000008 +#define SSI_SISR_RFF0 0x00000004 +#define SSI_SISR_TFE1 0x00000002 +#define SSI_SISR_TFE0 0x00000001 -#define CCSR_SSI_SRCR_RXEXT 0x00000400 -#define CCSR_SSI_SRCR_RXBIT0 0x00000200 -#define CCSR_SSI_SRCR_RFEN1 0x00000100 -#define CCSR_SSI_SRCR_RFEN0 0x00000080 -#define CCSR_SSI_SRCR_RFDIR 0x00000040 -#define CCSR_SSI_SRCR_RXDIR 0x00000020 -#define CCSR_SSI_SRCR_RSHFD 0x00000010 -#define CCSR_SSI_SRCR_RSCKP 0x00000008 -#define CCSR_SSI_SRCR_RFSI 0x00000004 -#define CCSR_SSI_SRCR_RFSL 0x00000002 -#define CCSR_SSI_SRCR_REFS 0x00000001 +/* SSI Interrupt Enable Register -- REG_SSI_SIER 0x18 */ +#define SSI_SIER_RFRC_EN 0x01000000 +#define SSI_SIER_TFRC_EN 0x00800000 +#define SSI_SIER_RDMAE 0x00400000 +#define SSI_SIER_RIE 0x00200000 +#define SSI_SIER_TDMAE 0x00100000 +#define SSI_SIER_TIE 0x00080000 +#define SSI_SIER_CMDAU_EN 0x00040000 +#define SSI_SIER_CMDDU_EN 0x00020000 +#define SSI_SIER_RXT_EN 0x00010000 +#define SSI_SIER_RDR1_EN 0x00008000 +#define SSI_SIER_RDR0_EN 0x00004000 +#define SSI_SIER_TDE1_EN 0x00002000 +#define SSI_SIER_TDE0_EN 0x00001000 +#define SSI_SIER_ROE1_EN 0x00000800 +#define SSI_SIER_ROE0_EN 0x00000400 +#define SSI_SIER_TUE1_EN 0x00000200 +#define SSI_SIER_TUE0_EN 0x00000100 +#define SSI_SIER_TFS_EN 0x00000080 +#define SSI_SIER_RFS_EN 0x00000040 +#define SSI_SIER_TLS_EN 0x00000020 +#define SSI_SIER_RLS_EN 0x00000010 +#define SSI_SIER_RFF1_EN 0x00000008 +#define SSI_SIER_RFF0_EN 0x00000004 +#define SSI_SIER_TFE1_EN 0x00000002 +#define SSI_SIER_TFE0_EN 0x00000001 -/* STCCR and SRCCR */ -#define CCSR_SSI_SxCCR_DIV2_SHIFT 18 -#define CCSR_SSI_SxCCR_DIV2 0x00040000 -#define CCSR_SSI_SxCCR_PSR_SHIFT 17 -#define CCSR_SSI_SxCCR_PSR 0x00020000 -#define CCSR_SSI_SxCCR_WL_SHIFT 13 -#define CCSR_SSI_SxCCR_WL_MASK 0x0001E000 -#define CCSR_SSI_SxCCR_WL(x) \ - (((((x) / 2) - 1) << CCSR_SSI_SxCCR_WL_SHIFT) & CCSR_SSI_SxCCR_WL_MASK) -#define CCSR_SSI_SxCCR_DC_SHIFT 8 -#define CCSR_SSI_SxCCR_DC_MASK 0x00001F00 -#define CCSR_SSI_SxCCR_DC(x) \ - ((((x) - 1) << CCSR_SSI_SxCCR_DC_SHIFT) & CCSR_SSI_SxCCR_DC_MASK) -#define CCSR_SSI_SxCCR_PM_SHIFT 0 -#define CCSR_SSI_SxCCR_PM_MASK 0x000000FF -#define CCSR_SSI_SxCCR_PM(x) \ - ((((x) - 1) << CCSR_SSI_SxCCR_PM_SHIFT) & CCSR_SSI_SxCCR_PM_MASK) +/* SSI Transmit Configuration Register -- REG_SSI_STCR 0x1C */ +#define SSI_STCR_TXBIT0 0x00000200 +#define SSI_STCR_TFEN1 0x00000100 +#define SSI_STCR_TFEN0 0x00000080 +#define SSI_STCR_TFDIR 0x00000040 +#define SSI_STCR_TXDIR 0x00000020 +#define SSI_STCR_TSHFD 0x00000010 +#define SSI_STCR_TSCKP 0x00000008 +#define SSI_STCR_TFSI 0x00000004 +#define SSI_STCR_TFSL 0x00000002 +#define SSI_STCR_TEFS 0x00000001 + +/* SSI Receive Configuration Register -- REG_SSI_SRCR 0x20 */ +#define SSI_SRCR_RXEXT 0x00000400 +#define SSI_SRCR_RXBIT0 0x00000200 +#define SSI_SRCR_RFEN1 0x00000100 +#define SSI_SRCR_RFEN0 0x00000080 +#define SSI_SRCR_RFDIR 0x00000040 +#define SSI_SRCR_RXDIR 0x00000020 +#define SSI_SRCR_RSHFD 0x00000010 +#define SSI_SRCR_RSCKP 0x00000008 +#define SSI_SRCR_RFSI 0x00000004 +#define SSI_SRCR_RFSL 0x00000002 +#define SSI_SRCR_REFS 0x00000001 /* - * The xFCNT bits are read-only, and the xFWM bits are read/write. Use the - * CCSR_SSI_SFCSR_xFCNTy() macros to read the FIFO counters, and use the - * CCSR_SSI_SFCSR_xFWMy() macros to set the watermarks. + * SSI Transmit Clock Control Register -- REG_SSI_STCCR 0x24 + * SSI Receive Clock Control Register -- REG_SSI_SRCCR 0x28 + */ +#define SSI_SxCCR_DIV2_SHIFT 18 +#define SSI_SxCCR_DIV2 0x00040000 +#define SSI_SxCCR_PSR_SHIFT 17 +#define SSI_SxCCR_PSR 0x00020000 +#define SSI_SxCCR_WL_SHIFT 13 +#define SSI_SxCCR_WL_MASK 0x0001E000 +#define SSI_SxCCR_WL(x) \ + (((((x) / 2) - 1) << SSI_SxCCR_WL_SHIFT) & SSI_SxCCR_WL_MASK) +#define SSI_SxCCR_DC_SHIFT 8 +#define SSI_SxCCR_DC_MASK 0x00001F00 +#define SSI_SxCCR_DC(x) \ + ((((x) - 1) << SSI_SxCCR_DC_SHIFT) & SSI_SxCCR_DC_MASK) +#define SSI_SxCCR_PM_SHIFT 0 +#define SSI_SxCCR_PM_MASK 0x000000FF +#define SSI_SxCCR_PM(x) \ + ((((x) - 1) << SSI_SxCCR_PM_SHIFT) & SSI_SxCCR_PM_MASK) + +/* + * SSI FIFO Control/Status Register -- REG_SSI_SFCSR 0x2c + * + * Tx or Rx FIFO Counter -- SSI_SFCSR_xFCNTy Read-Only + * Tx or Rx FIFO Watermarks -- SSI_SFCSR_xFWMy Read/Write */ -#define CCSR_SSI_SFCSR_RFCNT1_SHIFT 28 -#define CCSR_SSI_SFCSR_RFCNT1_MASK 0xF0000000 -#define CCSR_SSI_SFCSR_RFCNT1(x) \ - (((x) & CCSR_SSI_SFCSR_RFCNT1_MASK) >> CCSR_SSI_SFCSR_RFCNT1_SHIFT) -#define CCSR_SSI_SFCSR_TFCNT1_SHIFT 24 -#define CCSR_SSI_SFCSR_TFCNT1_MASK 0x0F000000 -#define CCSR_SSI_SFCSR_TFCNT1(x) \ - (((x) & CCSR_SSI_SFCSR_TFCNT1_MASK) >> CCSR_SSI_SFCSR_TFCNT1_SHIFT) -#define CCSR_SSI_SFCSR_RFWM1_SHIFT 20 -#define CCSR_SSI_SFCSR_RFWM1_MASK 0x00F00000 -#define CCSR_SSI_SFCSR_RFWM1(x) \ - (((x) << CCSR_SSI_SFCSR_RFWM1_SHIFT) & CCSR_SSI_SFCSR_RFWM1_MASK) -#define CCSR_SSI_SFCSR_TFWM1_SHIFT 16 -#define CCSR_SSI_SFCSR_TFWM1_MASK 0x000F0000 -#define CCSR_SSI_SFCSR_TFWM1(x) \ - (((x) << CCSR_SSI_SFCSR_TFWM1_SHIFT) & CCSR_SSI_SFCSR_TFWM1_MASK) -#define CCSR_SSI_SFCSR_RFCNT0_SHIFT 12 -#define CCSR_SSI_SFCSR_RFCNT0_MASK 0x0000F000 -#define CCSR_SSI_SFCSR_RFCNT0(x) \ - (((x) & CCSR_SSI_SFCSR_RFCNT0_MASK) >> CCSR_SSI_SFCSR_RFCNT0_SHIFT) -#define CCSR_SSI_SFCSR_TFCNT0_SHIFT 8 -#define CCSR_SSI_SFCSR_TFCNT0_MASK 0x00000F00 -#define CCSR_SSI_SFCSR_TFCNT0(x) \ - (((x) & CCSR_SSI_SFCSR_TFCNT0_MASK) >> CCSR_SSI_SFCSR_TFCNT0_SHIFT) -#define CCSR_SSI_SFCSR_RFWM0_SHIFT 4 -#define CCSR_SSI_SFCSR_RFWM0_MASK 0x000000F0 -#define CCSR_SSI_SFCSR_RFWM0(x) \ - (((x) << CCSR_SSI_SFCSR_RFWM0_SHIFT) & CCSR_SSI_SFCSR_RFWM0_MASK) -#define CCSR_SSI_SFCSR_TFWM0_SHIFT 0 -#define CCSR_SSI_SFCSR_TFWM0_MASK 0x0000000F -#define CCSR_SSI_SFCSR_TFWM0(x) \ - (((x) << CCSR_SSI_SFCSR_TFWM0_SHIFT) & CCSR_SSI_SFCSR_TFWM0_MASK) +#define SSI_SFCSR_RFCNT1_SHIFT 28 +#define SSI_SFCSR_RFCNT1_MASK 0xF0000000 +#define SSI_SFCSR_RFCNT1(x) \ + (((x) & SSI_SFCSR_RFCNT1_MASK) >> SSI_SFCSR_RFCNT1_SHIFT) +#define SSI_SFCSR_TFCNT1_SHIFT 24 +#define SSI_SFCSR_TFCNT1_MASK 0x0F000000 +#define SSI_SFCSR_TFCNT1(x) \ + (((x) & SSI_SFCSR_TFCNT1_MASK) >> SSI_SFCSR_TFCNT1_SHIFT) +#define SSI_SFCSR_RFWM1_SHIFT 20 +#define SSI_SFCSR_RFWM1_MASK 0x00F00000 +#define SSI_SFCSR_RFWM1(x) \ + (((x) << SSI_SFCSR_RFWM1_SHIFT) & SSI_SFCSR_RFWM1_MASK) +#define SSI_SFCSR_TFWM1_SHIFT 16 +#define SSI_SFCSR_TFWM1_MASK 0x000F0000 +#define SSI_SFCSR_TFWM1(x) \ + (((x) << SSI_SFCSR_TFWM1_SHIFT) & SSI_SFCSR_TFWM1_MASK) +#define SSI_SFCSR_RFCNT0_SHIFT 12 +#define SSI_SFCSR_RFCNT0_MASK 0x0000F000 +#define SSI_SFCSR_RFCNT0(x) \ + (((x) & SSI_SFCSR_RFCNT0_MASK) >> SSI_SFCSR_RFCNT0_SHIFT) +#define SSI_SFCSR_TFCNT0_SHIFT 8 +#define SSI_SFCSR_TFCNT0_MASK 0x00000F00 +#define SSI_SFCSR_TFCNT0(x) \ + (((x) & SSI_SFCSR_TFCNT0_MASK) >> SSI_SFCSR_TFCNT0_SHIFT) +#define SSI_SFCSR_RFWM0_SHIFT 4 +#define SSI_SFCSR_RFWM0_MASK 0x000000F0 +#define SSI_SFCSR_RFWM0(x) \ + (((x) << SSI_SFCSR_RFWM0_SHIFT) & SSI_SFCSR_RFWM0_MASK) +#define SSI_SFCSR_TFWM0_SHIFT 0 +#define SSI_SFCSR_TFWM0_MASK 0x0000000F +#define SSI_SFCSR_TFWM0(x) \ + (((x) << SSI_SFCSR_TFWM0_SHIFT) & SSI_SFCSR_TFWM0_MASK) -#define CCSR_SSI_STR_TEST 0x00008000 -#define CCSR_SSI_STR_RCK2TCK 0x00004000 -#define CCSR_SSI_STR_RFS2TFS 0x00002000 -#define CCSR_SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F) -#define CCSR_SSI_STR_TXD2RXD 0x00000080 -#define CCSR_SSI_STR_TCK2RCK 0x00000040 -#define CCSR_SSI_STR_TFS2RFS 0x00000020 -#define CCSR_SSI_STR_TXSTATE(x) ((x) & 0x1F) +/* SSI Test Register -- REG_SSI_STR 0x30 */ +#define SSI_STR_TEST 0x00008000 +#define SSI_STR_RCK2TCK 0x00004000 +#define SSI_STR_RFS2TFS 0x00002000 +#define SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F) +#define SSI_STR_TXD2RXD 0x00000080 +#define SSI_STR_TCK2RCK 0x00000040 +#define SSI_STR_TFS2RFS 0x00000020 +#define SSI_STR_TXSTATE(x) ((x) & 0x1F) -#define CCSR_SSI_SOR_CLKOFF 0x00000040 -#define CCSR_SSI_SOR_RX_CLR 0x00000020 -#define CCSR_SSI_SOR_TX_CLR 0x00000010 -#define CCSR_SSI_SOR_INIT 0x00000008 -#define CCSR_SSI_SOR_WAIT_SHIFT 1 -#define CCSR_SSI_SOR_WAIT_MASK 0x00000006 -#define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) -#define CCSR_SSI_SOR_SYNRST 0x00000001 +/* SSI Option Register -- REG_SSI_SOR 0x34 */ +#define SSI_SOR_CLKOFF 0x00000040 +#define SSI_SOR_RX_CLR 0x00000020 +#define SSI_SOR_TX_CLR 0x00000010 +#define SSI_SOR_xX_CLR(tx) ((tx) ? SSI_SOR_TX_CLR : SSI_SOR_RX_CLR) +#define SSI_SOR_INIT 0x00000008 +#define SSI_SOR_WAIT_SHIFT 1 +#define SSI_SOR_WAIT_MASK 0x00000006 +#define SSI_SOR_WAIT(x) (((x) & 3) << SSI_SOR_WAIT_SHIFT) +#define SSI_SOR_SYNRST 0x00000001 -#define CCSR_SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) -#define CCSR_SSI_SACNT_WR 0x00000010 -#define CCSR_SSI_SACNT_RD 0x00000008 -#define CCSR_SSI_SACNT_RDWR_MASK 0x00000018 -#define CCSR_SSI_SACNT_TIF 0x00000004 -#define CCSR_SSI_SACNT_FV 0x00000002 -#define CCSR_SSI_SACNT_AC97EN 0x00000001 +/* SSI AC97 Control Register -- REG_SSI_SACNT 0x38 */ +#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) +#define SSI_SACNT_WR 0x00000010 +#define SSI_SACNT_RD 0x00000008 +#define SSI_SACNT_RDWR_MASK 0x00000018 +#define SSI_SACNT_TIF 0x00000004 +#define SSI_SACNT_FV 0x00000002 +#define SSI_SACNT_AC97EN 0x00000001 struct device; @@ -255,7 +318,7 @@ static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr) } static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, - struct device *dev) + struct device *dev) { return 0; } diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c index 5469ffbc0253..7aac63e2c561 100644 --- a/sound/soc/fsl/fsl_ssi_dbg.c +++ b/sound/soc/fsl/fsl_ssi_dbg.c @@ -18,86 +18,86 @@ void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr) { - if (sisr & CCSR_SSI_SISR_RFRC) + if (sisr & SSI_SISR_RFRC) dbg->stats.rfrc++; - if (sisr & CCSR_SSI_SISR_TFRC) + if (sisr & SSI_SISR_TFRC) dbg->stats.tfrc++; - if (sisr & CCSR_SSI_SISR_CMDAU) + if (sisr & SSI_SISR_CMDAU) dbg->stats.cmdau++; - if (sisr & CCSR_SSI_SISR_CMDDU) + if (sisr & SSI_SISR_CMDDU) dbg->stats.cmddu++; - if (sisr & CCSR_SSI_SISR_RXT) + if (sisr & SSI_SISR_RXT) dbg->stats.rxt++; - if (sisr & CCSR_SSI_SISR_RDR1) + if (sisr & SSI_SISR_RDR1) dbg->stats.rdr1++; - if (sisr & CCSR_SSI_SISR_RDR0) + if (sisr & SSI_SISR_RDR0) dbg->stats.rdr0++; - if (sisr & CCSR_SSI_SISR_TDE1) + if (sisr & SSI_SISR_TDE1) dbg->stats.tde1++; - if (sisr & CCSR_SSI_SISR_TDE0) + if (sisr & SSI_SISR_TDE0) dbg->stats.tde0++; - if (sisr & CCSR_SSI_SISR_ROE1) + if (sisr & SSI_SISR_ROE1) dbg->stats.roe1++; - if (sisr & CCSR_SSI_SISR_ROE0) + if (sisr & SSI_SISR_ROE0) dbg->stats.roe0++; - if (sisr & CCSR_SSI_SISR_TUE1) + if (sisr & SSI_SISR_TUE1) dbg->stats.tue1++; - if (sisr & CCSR_SSI_SISR_TUE0) + if (sisr & SSI_SISR_TUE0) dbg->stats.tue0++; - if (sisr & CCSR_SSI_SISR_TFS) + if (sisr & SSI_SISR_TFS) dbg->stats.tfs++; - if (sisr & CCSR_SSI_SISR_RFS) + if (sisr & SSI_SISR_RFS) dbg->stats.rfs++; - if (sisr & CCSR_SSI_SISR_TLS) + if (sisr & SSI_SISR_TLS) dbg->stats.tls++; - if (sisr & CCSR_SSI_SISR_RLS) + if (sisr & SSI_SISR_RLS) dbg->stats.rls++; - if (sisr & CCSR_SSI_SISR_RFF1) + if (sisr & SSI_SISR_RFF1) dbg->stats.rff1++; - if (sisr & CCSR_SSI_SISR_RFF0) + if (sisr & SSI_SISR_RFF0) dbg->stats.rff0++; - if (sisr & CCSR_SSI_SISR_TFE1) + if (sisr & SSI_SISR_TFE1) dbg->stats.tfe1++; - if (sisr & CCSR_SSI_SISR_TFE0) + if (sisr & SSI_SISR_TFE0) dbg->stats.tfe0++; } -/* Show the statistics of a flag only if its interrupt is enabled. The - * compiler will optimze this code to a no-op if the interrupt is not - * enabled. +/** + * Show the statistics of a flag only if its interrupt is enabled + * + * Compilers will optimize it to a no-op if the interrupt is disabled */ #define SIER_SHOW(flag, name) \ do { \ - if (CCSR_SSI_SIER_##flag) \ + if (SSI_SIER_##flag) \ seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \ } while (0) /** - * fsl_sysfs_ssi_show: display SSI statistics + * Display the statistics for the current SSI device * - * Display the statistics for the current SSI device. To avoid confusion, - * we only show those counts that are enabled. + * To avoid confusion, only show those counts that are enabled */ static int fsl_ssi_stats_show(struct seq_file *s, void *unused) { @@ -147,7 +147,8 @@ int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) return -ENOMEM; ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO, - ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops); + ssi_dbg->dbg_dir, ssi_dbg, + &fsl_ssi_stats_ops); if (!ssi_dbg->dbg_stats) { debugfs_remove(ssi_dbg->dbg_dir); return -ENOMEM; diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c index 0c8f86d4020e..07a57209e055 100644 --- a/sound/soc/hisilicon/hi6210-i2s.c +++ b/sound/soc/hisilicon/hi6210-i2s.c @@ -36,7 +36,6 @@ #include <linux/of_irq.h> #include <linux/mfd/syscon.h> #include <linux/reset-controller.h> -#include <linux/clk.h> #include "hi6210-i2s.h" diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 7b49d04e3c60..ceb105cbd461 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,71 +1,125 @@ +config SND_SOC_INTEL_SST_TOPLEVEL + bool "Intel ASoC SST drivers" + default y + depends on X86 || COMPILE_TEST + select SND_SOC_INTEL_MACH + help + Intel ASoC SST Platform Drivers. If you have a Intel machine that + has an audio controller with a DSP and I2S or DMIC port, then + enable this option by saying Y + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Intel SST drivers. + +if SND_SOC_INTEL_SST_TOPLEVEL + config SND_SST_IPC tristate + # This option controls the IPC core for HiFi2 platforms config SND_SST_IPC_PCI tristate select SND_SST_IPC + # This option controls the PCI-based IPC for HiFi2 platforms + # (Medfield, Merrifield). config SND_SST_IPC_ACPI tristate select SND_SST_IPC - select SND_SOC_INTEL_SST - select IOSF_MBI + # This option controls the ACPI-based IPC for HiFi2 platforms + # (Baytrail, Cherrytrail) -config SND_SOC_INTEL_COMMON +config SND_SOC_INTEL_SST_ACPI tristate + # This option controls ACPI-based probing on + # Haswell/Broadwell/Baytrail legacy and will be set + # when these platforms are enabled config SND_SOC_INTEL_SST tristate - select SND_SOC_INTEL_SST_ACPI if ACPI config SND_SOC_INTEL_SST_FIRMWARE tristate select DW_DMAC_CORE - -config SND_SOC_INTEL_SST_ACPI - tristate - -config SND_SOC_ACPI_INTEL_MATCH - tristate - select SND_SOC_ACPI if ACPI - -config SND_SOC_INTEL_SST_TOPLEVEL - tristate "Intel ASoC SST drivers" - depends on X86 || COMPILE_TEST - select SND_SOC_INTEL_MACH - select SND_SOC_INTEL_COMMON - help - Intel ASoC Audio Drivers. If you have a Intel machine that - has audio controller with a DSP and I2S or DMIC port, then - enable this option by saying Y or M - If unsure select "N". + # This option controls firmware download on + # Haswell/Broadwell/Baytrail legacy and will be set + # when these platforms are enabled config SND_SOC_INTEL_HASWELL - tristate "Intel ASoC SST driver for Haswell/Broadwell" - depends on SND_SOC_INTEL_SST_TOPLEVEL && SND_DMA_SGBUF - depends on DMADEVICES + tristate "Haswell/Broadwell Platforms" + depends on SND_DMA_SGBUF + depends on DMADEVICES && ACPI select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE + select SND_SOC_ACPI_INTEL_MATCH + help + If you have a Intel Haswell or Broadwell platform connected to + an I2S codec, then enable this option by saying Y or m. This is + typically used for Chromebooks. This is a recommended option. config SND_SOC_INTEL_BAYTRAIL - tristate "Intel ASoC SST driver for Baytrail (legacy)" - depends on SND_SOC_INTEL_SST_TOPLEVEL - depends on DMADEVICES + tristate "Baytrail (legacy) Platforms" + depends on DMADEVICES && ACPI select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE + select SND_SOC_ACPI_INTEL_MATCH + help + If you have a Intel Baytrail platform connected to an I2S codec, + then enable this option by saying Y or m. This was typically used + for Baytrail Chromebooks but this option is now deprecated and is + not recommended, use SND_SST_ATOM_HIFI2_PLATFORM instead. + +config SND_SST_ATOM_HIFI2_PLATFORM_PCI + tristate "PCI HiFi2 (Medfield, Merrifield) Platforms" + depends on X86 && PCI + select SND_SST_IPC_PCI + select SND_SOC_COMPRESS + help + If you have a Intel Medfield or Merrifield/Edison platform, then + enable this option by saying Y or m. Distros will typically not + enable this option: Medfield devices are not available to + developers and while Merrifield/Edison can run a mainline kernel with + limited functionality it will require a firmware file which + is not in the standard firmware tree config SND_SST_ATOM_HIFI2_PLATFORM - tristate "Intel ASoC SST driver for HiFi2 platforms (*field, *trail)" - depends on SND_SOC_INTEL_SST_TOPLEVEL && X86 + tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms" + depends on X86 && ACPI + select SND_SST_IPC_ACPI select SND_SOC_COMPRESS + select SND_SOC_ACPI_INTEL_MATCH + select IOSF_MBI + help + If you have a Intel Baytrail or Cherrytrail platform with an I2S + codec, then enable this option by saying Y or m. This is a + recommended option + +config SND_SOC_INTEL_SKYLAKE_SSP_CLK + tristate config SND_SOC_INTEL_SKYLAKE - tristate "Intel ASoC SST driver for SKL/BXT/KBL/GLK/CNL" - depends on SND_SOC_INTEL_SST_TOPLEVEL && PCI && ACPI + tristate "SKL/BXT/KBL/GLK/CNL... Platforms" + depends on PCI && ACPI select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST + select SND_SOC_ACPI_INTEL_MATCH + help + If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ + GeminiLake or CannonLake platform with the DSP enabled in the BIOS + then enable this option by saying Y or m. + +config SND_SOC_ACPI_INTEL_MATCH + tristate + select SND_SOC_ACPI if ACPI + # this option controls the compilation of ACPI matching tables and + # helpers and is not meant to be selected by the user. + +endif ## SND_SOC_INTEL_SST_TOPLEVEL # ASoC codec drivers source "sound/soc/intel/boards/Kconfig" diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index b973d457e834..8160520fd74c 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 # Core support -obj-$(CONFIG_SND_SOC_INTEL_COMMON) += common/ +obj-$(CONFIG_SND_SOC) += common/ # Platform Support obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 32d6e02e2104..6cd481bec275 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -236,6 +236,9 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) /* Find the IRQ */ ctx->irq_num = platform_get_irq(pdev, ctx->pdata->res_info->acpi_ipc_irq_index); + if (ctx->irq_num <= 0) + return ctx->irq_num < 0 ? ctx->irq_num : -EIO; + return 0; } diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index 65e257b17a7e..7ee6aeb7e0af 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -220,10 +220,10 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, sst_free_block(sst_drv_ctx, block); out: test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id); - return 0; + return ret; } -/* +/** * sst_pause_stream - Send msg for a pausing stream * @str_id: stream ID * @@ -261,7 +261,7 @@ int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) } } else { retval = -EBADRQC; - dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n "); + dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n"); } return retval; @@ -284,7 +284,7 @@ int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) if (!str_info) return -EINVAL; if (str_info->status == STREAM_RUNNING) - return 0; + return 0; if (str_info->status == STREAM_PAUSED) { retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD, diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 6f754708a48c..fefb1ee9fec6 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -1,183 +1,183 @@ -config SND_SOC_INTEL_MACH - tristate "Intel Audio machine drivers" +menuconfig SND_SOC_INTEL_MACH + bool "Intel Machine drivers" depends on SND_SOC_INTEL_SST_TOPLEVEL - select SND_SOC_ACPI_INTEL_MATCH if ACPI + help + Intel ASoC Machine Drivers. If you have a Intel machine that + has an audio controller with a DSP and I2S or DMIC port, then + enable this option by saying Y + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Intel ASoC machine drivers. if SND_SOC_INTEL_MACH -config SND_MFLD_MACHINE - tristate "SOC Machine Audio driver for Intel Medfield MID platform" - depends on INTEL_SCU_IPC - select SND_SOC_SN95031 - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_PCI - help - This adds support for ASoC machine driver for Intel(R) MID Medfield platform - used as alsa device in audio substem in Intel(R) MID devices - Say Y if you have such a device. - If unsure select "N". +if SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_HASWELL_MACH - tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" + tristate "Haswell Lynxpoint" depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 help This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell - Ultrabook platforms. - Say Y if you have such a device. + Ultrabook platforms. This is a recommended option. + Say Y or m if you have such a device. If unsure select "N". config SND_SOC_INTEL_BDW_RT5677_MACH - tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" - depends on X86_INTEL_LPSS && GPIOLIB && I2C - depends on SND_SOC_INTEL_HASWELL + tristate "Broadwell with RT5677 codec" + depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM && GPIOLIB select SND_SOC_RT5677 help This adds support for Intel Broadwell platform based boards with - the RT5677 audio codec. + the RT5677 audio codec. This is a recommended option. + Say Y or m if you have such a device. + If unsure select "N". config SND_SOC_INTEL_BROADWELL_MACH - tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" + tristate "Broadwell Wildcatpoint" depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on SND_SOC_INTEL_HASWELL select SND_SOC_RT286 help This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell Ultrabook platforms. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_HASWELL + +if SND_SOC_INTEL_BAYTRAIL config SND_SOC_INTEL_BYT_MAX98090_MACH - tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" + tristate "Baytrail with MAX98090 codec" depends on X86_INTEL_LPSS && I2C - depends on SND_SST_IPC_ACPI = n - depends on SND_SOC_INTEL_BAYTRAIL select SND_SOC_MAX98090 help This adds audio driver for Intel Baytrail platform based boards - with the MAX98090 audio codec. + with the MAX98090 audio codec. This driver is deprecated, use + SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH instead for better + functionality. config SND_SOC_INTEL_BYT_RT5640_MACH - tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" + tristate "Baytrail with RT5640 codec" depends on X86_INTEL_LPSS && I2C - depends on SND_SST_IPC_ACPI = n - depends on SND_SOC_INTEL_BAYTRAIL select SND_SOC_RT5640 help This adds audio driver for Intel Baytrail platform based boards with the RT5640 audio codec. This driver is deprecated, use SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality. +endif ## SND_SOC_INTEL_BAYTRAIL + +if SND_SST_ATOM_HIFI2_PLATFORM + config SND_SOC_INTEL_BYTCR_RT5640_MACH - tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec" - depends on X86 && I2C && ACPI + tristate "Baytrail and Baytrail-CR with RT5640 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ACPI select SND_SOC_RT5640 - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI help - This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR - platforms with RT5640 audio codec. - Say Y if you have such a device. - If unsure select "N". + This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR + platforms with RT5640 audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". config SND_SOC_INTEL_BYTCR_RT5651_MACH - tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec" - depends on X86 && I2C && ACPI + tristate "Baytrail and Baytrail-CR with RT5651 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ACPI select SND_SOC_RT5651 - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI help - This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR - platforms with RT5651 audio codec. - Say Y if you have such a device. - If unsure select "N". + This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR + platforms with RT5651 audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". config SND_SOC_INTEL_CHT_BSW_RT5672_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" + tristate "Cherrytrail & Braswell with RT5672 codec" depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_RT5670 - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI + select SND_SOC_ACPI + select SND_SOC_RT5670 help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5672 audio codec. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_CHT_BSW_RT5645_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" + tristate "Cherrytrail & Braswell with RT5645/5650 codec" depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ACPI select SND_SOC_RT5645 - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5645/5650 audio codec. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" + tristate "Cherrytrail & Braswell with MAX98090 & TI codec" depends on X86_INTEL_LPSS && I2C && ACPI select SND_SOC_MAX98090 select SND_SOC_TS3A227E - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with MAX98090 audio codec it also can support TI jack chip as aux device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_BYT_CHT_DA7213_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with DA7212/7213 codec" + tristate "Baytrail & Cherrytrail with DA7212/7213 codec" depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ACPI select SND_SOC_DA7213 - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail platforms with DA7212/7213 audio codec. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_BYT_CHT_ES8316_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec" + tristate "Baytrail & Cherrytrail with ES8316 codec" depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ACPI select SND_SOC_ES8316 - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail & Cherrytrail platforms with ES8316 audio codec. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" + tristate "Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" depends on X86_INTEL_LPSS && I2C && ACPI - depends on SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI help This adds support for ASoC machine driver for the MinnowBoard Max or Up boards and provides access to I2S signals on the Low-Speed - connector + connector. This is not a recommended option outside of these cases. + It is not intended to be enabled by distros by default. + Say Y or m if you have such a device. + If unsure select "N". +endif ## SND_SST_ATOM_HIFI2_PLATFORM + +if SND_SOC_INTEL_SKYLAKE + config SND_SOC_INTEL_SKL_RT286_MACH - tristate "ASoC Audio driver for SKL with RT286 I2S mode" - depends on X86 && ACPI && I2C - depends on SND_SOC_INTEL_SKYLAKE + tristate "SKL with RT286 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI select SND_SOC_RT286 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI help This adds support for ASoC machine driver for Skylake platforms with RT286 I2S audio codec. - Say Y if you have such a device. + Say Y or m if you have such a device. If unsure select "N". config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH - tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode" - depends on X86_INTEL_LPSS && I2C - depends on SND_SOC_INTEL_SKYLAKE + tristate "SKL with NAU88L25 and SSM4567 in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI select SND_SOC_NAU8825 select SND_SOC_SSM4567 select SND_SOC_DMIC @@ -185,13 +185,12 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + SSM4567. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH - tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode" - depends on X86_INTEL_LPSS && I2C - depends on SND_SOC_INTEL_SKYLAKE + tristate "SKL with NAU88L25 and MAX98357A in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI select SND_SOC_NAU8825 select SND_SOC_MAX98357A select SND_SOC_DMIC @@ -199,13 +198,12 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + MAX98357A. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH - tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode" - depends on X86 && ACPI && I2C - depends on SND_SOC_INTEL_SKYLAKE + tristate "Broxton with DA7219 and MAX98357A in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_DMIC @@ -214,13 +212,12 @@ config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH help This adds support for ASoC machine driver for Broxton-P platforms with DA7219 + MAX98357A I2S audio codec. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_BXT_RT298_MACH - tristate "ASoC Audio driver for Broxton with RT298 I2S mode" - depends on X86 && ACPI && I2C - depends on SND_SOC_INTEL_SKYLAKE + tristate "Broxton with RT298 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI select SND_SOC_RT298 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI @@ -228,29 +225,27 @@ config SND_SOC_INTEL_BXT_RT298_MACH help This adds support for ASoC machine driver for Broxton platforms with RT286 I2S audio codec. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH - tristate "ASoC Audio driver for KBL with RT5663 and MAX98927 in I2S Mode" - depends on X86_INTEL_LPSS && I2C - select SND_SOC_INTEL_SST - depends on SND_SOC_INTEL_SKYLAKE + tristate "KBL with RT5663 and MAX98927 in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI select SND_SOC_RT5663 select SND_SOC_MAX98927 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI + select SND_SOC_INTEL_SKYLAKE_SSP_CLK help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for RT5663 + MAX98927. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH - tristate "ASoC Audio driver for KBL with RT5663, RT5514 and MAX98927 in I2S Mode" - depends on X86_INTEL_LPSS && I2C && SPI - select SND_SOC_INTEL_SST - depends on SND_SOC_INTEL_SKYLAKE + tristate "KBL with RT5663, RT5514 and MAX98927 in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + depends on SPI select SND_SOC_RT5663 select SND_SOC_RT5514 select SND_SOC_RT5514_SPI @@ -259,7 +254,8 @@ config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for RT5663 + RT5514 + MAX98927. - Say Y if you have such a device. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKYLAKE -endif +endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index c4d82ad41bd7..2179dedb28ad 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -219,7 +219,7 @@ static struct snd_soc_card bytcht_da7213_card = { .num_dapm_routes = ARRAY_SIZE(audio_map), }; -static char codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char codec_name[SND_ACPI_I2C_ID_LEN]; static int bytcht_da7213_probe(struct platform_device *pdev) { @@ -243,7 +243,7 @@ static int bytcht_da7213_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); if (i2c_name) { snprintf(codec_name, sizeof(codec_name), "%s%s", "i2c-", i2c_name); diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 8088396717e3..305e7f4fe55a 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -232,15 +232,39 @@ static struct snd_soc_card byt_cht_es8316_card = { .fully_routed = true, }; +static char codec_name[SND_ACPI_I2C_ID_LEN]; + static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) { - int ret = 0; struct byt_cht_es8316_private *priv; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; + int dai_index = 0; + int i; + int ret = 0; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); if (!priv) return -ENOMEM; + mach = (&pdev->dev)->platform_data; + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) { + if (!strcmp(byt_cht_es8316_dais[i].codec_name, + "i2c-ESSX8316:00")) { + dai_index = i; + break; + } + } + + /* fixup codec name based on HID */ + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); + if (i2c_name) { + snprintf(codec_name, sizeof(codec_name), + "%s%s", "i2c-", i2c_name); + byt_cht_es8316_dais[dai_index].codec_name = codec_name; + } + /* register the soc card */ byt_cht_es8316_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index f2c0fc415e52..b6a1cfeec830 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -713,7 +713,7 @@ static struct snd_soc_card byt_rt5640_card = { .fully_routed = true, }; -static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN]; static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */ static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ @@ -762,7 +762,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); if (i2c_name) { snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), "%s%s", "i2c-", i2c_name); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index d955836c6870..456526a93dd5 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -38,6 +38,8 @@ enum { BYT_RT5651_DMIC_MAP, BYT_RT5651_IN1_MAP, BYT_RT5651_IN2_MAP, + BYT_RT5651_IN1_IN2_MAP, + BYT_RT5651_IN3_MAP, }; #define BYT_RT5651_MAP(quirk) ((quirk) & GENMASK(7, 0)) @@ -62,6 +64,8 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk IN1_MAP enabled"); if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP) dev_info(dev, "quirk IN2_MAP enabled"); + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN3_MAP) + dev_info(dev, "quirk IN3_MAP enabled"); if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN) dev_info(dev, "quirk DMIC enabled"); if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) @@ -127,6 +131,7 @@ static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), @@ -138,6 +143,7 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { {"Headset Mic", NULL, "Platform Clock"}, {"Internal Mic", NULL, "Platform Clock"}, {"Speaker", NULL, "Platform Clock"}, + {"Line In", NULL, "Platform Clock"}, {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, @@ -151,6 +157,9 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { {"Headphone", NULL, "HPOR"}, {"Speaker", NULL, "LOUTL"}, {"Speaker", NULL, "LOUTR"}, + {"IN2P", NULL, "Line In"}, + {"IN2N", NULL, "Line In"}, + }; static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = { @@ -171,11 +180,25 @@ static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_map[] = { {"IN2P", NULL, "Internal Mic"}, }; +static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_in2_map[] = { + {"Internal Mic", NULL, "micbias1"}, + {"IN1P", NULL, "Internal Mic"}, + {"IN2P", NULL, "Internal Mic"}, + {"IN3P", NULL, "Headset Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5651_intmic_in3_map[] = { + {"Internal Mic", NULL, "micbias1"}, + {"IN3P", NULL, "Headset Mic"}, + {"IN1P", NULL, "Internal Mic"}, +}; + static const struct snd_kcontrol_new byt_rt5651_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Internal Mic"), SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Line In"), }; static struct snd_soc_jack_pin bytcr_jack_pins[] = { @@ -247,8 +270,16 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), }, - .driver_data = (void *)(BYT_RT5651_DMIC_MAP | - BYT_RT5651_DMIC_EN), + .driver_data = (void *)(BYT_RT5651_IN3_MAP), + }, + { + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ADI"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Turbot"), + }, + .driver_data = (void *)(BYT_RT5651_MCLK_EN | + BYT_RT5651_IN3_MAP), }, { .callback = byt_rt5651_quirk_cb, @@ -256,7 +287,8 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), }, - .driver_data = (void *)(BYT_RT5651_IN2_MAP), + .driver_data = (void *)(BYT_RT5651_MCLK_EN | + BYT_RT5651_IN1_IN2_MAP), }, {} }; @@ -281,6 +313,14 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) custom_map = byt_rt5651_intmic_in2_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_in2_map); break; + case BYT_RT5651_IN1_IN2_MAP: + custom_map = byt_rt5651_intmic_in1_in2_map; + num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_in2_map); + break; + case BYT_RT5651_IN3_MAP: + custom_map = byt_rt5651_intmic_in3_map; + num_routes = ARRAY_SIZE(byt_rt5651_intmic_in3_map); + break; default: custom_map = byt_rt5651_intmic_dmic_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map); @@ -469,7 +509,7 @@ static struct snd_soc_card byt_rt5651_card = { .fully_routed = true, }; -static char byt_rt5651_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { @@ -499,7 +539,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); if (i2c_name) { snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name), "%s%s", "i2c-", i2c_name); diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 18d129caa974..31641aab62cd 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -49,7 +49,7 @@ struct cht_acpi_card { struct cht_mc_private { struct snd_soc_jack jack; struct cht_acpi_card *acpi_card; - char codec_name[16]; + char codec_name[SND_ACPI_I2C_ID_LEN]; struct clk *mclk; }; @@ -118,6 +118,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_MIC("Int Analog Mic", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), @@ -128,6 +129,8 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { {"IN1N", NULL, "Headset Mic"}, {"DMIC L1", NULL, "Int Mic"}, {"DMIC R1", NULL, "Int Mic"}, + {"IN2P", NULL, "Int Analog Mic"}, + {"IN2N", NULL, "Int Analog Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, {"Ext Spk", NULL, "SPOL"}, @@ -135,6 +138,9 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, + {"Int Analog Mic", NULL, "Platform Clock"}, + {"Int Analog Mic", NULL, "micbias1"}, + {"Int Analog Mic", NULL, "micbias2"}, {"Ext Spk", NULL, "Platform Clock"}, }; @@ -189,6 +195,7 @@ static const struct snd_kcontrol_new cht_mc_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Int Analog Mic"), SOC_DAPM_PIN_SWITCH("Ext Spk"), }; @@ -499,7 +506,7 @@ static struct cht_acpi_card snd_soc_cards[] = { {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650}, }; -static char cht_rt5645_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char cht_rt5645_codec_name[SND_ACPI_I2C_ID_LEN]; static char cht_rt5645_codec_aif_name[12]; /* = "rt5645-aif[1|2]" */ static char cht_rt5645_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ @@ -566,7 +573,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); if (i2c_name) { snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name), "%s%s", "i2c-", i2c_name); diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index f8f21eee9b2d..c14a52d2f714 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -35,7 +35,7 @@ struct cht_mc_private { struct snd_soc_jack headset; - char codec_name[16]; + char codec_name[SND_ACPI_I2C_ID_LEN]; struct clk *mclk; }; @@ -396,7 +396,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* fixup codec name based on HID */ if (mach) { - i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); if (i2c_name) { snprintf(drv->codec_name, sizeof(drv->codec_name), "i2c-%s", i2c_name); diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index 5e1ea0371c90..3c5160779204 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -76,7 +76,7 @@ static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream, } /* set correct codec filter for DAI format and clock config */ - snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000); + snd_soc_component_update_bits(codec_dai->component, 0x83, 0xffff, 0x8000); return ret; } diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 6f9a8bcf20f3..f5df6bca3156 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -28,6 +28,9 @@ #include "../../codecs/rt5663.h" #include "../../codecs/hdac_hdmi.h" #include "../skylake/skl.h" +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #define KBL_REALTEK_CODEC_DAI "rt5663-aif" #define KBL_MAXIM_CODEC_DAI "max98927-aif1" @@ -48,6 +51,8 @@ struct kbl_hdmi_pcm { struct kbl_rt5663_private { struct snd_soc_jack kabylake_headset; struct list_head hdmi_pcm_list; + struct clk *mclk; + struct clk *sclk; }; enum { @@ -69,6 +74,61 @@ static const struct snd_kcontrol_new kabylake_controls[] = { SOC_DAPM_PIN_SWITCH("Right Spk"), }; +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct kbl_rt5663_private *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + /* + * MCLK/SCLK need to be ON early for a successful synchronization of + * codec internal clock. And the clocks are turned off during + * POST_PMD after the stream is stopped. + */ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable MCLK */ + ret = clk_set_rate(priv->mclk, 24000000); + if (ret < 0) { + dev_err(card->dev, "Can't set rate for mclk, err: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, "Can't enable mclk, err: %d\n", ret); + return ret; + } + + /* Enable SCLK */ + ret = clk_set_rate(priv->sclk, 3072000); + if (ret < 0) { + dev_err(card->dev, "Can't set rate for sclk, err: %d\n", + ret); + clk_disable_unprepare(priv->mclk); + return ret; + } + + ret = clk_prepare_enable(priv->sclk); + if (ret < 0) { + dev_err(card->dev, "Can't enable sclk, err: %d\n", ret); + clk_disable_unprepare(priv->mclk); + } + break; + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(priv->mclk); + clk_disable_unprepare(priv->sclk); + break; + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -78,11 +138,14 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), SND_SOC_DAPM_SPK("HDMI3", NULL), - + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route kabylake_map[] = { /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "Platform Clock" }, { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -91,6 +154,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = { { "Right Spk", NULL, "Right BE_OUT" }, /* other jacks */ + { "Headset Mic", NULL, "Platform Clock" }, { "IN1P", NULL, "Headset Mic" }, { "IN1N", NULL, "Headset Mic" }, { "DMic", NULL, "SoC DMIC" }, @@ -101,7 +165,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = { { "ssp0 Tx", NULL, "spk_out" }, { "AIF Playback", NULL, "ssp1 Tx" }, - { "ssp1 Tx", NULL, "hs_out" }, + { "ssp1 Tx", NULL, "codec1_out" }, { "hs_in", NULL, "ssp1 Rx" }, { "ssp1 Rx", NULL, "AIF Capture" }, @@ -225,7 +289,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) } jack = &ctx->kabylake_headset; - snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); @@ -901,6 +965,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_rt5663_private *ctx; struct skl_machine_pdata *pdata; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); if (!ctx) @@ -919,6 +984,34 @@ static int kabylake_audio_probe(struct platform_device *pdev) dmic_constraints = pdata->dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; + ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk"); + if (IS_ERR(ctx->mclk)) { + ret = PTR_ERR(ctx->mclk); + if (ret == -ENOENT) { + dev_info(&pdev->dev, + "Failed to get ssp1_sclk, defer probe\n"); + return -EPROBE_DEFER; + } + + dev_err(&pdev->dev, "Failed to get ssp1_mclk with err:%d\n", + ret); + return ret; + } + + ctx->sclk = devm_clk_get(&pdev->dev, "ssp1_sclk"); + if (IS_ERR(ctx->sclk)) { + ret = PTR_ERR(ctx->sclk); + if (ret == -ENOENT) { + dev_info(&pdev->dev, + "Failed to get ssp1_sclk, defer probe\n"); + return -EPROBE_DEFER; + } + + dev_err(&pdev->dev, "Failed to get ssp1_sclk with err:%d\n", + ret); + return ret; + } + return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card); } diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 6072164f2d43..90ea98f01c4c 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -109,7 +109,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = { { "ssp0 Tx", NULL, "spk_out" }, { "AIF Playback", NULL, "ssp1 Tx" }, - { "ssp1 Tx", NULL, "hs_out" }, + { "ssp1 Tx", NULL, "codec1_out" }, { "hs_in", NULL, "ssp1 Rx" }, { "ssp1 Rx", NULL, "AIF Capture" }, @@ -195,7 +195,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) } jack = &ctx->kabylake_headset; - snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); diff --git a/sound/soc/intel/boards/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c deleted file mode 100644 index 6f44acfb4aae..000000000000 --- a/sound/soc/intel/boards/mfld_machine.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform - * - * Copyright (C) 2010 Intel Corp - * Author: Vinod Koul <vinod.koul@intel.com> - * Author: Harsha Priya <priya.harsha@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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; version 2 of the License. - * - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/init.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/module.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> -#include <sound/jack.h> -#include "../codecs/sn95031.h" - -#define MID_MONO 1 -#define MID_STEREO 2 -#define MID_MAX_CAP 5 -#define MFLD_JACK_INSERT 0x04 - -enum soc_mic_bias_zones { - MFLD_MV_START = 0, - /* mic bias volutage range for Headphones*/ - MFLD_MV_HP = 400, - /* mic bias volutage range for American Headset*/ - MFLD_MV_AM_HS = 650, - /* mic bias volutage range for Headset*/ - MFLD_MV_HS = 2000, - MFLD_MV_UNDEFINED, -}; - -static unsigned int hs_switch; -static unsigned int lo_dac; -static struct snd_soc_codec *mfld_codec; - -struct mfld_mc_private { - void __iomem *int_base; - u8 interrupt_status; -}; - -struct snd_soc_jack mfld_jack; - -/*Headset jack detection DAPM pins */ -static struct snd_soc_jack_pin mfld_jack_pins[] = { - { - .pin = "Headphones", - .mask = SND_JACK_HEADPHONE, - }, - { - .pin = "AMIC1", - .mask = SND_JACK_MICROPHONE, - }, -}; - -/* jack detection voltage zones */ -static struct snd_soc_jack_zone mfld_zones[] = { - {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE}, - {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET}, -}; - -/* sound card controls */ -static const char * const headset_switch_text[] = {"Earpiece", "Headset"}; - -static const char * const lo_text[] = {"Vibra", "Headset", "IHF", "None"}; - -static const struct soc_enum headset_enum = - SOC_ENUM_SINGLE_EXT(2, headset_switch_text); - -static const struct soc_enum lo_enum = - SOC_ENUM_SINGLE_EXT(4, lo_text); - -static int headset_get_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = hs_switch; - return 0; -} - -static int headset_set_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_context *dapm = &card->dapm; - - if (ucontrol->value.enumerated.item[0] == hs_switch) - return 0; - - snd_soc_dapm_mutex_lock(dapm); - - if (ucontrol->value.enumerated.item[0]) { - pr_debug("hs_set HS path\n"); - snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); - snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); - } else { - pr_debug("hs_set EP path\n"); - snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); - snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); - } - - snd_soc_dapm_sync_unlocked(dapm); - - snd_soc_dapm_mutex_unlock(dapm); - - hs_switch = ucontrol->value.enumerated.item[0]; - - return 0; -} - -static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm) -{ - snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL"); - snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR"); - snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL"); - snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR"); - snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT"); - snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT"); - if (hs_switch) { - snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); - snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); - } else { - snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); - snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); - } -} - -static int lo_get_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = lo_dac; - return 0; -} - -static int lo_set_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_context *dapm = &card->dapm; - - if (ucontrol->value.enumerated.item[0] == lo_dac) - return 0; - - snd_soc_dapm_mutex_lock(dapm); - - /* we dont want to work with last state of lineout so just enable all - * pins and then disable pins not required - */ - lo_enable_out_pins(dapm); - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - pr_debug("set vibra path\n"); - snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT"); - snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT"); - snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0); - break; - - case 1: - pr_debug("set hs path\n"); - snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); - snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); - snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x22); - break; - - case 2: - pr_debug("set spkr path\n"); - snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL"); - snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR"); - snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x44); - break; - - case 3: - pr_debug("set null path\n"); - snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL"); - snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR"); - snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x66); - break; - } - - snd_soc_dapm_sync_unlocked(dapm); - - snd_soc_dapm_mutex_unlock(dapm); - - lo_dac = ucontrol->value.enumerated.item[0]; - return 0; -} - -static const struct snd_kcontrol_new mfld_snd_controls[] = { - SOC_ENUM_EXT("Playback Switch", headset_enum, - headset_get_switch, headset_set_switch), - SOC_ENUM_EXT("Lineout Mux", lo_enum, - lo_get_switch, lo_set_switch), -}; - -static const struct snd_soc_dapm_widget mfld_widgets[] = { - SND_SOC_DAPM_HP("Headphones", NULL), - SND_SOC_DAPM_MIC("Mic", NULL), -}; - -static const struct snd_soc_dapm_route mfld_map[] = { - {"Headphones", NULL, "HPOUTR"}, - {"Headphones", NULL, "HPOUTL"}, - {"Mic", NULL, "AMIC1"}, -}; - -static void mfld_jack_check(unsigned int intr_status) -{ - struct mfld_jack_data jack_data; - - if (!mfld_codec) - return; - - jack_data.mfld_jack = &mfld_jack; - jack_data.intr_id = intr_status; - - sn95031_jack_detection(mfld_codec, &jack_data); - /* TODO: add american headset detection post gpiolib support */ -} - -static int mfld_init(struct snd_soc_pcm_runtime *runtime) -{ - struct snd_soc_dapm_context *dapm = &runtime->card->dapm; - int ret_val; - - /* default is earpiece pin, userspace sets it explcitly */ - snd_soc_dapm_disable_pin(dapm, "Headphones"); - /* default is lineout NC, userspace sets it explcitly */ - snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); - snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); - lo_dac = 3; - hs_switch = 0; - /* we dont use linein in this so set to NC */ - snd_soc_dapm_disable_pin(dapm, "LINEINL"); - snd_soc_dapm_disable_pin(dapm, "LINEINR"); - - /* Headset and button jack detection */ - ret_val = snd_soc_card_jack_new(runtime->card, - "Intel(R) MID Audio Jack", SND_JACK_HEADSET | - SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack, - mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins)); - if (ret_val) { - pr_err("jack creation failed\n"); - return ret_val; - } - - ret_val = snd_soc_jack_add_zones(&mfld_jack, - ARRAY_SIZE(mfld_zones), mfld_zones); - if (ret_val) { - pr_err("adding jack zones failed\n"); - return ret_val; - } - - mfld_codec = runtime->codec; - - /* we want to check if anything is inserted at boot, - * so send a fake event to codec and it will read adc - * to find if anything is there or not */ - mfld_jack_check(MFLD_JACK_INSERT); - return ret_val; -} - -static struct snd_soc_dai_link mfld_msic_dailink[] = { - { - .name = "Medfield Headset", - .stream_name = "Headset", - .cpu_dai_name = "Headset-cpu-dai", - .codec_dai_name = "SN95031 Headset", - .codec_name = "sn95031", - .platform_name = "sst-platform", - .init = mfld_init, - }, - { - .name = "Medfield Speaker", - .stream_name = "Speaker", - .cpu_dai_name = "Speaker-cpu-dai", - .codec_dai_name = "SN95031 Speaker", - .codec_name = "sn95031", - .platform_name = "sst-platform", - .init = NULL, - }, - { - .name = "Medfield Vibra", - .stream_name = "Vibra1", - .cpu_dai_name = "Vibra1-cpu-dai", - .codec_dai_name = "SN95031 Vibra1", - .codec_name = "sn95031", - .platform_name = "sst-platform", - .init = NULL, - }, - { - .name = "Medfield Haptics", - .stream_name = "Vibra2", - .cpu_dai_name = "Vibra2-cpu-dai", - .codec_dai_name = "SN95031 Vibra2", - .codec_name = "sn95031", - .platform_name = "sst-platform", - .init = NULL, - }, - { - .name = "Medfield Compress", - .stream_name = "Speaker", - .cpu_dai_name = "Compress-cpu-dai", - .codec_dai_name = "SN95031 Speaker", - .codec_name = "sn95031", - .platform_name = "sst-platform", - .init = NULL, - }, -}; - -/* SoC card */ -static struct snd_soc_card snd_soc_card_mfld = { - .name = "medfield_audio", - .owner = THIS_MODULE, - .dai_link = mfld_msic_dailink, - .num_links = ARRAY_SIZE(mfld_msic_dailink), - - .controls = mfld_snd_controls, - .num_controls = ARRAY_SIZE(mfld_snd_controls), - .dapm_widgets = mfld_widgets, - .num_dapm_widgets = ARRAY_SIZE(mfld_widgets), - .dapm_routes = mfld_map, - .num_dapm_routes = ARRAY_SIZE(mfld_map), -}; - -static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) -{ - struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; - - memcpy_fromio(&mc_private->interrupt_status, - ((void *)(mc_private->int_base)), - sizeof(u8)); - return IRQ_WAKE_THREAD; -} - -static irqreturn_t snd_mfld_jack_detection(int irq, void *data) -{ - struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; - - mfld_jack_check(mc_drv_ctx->interrupt_status); - - return IRQ_HANDLED; -} - -static int snd_mfld_mc_probe(struct platform_device *pdev) -{ - int ret_val = 0, irq; - struct mfld_mc_private *mc_drv_ctx; - struct resource *irq_mem; - - pr_debug("snd_mfld_mc_probe called\n"); - - /* retrive the irq number */ - irq = platform_get_irq(pdev, 0); - - /* audio interrupt base of SRAM location where - * interrupts are stored by System FW */ - mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC); - if (!mc_drv_ctx) - return -ENOMEM; - - irq_mem = platform_get_resource_byname( - pdev, IORESOURCE_MEM, "IRQ_BASE"); - if (!irq_mem) { - pr_err("no mem resource given\n"); - return -ENODEV; - } - mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start, - resource_size(irq_mem)); - if (!mc_drv_ctx->int_base) { - pr_err("Mapping of cache failed\n"); - return -ENOMEM; - } - /* register for interrupt */ - ret_val = devm_request_threaded_irq(&pdev->dev, irq, - snd_mfld_jack_intr_handler, - snd_mfld_jack_detection, - IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); - if (ret_val) { - pr_err("cannot register IRQ\n"); - return ret_val; - } - /* register the soc card */ - snd_soc_card_mfld.dev = &pdev->dev; - ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld); - if (ret_val) { - pr_debug("snd_soc_register_card failed %d\n", ret_val); - return ret_val; - } - platform_set_drvdata(pdev, mc_drv_ctx); - pr_debug("successfully exited probe\n"); - return 0; -} - -static struct platform_driver snd_mfld_mc_driver = { - .driver = { - .name = "msic_audio", - }, - .probe = snd_mfld_mc_probe, -}; - -module_platform_driver(snd_mfld_mc_driver); - -MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver"); -MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); -MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:msic-audio"); diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index 11c0805393ff..fd82f4b1d4a0 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -269,7 +269,7 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, */ timeout = jiffies + msecs_to_jiffies(time); - while (((sst_dsp_shim_read_unlocked(ctx, offset) & mask) != target) + while ((((reg = sst_dsp_shim_read_unlocked(ctx, offset)) & mask) != target) && time_before(jiffies, timeout)) { k++; if (k > 10) @@ -278,8 +278,6 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, usleep_range(s, 2*s); } - reg = sst_dsp_shim_read_unlocked(ctx, offset); - if ((reg & mask) == target) { dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s successful\n", reg, operation); diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index d1ccbecd141f..86f6e1d801af 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -14,3 +14,8 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-utils.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o + +#Skylake Clock device support +snd-soc-skl-ssp-clk-objs := skl-ssp-clk.o + +obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_SSP_CLK) += snd-soc-skl-ssp-clk.o diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 4524211960e4..440bca7afbf1 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -595,7 +595,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3); skl->d0i3.state = SKL_DSP_D0I3_NONE; - return 0; + return skl_dsp_acquire_irq(sst); } EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 387de388ce29..245df1067ba8 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -458,7 +458,7 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, cnl->boot_complete = false; init_waitqueue_head(&cnl->boot_wait); - return 0; + return skl_dsp_acquire_irq(sst); } EXPORT_SYMBOL_GPL(cnl_sst_dsp_init); diff --git a/sound/soc/intel/skylake/skl-i2s.h b/sound/soc/intel/skylake/skl-i2s.h new file mode 100644 index 000000000000..ad0a1bbca13c --- /dev/null +++ b/sound/soc/intel/skylake/skl-i2s.h @@ -0,0 +1,95 @@ +/* + * skl-i2s.h - i2s blob mapping + * + * Copyright (C) 2017 Intel Corp + * Author: Subhransu S. Prusty < subhransu.s.prusty@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SOUND_SOC_SKL_I2S_H +#define __SOUND_SOC_SKL_I2S_H + +#define SKL_I2S_MAX_TIME_SLOTS 8 +#define SKL_MCLK_DIV_CLK_SRC_MASK GENMASK(17, 16) + +#define SKL_MNDSS_DIV_CLK_SRC_MASK GENMASK(21, 20) +#define SKL_SHIFT(x) (ffs(x) - 1) +#define SKL_MCLK_DIV_RATIO_MASK GENMASK(11, 0) + +#define is_legacy_blob(x) (x.signature != 0xEE) +#define ext_to_legacy_blob(i2s_config_blob_ext) \ + ((struct skl_i2s_config_blob_legacy *) i2s_config_blob_ext) + +#define get_clk_src(mclk, mask) \ + ((mclk.mdivctrl & mask) >> SKL_SHIFT(mask)) +struct skl_i2s_config { + u32 ssc0; + u32 ssc1; + u32 sscto; + u32 sspsp; + u32 sstsa; + u32 ssrsa; + u32 ssc2; + u32 sspsp2; + u32 ssc3; + u32 ssioc; +} __packed; + +struct skl_i2s_config_mclk { + u32 mdivctrl; + u32 mdivr; +}; + +struct skl_i2s_config_mclk_ext { + u32 mdivctrl; + u32 mdivr_count; + u32 mdivr[0]; +} __packed; + +struct skl_i2s_config_blob_signature { + u32 minor_ver : 8; + u32 major_ver : 8; + u32 resvdz : 8; + u32 signature : 8; +} __packed; + +struct skl_i2s_config_blob_header { + struct skl_i2s_config_blob_signature sig; + u32 size; +}; + +/** + * struct skl_i2s_config_blob_legacy - Structure defines I2S Gateway + * configuration legacy blob + * + * @gtw_attr: Gateway attribute for the I2S Gateway + * @tdm_ts_group: TDM slot mapping against channels in the Gateway. + * @i2s_cfg: I2S HW registers + * @mclk: MCLK clock source and divider values + */ +struct skl_i2s_config_blob_legacy { + u32 gtw_attr; + u32 tdm_ts_group[SKL_I2S_MAX_TIME_SLOTS]; + struct skl_i2s_config i2s_cfg; + struct skl_i2s_config_mclk mclk; +}; + +struct skl_i2s_config_blob_ext { + u32 gtw_attr; + struct skl_i2s_config_blob_header hdr; + u32 tdm_ts_group[SKL_I2S_MAX_TIME_SLOTS]; + struct skl_i2s_config i2s_cfg; + struct skl_i2s_config_mclk_ext mclk; +} __packed; +#endif /* __SOUND_SOC_SKL_I2S_H */ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 61b5bfa79d13..60d76adade43 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -55,6 +55,19 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) return 0; } +#define SKL_ASTATE_PARAM_ID 4 + +void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) +{ + struct skl_ipc_large_config_msg msg = {0}; + + msg.large_param_id = SKL_ASTATE_PARAM_ID; + msg.param_data_size = (cnt * sizeof(struct skl_astate_param) + + sizeof(cnt)); + + skl_ipc_set_large_config(&ctx->ipc, &msg, data); +} + #define NOTIFICATION_PARAM_ID 3 #define NOTIFICATION_MASK 0xf @@ -404,11 +417,20 @@ int skl_resume_dsp(struct skl *skl) if (skl->skl_sst->is_first_boot == true) return 0; + /* disable dynamic clock gating during fw and lib download */ + ctx->enable_miscbdcge(ctx->dev, false); + ret = skl_dsp_wake(ctx->dsp); + ctx->enable_miscbdcge(ctx->dev, true); if (ret < 0) return ret; skl_dsp_enable_notification(skl->skl_sst, false); + + if (skl->cfg.astate_cfg != NULL) { + skl_dsp_set_astate_cfg(skl->skl_sst, skl->cfg.astate_cfg->count, + skl->cfg.astate_cfg); + } return ret; } @@ -653,6 +675,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, kfree(dma_ctrl); return err; } +EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control); static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index d14c50a60289..b9b140275be0 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -19,6 +19,7 @@ */ #include <linux/pci.h> #include "skl.h" +#include "skl-i2s.h" #define NHLT_ACPI_HEADER_SIG "NHLT" @@ -27,6 +28,7 @@ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); + struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) { acpi_handle handle; @@ -43,7 +45,8 @@ struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL); if (obj && obj->type == ACPI_TYPE_BUFFER) { nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - nhlt_table = (struct nhlt_acpi_table *) + if (nhlt_ptr->length) + nhlt_table = (struct nhlt_acpi_table *) memremap(nhlt_ptr->min_addr, nhlt_ptr->length, MEMREMAP_WB); ACPI_FREE(obj); @@ -119,11 +122,16 @@ static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, if ((epnt->virtual_bus_id == instance_id) && (epnt->linktype == link_type) && - (epnt->direction == dirn) && - (epnt->device_type == dev_type)) - return true; - else - return false; + (epnt->direction == dirn)) { + /* do not check dev_type for DMIC link type */ + if (epnt->linktype == NHLT_LINK_DMIC) + return true; + + if (epnt->device_type == dev_type) + return true; + } + + return false; } struct nhlt_specific_cfg @@ -271,3 +279,173 @@ void skl_nhlt_remove_sysfs(struct skl *skl) sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); } + +/* + * Queries NHLT for all the fmt configuration for a particular endpoint and + * stores all possible rates supported in a rate table for the corresponding + * sclk/sclkfs. + */ +static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks, + struct nhlt_fmt *fmt, u8 id) +{ + struct skl_i2s_config_blob_ext *i2s_config_ext; + struct skl_i2s_config_blob_legacy *i2s_config; + struct skl_clk_parent_src *parent; + struct skl_ssp_clk *sclk, *sclkfs; + struct nhlt_fmt_cfg *fmt_cfg; + struct wav_fmt_ext *wav_fmt; + unsigned long rate = 0; + bool present = false; + int rate_index = 0; + u16 channels, bps; + u8 clk_src; + int i, j; + u32 fs; + + sclk = &ssp_clks[SKL_SCLK_OFS]; + sclkfs = &ssp_clks[SKL_SCLKFS_OFS]; + + if (fmt->fmt_count == 0) + return; + + for (i = 0; i < fmt->fmt_count; i++) { + fmt_cfg = &fmt->fmt_config[i]; + wav_fmt = &fmt_cfg->fmt_ext; + + channels = wav_fmt->fmt.channels; + bps = wav_fmt->fmt.bits_per_sample; + fs = wav_fmt->fmt.samples_per_sec; + + /* + * In case of TDM configuration on a ssp, there can + * be more than one blob in which channel masks are + * different for each usecase for a specific rate and bps. + * But the sclk rate will be generated for the total + * number of channels used for that endpoint. + * + * So for the given fs and bps, choose blob which has + * the superset of all channels for that endpoint and + * derive the rate. + */ + for (j = i; j < fmt->fmt_count; j++) { + fmt_cfg = &fmt->fmt_config[j]; + wav_fmt = &fmt_cfg->fmt_ext; + if ((fs == wav_fmt->fmt.samples_per_sec) && + (bps == wav_fmt->fmt.bits_per_sample)) + channels = max_t(u16, channels, + wav_fmt->fmt.channels); + } + + rate = channels * bps * fs; + + /* check if the rate is added already to the given SSP's sclk */ + for (j = 0; (j < SKL_MAX_CLK_RATES) && + (sclk[id].rate_cfg[j].rate != 0); j++) { + if (sclk[id].rate_cfg[j].rate == rate) { + present = true; + break; + } + } + + /* Fill rate and parent for sclk/sclkfs */ + if (!present) { + i2s_config_ext = (struct skl_i2s_config_blob_ext *) + fmt->fmt_config[0].config.caps; + + /* MCLK Divider Source Select */ + if (is_legacy_blob(i2s_config_ext->hdr.sig)) { + i2s_config = ext_to_legacy_blob(i2s_config_ext); + clk_src = get_clk_src(i2s_config->mclk, + SKL_MNDSS_DIV_CLK_SRC_MASK); + } else { + clk_src = get_clk_src(i2s_config_ext->mclk, + SKL_MNDSS_DIV_CLK_SRC_MASK); + } + + parent = skl_get_parent_clk(clk_src); + + /* + * Do not copy the config data if there is no parent + * clock available for this clock source select + */ + if (!parent) + continue; + + sclk[id].rate_cfg[rate_index].rate = rate; + sclk[id].rate_cfg[rate_index].config = fmt_cfg; + sclkfs[id].rate_cfg[rate_index].rate = rate; + sclkfs[id].rate_cfg[rate_index].config = fmt_cfg; + sclk[id].parent_name = parent->name; + sclkfs[id].parent_name = parent->name; + + rate_index++; + } + } +} + +static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk, + struct nhlt_fmt *fmt, u8 id) +{ + struct skl_i2s_config_blob_ext *i2s_config_ext; + struct skl_i2s_config_blob_legacy *i2s_config; + struct nhlt_specific_cfg *fmt_cfg; + struct skl_clk_parent_src *parent; + u32 clkdiv, div_ratio; + u8 clk_src; + + fmt_cfg = &fmt->fmt_config[0].config; + i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps; + + /* MCLK Divider Source Select and divider */ + if (is_legacy_blob(i2s_config_ext->hdr.sig)) { + i2s_config = ext_to_legacy_blob(i2s_config_ext); + clk_src = get_clk_src(i2s_config->mclk, + SKL_MCLK_DIV_CLK_SRC_MASK); + clkdiv = i2s_config->mclk.mdivr & + SKL_MCLK_DIV_RATIO_MASK; + } else { + clk_src = get_clk_src(i2s_config_ext->mclk, + SKL_MCLK_DIV_CLK_SRC_MASK); + clkdiv = i2s_config_ext->mclk.mdivr[0] & + SKL_MCLK_DIV_RATIO_MASK; + } + + /* bypass divider */ + div_ratio = 1; + + if (clkdiv != SKL_MCLK_DIV_RATIO_MASK) + /* Divider is 2 + clkdiv */ + div_ratio = clkdiv + 2; + + /* Calculate MCLK rate from source using div value */ + parent = skl_get_parent_clk(clk_src); + if (!parent) + return; + + mclk[id].rate_cfg[0].rate = parent->rate/div_ratio; + mclk[id].rate_cfg[0].config = &fmt->fmt_config[0]; + mclk[id].parent_name = parent->name; +} + +void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks) +{ + struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; + struct nhlt_endpoint *epnt; + struct nhlt_fmt *fmt; + int i; + u8 id; + + epnt = (struct nhlt_endpoint *)nhlt->desc; + for (i = 0; i < nhlt->endpoint_count; i++) { + if (epnt->linktype == NHLT_LINK_SSP) { + id = epnt->virtual_bus_id; + + fmt = (struct nhlt_fmt *)(epnt->config.caps + + epnt->config.size); + + skl_get_ssp_clks(skl, ssp_clks, fmt, id); + skl_get_mclk(skl, ssp_clks, fmt, id); + } + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } +} diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1dd97479e0c0..e46828533826 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -537,7 +537,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); - link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); + link = snd_hdac_ext_bus_get_link(ebus, codec_dai->component->name); if (!link) return -EINVAL; @@ -620,7 +620,7 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, link_dev->link_prepared = 0; - link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); + link = snd_hdac_ext_bus_get_link(ebus, rtd->codec_dai->component->name); if (!link) return -EINVAL; @@ -1343,7 +1343,11 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform) return -EIO; } + /* disable dynamic clock gating during fw and lib download */ + skl->skl_sst->enable_miscbdcge(platform->dev, false); + ret = ops->init_fw(platform->dev, skl->skl_sst); + skl->skl_sst->enable_miscbdcge(platform->dev, true); if (ret < 0) { dev_err(platform->dev, "Failed to boot first fw: %d\n", ret); return ret; @@ -1351,6 +1355,12 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform) skl_populate_modules(skl); skl->skl_sst->update_d0i3c = skl_update_d0i3c; skl_dsp_enable_notification(skl->skl_sst, false); + + if (skl->cfg.astate_cfg != NULL) { + skl_dsp_set_astate_cfg(skl->skl_sst, + skl->cfg.astate_cfg->count, + skl->cfg.astate_cfg); + } } pm_runtime_mark_last_busy(platform->dev); pm_runtime_put_autosuspend(platform->dev); diff --git a/sound/soc/intel/skylake/skl-ssp-clk.c b/sound/soc/intel/skylake/skl-ssp-clk.c new file mode 100644 index 000000000000..7fbddf5e3b00 --- /dev/null +++ b/sound/soc/intel/skylake/skl-ssp-clk.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-17 Intel Corporation + +/* + * skl-ssp-clk.c - ASoC skylake ssp clock driver + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include "skl.h" +#include "skl-ssp-clk.h" +#include "skl-topology.h" + +#define to_skl_clk(_hw) container_of(_hw, struct skl_clk, hw) + +struct skl_clk_parent { + struct clk_hw *hw; + struct clk_lookup *lookup; +}; + +struct skl_clk { + struct clk_hw hw; + struct clk_lookup *lookup; + unsigned long rate; + struct skl_clk_pdata *pdata; + u32 id; +}; + +struct skl_clk_data { + struct skl_clk_parent parent[SKL_MAX_CLK_SRC]; + struct skl_clk *clk[SKL_MAX_CLK_CNT]; + u8 avail_clk_cnt; +}; + +static int skl_get_clk_type(u32 index) +{ + switch (index) { + case 0 ... (SKL_SCLK_OFS - 1): + return SKL_MCLK; + + case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1): + return SKL_SCLK; + + case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1): + return SKL_SCLK_FS; + + default: + return -EINVAL; + } +} + +static int skl_get_vbus_id(u32 index, u8 clk_type) +{ + switch (clk_type) { + case SKL_MCLK: + return index; + + case SKL_SCLK: + return index - SKL_SCLK_OFS; + + case SKL_SCLK_FS: + return index - SKL_SCLKFS_OFS; + + default: + return -EINVAL; + } +} + +static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type) +{ + struct nhlt_fmt_cfg *fmt_cfg; + union skl_clk_ctrl_ipc *ipc; + struct wav_fmt *wfmt; + + if (!rcfg) + return; + + ipc = &rcfg->dma_ctl_ipc; + if (clk_type == SKL_SCLK_FS) { + fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config; + wfmt = &fmt_cfg->fmt_ext.fmt; + + /* Remove TLV Header size */ + ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) - + sizeof(struct skl_tlv_hdr); + ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec; + ipc->sclk_fs.bit_depth = wfmt->bits_per_sample; + ipc->sclk_fs.valid_bit_depth = + fmt_cfg->fmt_ext.sample.valid_bits_per_sample; + ipc->sclk_fs.number_of_channels = wfmt->channels; + } else { + ipc->mclk.hdr.type = DMA_CLK_CONTROLS; + /* Remove TLV Header size */ + ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) - + sizeof(struct skl_tlv_hdr); + } +} + +/* Sends dma control IPC to turn the clock ON/OFF */ +static int skl_send_clk_dma_control(struct skl *skl, + struct skl_clk_rate_cfg_table *rcfg, + u32 vbus_id, u8 clk_type, + bool enable) +{ + struct nhlt_specific_cfg *sp_cfg; + u32 i2s_config_size, node_id = 0; + struct nhlt_fmt_cfg *fmt_cfg; + union skl_clk_ctrl_ipc *ipc; + void *i2s_config = NULL; + u8 *data, size; + int ret; + + if (!rcfg) + return -EIO; + + ipc = &rcfg->dma_ctl_ipc; + fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config; + sp_cfg = &fmt_cfg->config; + + if (clk_type == SKL_SCLK_FS) { + ipc->sclk_fs.hdr.type = + enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP; + data = (u8 *)&ipc->sclk_fs; + size = sizeof(struct skl_dmactrl_sclkfs_cfg); + } else { + /* 1 to enable mclk, 0 to enable sclk */ + if (clk_type == SKL_SCLK) + ipc->mclk.mclk = 0; + else + ipc->mclk.mclk = 1; + + ipc->mclk.keep_running = enable; + ipc->mclk.warm_up_over = enable; + ipc->mclk.clk_stop_over = !enable; + data = (u8 *)&ipc->mclk; + size = sizeof(struct skl_dmactrl_mclk_cfg); + } + + i2s_config_size = sp_cfg->size + size; + i2s_config = kzalloc(i2s_config_size, GFP_KERNEL); + if (!i2s_config) + return -ENOMEM; + + /* copy blob */ + memcpy(i2s_config, sp_cfg->caps, sp_cfg->size); + + /* copy additional dma controls information */ + memcpy(i2s_config + sp_cfg->size, data, size); + + node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4)); + ret = skl_dsp_set_dma_control(skl->skl_sst, (u32 *)i2s_config, + i2s_config_size, node_id); + kfree(i2s_config); + + return ret; +} + +static struct skl_clk_rate_cfg_table *skl_get_rate_cfg( + struct skl_clk_rate_cfg_table *rcfg, + unsigned long rate) +{ + int i; + + for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) { + if (rcfg[i].rate == rate) + return &rcfg[i]; + } + + return NULL; +} + +static int skl_clk_change_status(struct skl_clk *clkdev, + bool enable) +{ + struct skl_clk_rate_cfg_table *rcfg; + int vbus_id, clk_type; + + clk_type = skl_get_clk_type(clkdev->id); + if (clk_type < 0) + return clk_type; + + vbus_id = skl_get_vbus_id(clkdev->id, clk_type); + if (vbus_id < 0) + return vbus_id; + + rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg, + clkdev->rate); + if (!rcfg) + return -EINVAL; + + return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg, + vbus_id, clk_type, enable); +} + +static int skl_clk_prepare(struct clk_hw *hw) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + + return skl_clk_change_status(clkdev, true); +} + +static void skl_clk_unprepare(struct clk_hw *hw) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + + skl_clk_change_status(clkdev, false); +} + +static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + struct skl_clk_rate_cfg_table *rcfg; + int clk_type; + + if (!rate) + return -EINVAL; + + rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg, + rate); + if (!rcfg) + return -EINVAL; + + clk_type = skl_get_clk_type(clkdev->id); + if (clk_type < 0) + return clk_type; + + skl_fill_clk_ipc(rcfg, clk_type); + clkdev->rate = rate; + + return 0; +} + +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + + if (clkdev->rate) + return clkdev->rate; + + return 0; +} + +/* Not supported by clk driver. Implemented to satisfy clk fw */ +long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +/* + * prepare/unprepare are used instead of enable/disable as IPC will be sent + * in non-atomic context. + */ +static const struct clk_ops skl_clk_ops = { + .prepare = skl_clk_prepare, + .unprepare = skl_clk_unprepare, + .set_rate = skl_clk_set_rate, + .round_rate = skl_clk_round_rate, + .recalc_rate = skl_clk_recalc_rate, +}; + +static void unregister_parent_src_clk(struct skl_clk_parent *pclk, + unsigned int id) +{ + while (id--) { + clkdev_drop(pclk[id].lookup); + clk_hw_unregister_fixed_rate(pclk[id].hw); + } +} + +static void unregister_src_clk(struct skl_clk_data *dclk) +{ + u8 cnt = dclk->avail_clk_cnt; + + while (cnt--) + clkdev_drop(dclk->clk[cnt]->lookup); +} + +static int skl_register_parent_clks(struct device *dev, + struct skl_clk_parent *parent, + struct skl_clk_parent_src *pclk) +{ + int i, ret; + + for (i = 0; i < SKL_MAX_CLK_SRC; i++) { + + /* Register Parent clock */ + parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name, + pclk[i].parent_name, 0, pclk[i].rate); + if (IS_ERR(parent[i].hw)) { + ret = PTR_ERR(parent[i].hw); + goto err; + } + + parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name, + NULL); + if (!parent[i].lookup) { + clk_hw_unregister_fixed_rate(parent[i].hw); + ret = -ENOMEM; + goto err; + } + } + + return 0; +err: + unregister_parent_src_clk(parent, i); + return ret; +} + +/* Assign fmt_config to clk_data */ +static struct skl_clk *register_skl_clk(struct device *dev, + struct skl_ssp_clk *clk, + struct skl_clk_pdata *clk_pdata, int id) +{ + struct clk_init_data init; + struct skl_clk *clkdev; + int ret; + + clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL); + if (!clkdev) + return ERR_PTR(-ENOMEM); + + init.name = clk->name; + init.ops = &skl_clk_ops; + init.flags = CLK_SET_RATE_GATE; + init.parent_names = &clk->parent_name; + init.num_parents = 1; + clkdev->hw.init = &init; + clkdev->pdata = clk_pdata; + + clkdev->id = id; + ret = devm_clk_hw_register(dev, &clkdev->hw); + if (ret) { + clkdev = ERR_PTR(ret); + return clkdev; + } + + clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL); + if (!clkdev->lookup) + clkdev = ERR_PTR(-ENOMEM); + + return clkdev; +} + +static int skl_clk_dev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent_dev = dev->parent; + struct skl_clk_parent_src *parent_clks; + struct skl_clk_pdata *clk_pdata; + struct skl_clk_data *data; + struct skl_ssp_clk *clks; + int ret, i; + + clk_pdata = dev_get_platdata(&pdev->dev); + parent_clks = clk_pdata->parent_clks; + clks = clk_pdata->ssp_clks; + if (!parent_clks || !clks) + return -EIO; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* Register Parent clock */ + ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks); + if (ret < 0) + return ret; + + for (i = 0; i < clk_pdata->num_clks; i++) { + /* + * Only register valid clocks + * i.e. for which nhlt entry is present. + */ + if (clks[i].rate_cfg[0].rate == 0) + continue; + + data->clk[i] = register_skl_clk(dev, &clks[i], clk_pdata, i); + if (IS_ERR(data->clk[i])) { + ret = PTR_ERR(data->clk[i]); + goto err_unreg_skl_clk; + } + + data->avail_clk_cnt++; + } + + platform_set_drvdata(pdev, data); + + return 0; + +err_unreg_skl_clk: + unregister_src_clk(data); + unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC); + + return ret; +} + +static int skl_clk_dev_remove(struct platform_device *pdev) +{ + struct skl_clk_data *data; + + data = platform_get_drvdata(pdev); + unregister_src_clk(data); + unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC); + + return 0; +} + +static struct platform_driver skl_clk_driver = { + .driver = { + .name = "skl-ssp-clk", + }, + .probe = skl_clk_dev_probe, + .remove = skl_clk_dev_remove, +}; + +module_platform_driver(skl_clk_driver); + +MODULE_DESCRIPTION("Skylake clock driver"); +MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>"); +MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl-ssp-clk"); diff --git a/sound/soc/intel/skylake/skl-ssp-clk.h b/sound/soc/intel/skylake/skl-ssp-clk.h new file mode 100644 index 000000000000..d1be50f96c05 --- /dev/null +++ b/sound/soc/intel/skylake/skl-ssp-clk.h @@ -0,0 +1,117 @@ +/* + * skl-ssp-clk.h - Skylake ssp clock information and ipc structure + * + * Copyright (C) 2017 Intel Corp + * Author: Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com> + * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef SOUND_SOC_SKL_SSP_CLK_H +#define SOUND_SOC_SKL_SSP_CLK_H + +#define SKL_MAX_SSP 6 +/* xtal/cardinal/pll, parent of ssp clocks and mclk */ +#define SKL_MAX_CLK_SRC 3 +#define SKL_MAX_SSP_CLK_TYPES 3 /* mclk, sclk, sclkfs */ + +#define SKL_MAX_CLK_CNT (SKL_MAX_SSP * SKL_MAX_SSP_CLK_TYPES) + +/* Max number of configurations supported for each clock */ +#define SKL_MAX_CLK_RATES 10 + +#define SKL_SCLK_OFS SKL_MAX_SSP +#define SKL_SCLKFS_OFS (SKL_SCLK_OFS + SKL_MAX_SSP) + +enum skl_clk_type { + SKL_MCLK, + SKL_SCLK, + SKL_SCLK_FS, +}; + +enum skl_clk_src_type { + SKL_XTAL, + SKL_CARDINAL, + SKL_PLL, +}; + +struct skl_clk_parent_src { + u8 clk_id; + const char *name; + unsigned long rate; + const char *parent_name; +}; + +struct skl_tlv_hdr { + u32 type; + u32 size; +}; + +struct skl_dmactrl_mclk_cfg { + struct skl_tlv_hdr hdr; + /* DMA Clk TLV params */ + u32 clk_warm_up:16; + u32 mclk:1; + u32 warm_up_over:1; + u32 rsvd0:14; + u32 clk_stop_delay:16; + u32 keep_running:1; + u32 clk_stop_over:1; + u32 rsvd1:14; +}; + +struct skl_dmactrl_sclkfs_cfg { + struct skl_tlv_hdr hdr; + /* DMA SClk&FS TLV params */ + u32 sampling_frequency; + u32 bit_depth; + u32 channel_map; + u32 channel_config; + u32 interleaving_style; + u32 number_of_channels : 8; + u32 valid_bit_depth : 8; + u32 sample_type : 8; + u32 reserved : 8; +}; + +union skl_clk_ctrl_ipc { + struct skl_dmactrl_mclk_cfg mclk; + struct skl_dmactrl_sclkfs_cfg sclk_fs; +}; + +struct skl_clk_rate_cfg_table { + unsigned long rate; + union skl_clk_ctrl_ipc dma_ctl_ipc; + void *config; +}; + +/* + * rate for mclk will be in rates[0]. For sclk and sclkfs, rates[] store + * all possible clocks ssp can generate for that platform. + */ +struct skl_ssp_clk { + const char *name; + const char *parent_name; + struct skl_clk_rate_cfg_table rate_cfg[SKL_MAX_CLK_RATES]; +}; + +struct skl_clk_pdata { + struct skl_clk_parent_src *parent_clks; + int num_clks; + struct skl_ssp_clk *ssp_clks; + void *pvt_data; +}; + +#endif /* SOUND_SOC_SKL_SSP_CLK_H */ diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 19ee1d4f3bdf..71e31ad0bb3f 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -435,16 +435,22 @@ struct sst_dsp *skl_dsp_ctx_init(struct device *dev, return NULL; } + return sst; +} + +int skl_dsp_acquire_irq(struct sst_dsp *sst) +{ + struct sst_dsp_device *sst_dev = sst->sst_dev; + int ret; + /* Register the ISR */ ret = request_threaded_irq(sst->irq, sst->ops->irq_handler, sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); - if (ret) { + if (ret) dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n", sst->irq); - return NULL; - } - return sst; + return ret; } void skl_dsp_free(struct sst_dsp *dsp) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index eba20d37ba8c..12fc9a73dc8a 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -206,6 +206,7 @@ int skl_cldma_wait_interruptible(struct sst_dsp *ctx); void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state); struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); +int skl_dsp_acquire_irq(struct sst_dsp *sst); bool is_skl_dsp_running(struct sst_dsp *ctx); unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx); @@ -251,6 +252,9 @@ void skl_freeup_uuid_list(struct skl_sst *ctx); int skl_dsp_strip_extended_manifest(struct firmware *fw); void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable); + +void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data); + int skl_sst_ctx_init(struct device *dev, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp, struct sst_dsp_device *skl_dev); diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 8ff89280d9fd..2ae405617876 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -178,7 +178,8 @@ static inline int skl_pvtid_128(struct uuid_module *module) * skl_get_pvt_id: generate a private id for use as module id * * @ctx: driver context - * @mconfig: module configuration data + * @uuid_mod: module's uuid + * @instance_id: module's instance id * * This generates a 128 bit private unique id for a module TYPE so that * module instance is unique @@ -208,7 +209,8 @@ EXPORT_SYMBOL_GPL(skl_get_pvt_id); * skl_put_pvt_id: free up the private id allocated * * @ctx: driver context - * @mconfig: module configuration data + * @uuid_mod: module's uuid + * @pvt_id: module pvt id * * This frees a 128 bit private unique id previously generated */ diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index a436abf2fe3f..5a7e41b65ef3 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -569,7 +569,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, sst->fw_ops = skl_fw_ops; - return 0; + return skl_dsp_acquire_irq(sst); } EXPORT_SYMBOL_GPL(skl_sst_dsp_init); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a072bcf209d2..73af6e19ebbd 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -190,7 +190,6 @@ skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig) u8 res_idx = mconfig->res_idx; struct skl_module_res *res = &mconfig->module->resources[res_idx]; - res = &mconfig->module->resources[res_idx]; skl->resource.mcps -= res->cps; } @@ -2908,7 +2907,7 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt, break; default: - dev_warn(bus->dev, "Control load not supported %d:%d:%d\n", + dev_dbg(bus->dev, "Control load not supported %d:%d:%d\n", hdr->ops.get, hdr->ops.put, hdr->ops.info); break; } @@ -3056,11 +3055,13 @@ static int skl_tplg_get_int_tkn(struct device *dev, struct snd_soc_tplg_vendor_value_elem *tkn_elem, struct skl *skl) { - int tkn_count = 0, ret; + int tkn_count = 0, ret, size; static int mod_idx, res_val_idx, intf_val_idx, dir, pin_idx; struct skl_module_res *res = NULL; struct skl_module_iface *fmt = NULL; struct skl_module *mod = NULL; + static struct skl_astate_param *astate_table; + static int astate_cfg_idx, count; int i; if (skl->modules) { @@ -3093,6 +3094,46 @@ static int skl_tplg_get_int_tkn(struct device *dev, mod_idx = tkn_elem->value; break; + case SKL_TKN_U32_ASTATE_COUNT: + if (astate_table != NULL) { + dev_err(dev, "More than one entry for A-State count"); + return -EINVAL; + } + + if (tkn_elem->value > SKL_MAX_ASTATE_CFG) { + dev_err(dev, "Invalid A-State count %d\n", + tkn_elem->value); + return -EINVAL; + } + + size = tkn_elem->value * sizeof(struct skl_astate_param) + + sizeof(count); + skl->cfg.astate_cfg = devm_kzalloc(dev, size, GFP_KERNEL); + if (!skl->cfg.astate_cfg) + return -ENOMEM; + + astate_table = skl->cfg.astate_cfg->astate_table; + count = skl->cfg.astate_cfg->count = tkn_elem->value; + break; + + case SKL_TKN_U32_ASTATE_IDX: + if (tkn_elem->value >= count) { + dev_err(dev, "Invalid A-State index %d\n", + tkn_elem->value); + return -EINVAL; + } + + astate_cfg_idx = tkn_elem->value; + break; + + case SKL_TKN_U32_ASTATE_KCPS: + astate_table[astate_cfg_idx].kcps = tkn_elem->value; + break; + + case SKL_TKN_U32_ASTATE_CLK_SRC: + astate_table[astate_cfg_idx].clk_src = tkn_elem->value; + break; + case SKL_TKN_U8_IN_PIN_TYPE: case SKL_TKN_U8_OUT_PIN_TYPE: case SKL_TKN_U8_IN_QUEUE_COUNT: diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 31d8634e8aa1..32ce64c6b2dc 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -355,6 +355,7 @@ static int skl_resume(struct device *dev) if (ebus->cmd_dma_state) snd_hdac_bus_init_cmd_io(&ebus->bus); + ret = 0; } else { ret = _skl_resume(ebus); @@ -435,19 +436,51 @@ static int skl_free(struct hdac_ext_bus *ebus) return 0; } -static int skl_machine_device_register(struct skl *skl, void *driver_data) +/* + * For each ssp there are 3 clocks (mclk/sclk/sclkfs). + * e.g. for ssp0, clocks will be named as + * "ssp0_mclk", "ssp0_sclk", "ssp0_sclkfs" + * So for skl+, there are 6 ssps, so 18 clocks will be created. + */ +static struct skl_ssp_clk skl_ssp_clks[] = { + {.name = "ssp0_mclk"}, {.name = "ssp1_mclk"}, {.name = "ssp2_mclk"}, + {.name = "ssp3_mclk"}, {.name = "ssp4_mclk"}, {.name = "ssp5_mclk"}, + {.name = "ssp0_sclk"}, {.name = "ssp1_sclk"}, {.name = "ssp2_sclk"}, + {.name = "ssp3_sclk"}, {.name = "ssp4_sclk"}, {.name = "ssp5_sclk"}, + {.name = "ssp0_sclkfs"}, {.name = "ssp1_sclkfs"}, + {.name = "ssp2_sclkfs"}, + {.name = "ssp3_sclkfs"}, {.name = "ssp4_sclkfs"}, + {.name = "ssp5_sclkfs"}, +}; + +static int skl_find_machine(struct skl *skl, void *driver_data) { - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); - struct platform_device *pdev; struct snd_soc_acpi_mach *mach = driver_data; - int ret; + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct skl_machine_pdata *pdata; mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(bus->dev, "No matching machine driver found\n"); return -ENODEV; } + + skl->mach = mach; skl->fw_name = mach->fw_filename; + pdata = skl->mach->pdata; + + if (mach->pdata) + skl->use_tplg_pcm = pdata->use_tplg_pcm; + + return 0; +} + +static int skl_machine_device_register(struct skl *skl) +{ + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct snd_soc_acpi_mach *mach = skl->mach; + struct platform_device *pdev; + int ret; pdev = platform_device_alloc(mach->drv_name, -1); if (pdev == NULL) { @@ -462,11 +495,8 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) return -EIO; } - if (mach->pdata) { - skl->use_tplg_pcm = - ((struct skl_machine_pdata *)mach->pdata)->use_tplg_pcm; + if (mach->pdata) dev_set_drvdata(&pdev->dev, mach->pdata); - } skl->i2s_dev = pdev; @@ -509,6 +539,74 @@ static void skl_dmic_device_unregister(struct skl *skl) platform_device_unregister(skl->dmic_dev); } +static struct skl_clk_parent_src skl_clk_src[] = { + { .clk_id = SKL_XTAL, .name = "xtal" }, + { .clk_id = SKL_CARDINAL, .name = "cardinal", .rate = 24576000 }, + { .clk_id = SKL_PLL, .name = "pll", .rate = 96000000 }, +}; + +struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(skl_clk_src); i++) { + if (skl_clk_src[i].clk_id == clk_id) + return &skl_clk_src[i]; + } + + return NULL; +} + +static void init_skl_xtal_rate(int pci_id) +{ + switch (pci_id) { + case 0x9d70: + case 0x9d71: + skl_clk_src[0].rate = 24000000; + return; + + default: + skl_clk_src[0].rate = 19200000; + return; + } +} + +static int skl_clock_device_register(struct skl *skl) +{ + struct platform_device_info pdevinfo = {NULL}; + struct skl_clk_pdata *clk_pdata; + + clk_pdata = devm_kzalloc(&skl->pci->dev, sizeof(*clk_pdata), + GFP_KERNEL); + if (!clk_pdata) + return -ENOMEM; + + init_skl_xtal_rate(skl->pci->device); + + clk_pdata->parent_clks = skl_clk_src; + clk_pdata->ssp_clks = skl_ssp_clks; + clk_pdata->num_clks = ARRAY_SIZE(skl_ssp_clks); + + /* Query NHLT to fill the rates and parent */ + skl_get_clks(skl, clk_pdata->ssp_clks); + clk_pdata->pvt_data = skl; + + /* Register Platform device */ + pdevinfo.parent = &skl->pci->dev; + pdevinfo.id = -1; + pdevinfo.name = "skl-ssp-clk"; + pdevinfo.data = clk_pdata; + pdevinfo.size_data = sizeof(*clk_pdata); + skl->clk_dev = platform_device_register_full(&pdevinfo); + return PTR_ERR_OR_ZERO(skl->clk_dev); +} + +static void skl_clock_device_unregister(struct skl *skl) +{ + if (skl->clk_dev) + platform_device_unregister(skl->clk_dev); +} + /* * Probe the given codec address */ @@ -615,18 +713,30 @@ static void skl_probe_work(struct work_struct *work) /* create codec instances */ skl_codec_create(ebus); + /* register platform dai and controls */ + err = skl_platform_register(bus->dev); + if (err < 0) { + dev_err(bus->dev, "platform register failed: %d\n", err); + return; + } + + if (bus->ppcap) { + err = skl_machine_device_register(skl); + if (err < 0) { + dev_err(bus->dev, "machine register failed: %d\n", err); + goto out_err; + } + } + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { err = snd_hdac_display_power(bus, false); if (err < 0) { dev_err(bus->dev, "Cannot turn off display power on i915\n"); + skl_machine_device_unregister(skl); return; } } - /* register platform dai and controls */ - err = skl_platform_register(bus->dev); - if (err < 0) - return; /* * we are done probing so decrement link counts */ @@ -791,18 +901,21 @@ static int skl_probe(struct pci_dev *pci, /* check if dsp is there */ if (bus->ppcap) { - err = skl_machine_device_register(skl, - (void *)pci_id->driver_data); + /* create device for dsp clk */ + err = skl_clock_device_register(skl); + if (err < 0) + goto out_clk_free; + + err = skl_find_machine(skl, (void *)pci_id->driver_data); if (err < 0) goto out_nhlt_free; err = skl_init_dsp(skl); if (err < 0) { dev_dbg(bus->dev, "error failed to register dsp\n"); - goto out_mach_free; + goto out_nhlt_free; } skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; - } if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(ebus); @@ -820,8 +933,8 @@ static int skl_probe(struct pci_dev *pci, out_dsp_free: skl_free_dsp(skl); -out_mach_free: - skl_machine_device_unregister(skl); +out_clk_free: + skl_clock_device_unregister(skl); out_nhlt_free: skl_nhlt_free(skl->nhlt); out_free: @@ -872,6 +985,7 @@ static void skl_remove(struct pci_dev *pci) skl_free_dsp(skl); skl_machine_device_unregister(skl); skl_dmic_device_unregister(skl); + skl_clock_device_unregister(skl); skl_nhlt_remove_sysfs(skl); skl_nhlt_free(skl->nhlt); skl_free(ebus); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index e00cde8200dd..2d13f3fd988a 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -25,9 +25,12 @@ #include <sound/hdaudio_ext.h> #include <sound/soc.h> #include "skl-nhlt.h" +#include "skl-ssp-clk.h" #define SKL_SUSPEND_DELAY 2000 +#define SKL_MAX_ASTATE_CFG 3 + #define AZX_PCIREG_PGCTL 0x44 #define AZX_PGCTL_LSRMD_MASK (1 << 4) #define AZX_PCIREG_CGCTL 0x48 @@ -35,6 +38,10 @@ /* D0I3C Register fields */ #define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */ #define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */ +#define SKL_MAX_DMACTRL_CFG 18 +#define DMA_CLK_CONTROLS 1 +#define DMA_TRANSMITION_START 2 +#define DMA_TRANSMITION_STOP 3 struct skl_dsp_resource { u32 max_mcps; @@ -45,6 +52,20 @@ struct skl_dsp_resource { struct skl_debug; +struct skl_astate_param { + u32 kcps; + u32 clk_src; +}; + +struct skl_astate_config { + u32 count; + struct skl_astate_param astate_table[0]; +}; + +struct skl_fw_config { + struct skl_astate_config *astate_cfg; +}; + struct skl { struct hdac_ext_bus ebus; struct pci_dev *pci; @@ -52,6 +73,7 @@ struct skl { unsigned int init_done:1; /* delayed init status */ struct platform_device *dmic_dev; struct platform_device *i2s_dev; + struct platform_device *clk_dev; struct snd_soc_platform *platform; struct snd_soc_dai_driver *dais; @@ -75,6 +97,8 @@ struct skl { u8 nr_modules; struct skl_module **modules; bool use_tplg_pcm; + struct skl_fw_config cfg; + struct snd_soc_acpi_mach *mach; }; #define skl_to_ebus(s) (&(s)->ebus) @@ -125,6 +149,10 @@ const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); void skl_update_d0i3c(struct device *dev, bool enable); int skl_nhlt_create_sysfs(struct skl *skl); void skl_nhlt_remove_sysfs(struct skl *skl); +void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks); +struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id); +int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, + u32 caps_size, u32 node_id); struct skl_module_cfg; diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c index affa7fb25dd9..949fc3a1d025 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c @@ -14,451 +14,285 @@ * GNU General Public License for more details. */ -#include <sound/soc.h> -#include <linux/regmap.h> -#include <linux/pm_runtime.h> - #include "mt2701-afe-common.h" #include "mt2701-afe-clock-ctrl.h" -static const char *aud_clks[MT2701_CLOCK_NUM] = { - [MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk", - [MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel", - [MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel", - [MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div", - [MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div", - [MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing", - [MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing", - [MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel", - [MT2701_AUD_APLL_SEL] = "top_apll_sel", - [MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M", - [MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M", - [MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M", - [MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M", - [MT2701_AUD_AUDPLL] = "top_audpll", - [MT2701_AUD_AUDPLL_D4] = "top_audpll_d4", - [MT2701_AUD_AUDPLL_D8] = "top_audpll_d8", - [MT2701_AUD_AUDPLL_D16] = "top_audpll_d16", - [MT2701_AUD_AUDPLL_D24] = "top_audpll_d24", - [MT2701_AUD_AUDINTBUS] = "top_audintbus_sel", - [MT2701_AUD_CLK_26M] = "clk_26m", - [MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4", - [MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel", - [MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel", - [MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel", - [MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel", - [MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel", - [MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel", - [MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div", - [MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div", - [MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div", - [MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div", - [MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div", - [MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div", - [MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk", - [MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk", - [MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk", - [MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk", - [MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk", - [MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk", - [MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel", - [MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel", - [MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4", - [MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2", - [MT2701_AUD_SYSPLL_D5] = "top_syspll_d5", +static const char *const base_clks[] = { + [MT2701_INFRA_SYS_AUDIO] = "infra_sys_audio_clk", + [MT2701_TOP_AUD_MCLK_SRC0] = "top_audio_mux1_sel", + [MT2701_TOP_AUD_MCLK_SRC1] = "top_audio_mux2_sel", + [MT2701_TOP_AUD_A1SYS] = "top_audio_a1sys_hp", + [MT2701_TOP_AUD_A2SYS] = "top_audio_a2sys_hp", + [MT2701_AUDSYS_AFE] = "audio_afe_pd", + [MT2701_AUDSYS_AFE_CONN] = "audio_afe_conn_pd", + [MT2701_AUDSYS_A1SYS] = "audio_a1sys_pd", + [MT2701_AUDSYS_A2SYS] = "audio_a2sys_pd", }; int mt2701_init_clock(struct mtk_base_afe *afe) { struct mt2701_afe_private *afe_priv = afe->platform_priv; - int i = 0; - - for (i = 0; i < MT2701_CLOCK_NUM; i++) { - afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]); - if (IS_ERR(afe_priv->clocks[i])) { - dev_warn(afe->dev, "%s devm_clk_get %s fail\n", - __func__, aud_clks[i]); - return PTR_ERR(aud_clks[i]); + int i; + + for (i = 0; i < MT2701_BASE_CLK_NUM; i++) { + afe_priv->base_ck[i] = devm_clk_get(afe->dev, base_clks[i]); + if (IS_ERR(afe_priv->base_ck[i])) { + dev_err(afe->dev, "failed to get %s\n", base_clks[i]); + return PTR_ERR(afe_priv->base_ck[i]); + } + } + + /* Get I2S related clocks */ + for (i = 0; i < MT2701_I2S_NUM; i++) { + struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i]; + char name[13]; + + snprintf(name, sizeof(name), "i2s%d_src_sel", i); + i2s_path->sel_ck = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_path->sel_ck)) { + dev_err(afe->dev, "failed to get %s\n", name); + return PTR_ERR(i2s_path->sel_ck); + } + + snprintf(name, sizeof(name), "i2s%d_src_div", i); + i2s_path->div_ck = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_path->div_ck)) { + dev_err(afe->dev, "failed to get %s\n", name); + return PTR_ERR(i2s_path->div_ck); + } + + snprintf(name, sizeof(name), "i2s%d_mclk_en", i); + i2s_path->mclk_ck = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_path->mclk_ck)) { + dev_err(afe->dev, "failed to get %s\n", name); + return PTR_ERR(i2s_path->mclk_ck); + } + + snprintf(name, sizeof(name), "i2so%d_hop_ck", i); + i2s_path->hop_ck[I2S_OUT] = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_path->hop_ck[I2S_OUT])) { + dev_err(afe->dev, "failed to get %s\n", name); + return PTR_ERR(i2s_path->hop_ck[I2S_OUT]); + } + + snprintf(name, sizeof(name), "i2si%d_hop_ck", i); + i2s_path->hop_ck[I2S_IN] = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_path->hop_ck[I2S_IN])) { + dev_err(afe->dev, "failed to get %s\n", name); + return PTR_ERR(i2s_path->hop_ck[I2S_IN]); + } + + snprintf(name, sizeof(name), "asrc%d_out_ck", i); + i2s_path->asrco_ck = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_path->asrco_ck)) { + dev_err(afe->dev, "failed to get %s\n", name); + return PTR_ERR(i2s_path->asrco_ck); } } + /* Some platforms may support BT path */ + afe_priv->mrgif_ck = devm_clk_get(afe->dev, "audio_mrgif_pd"); + if (IS_ERR(afe_priv->mrgif_ck)) { + if (PTR_ERR(afe_priv->mrgif_ck) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + afe_priv->mrgif_ck = NULL; + } + return 0; } -int mt2701_afe_enable_clock(struct mtk_base_afe *afe) +int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir) { - int ret = 0; + struct mt2701_afe_private *afe_priv = afe->platform_priv; + struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id]; + int ret; - ret = mt2701_turn_on_a1sys_clock(afe); + ret = clk_prepare_enable(i2s_path->asrco_ck); if (ret) { - dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n", - __func__, ret); + dev_err(afe->dev, "failed to enable ASRC clock %d\n", ret); return ret; } - ret = mt2701_turn_on_a2sys_clock(afe); + ret = clk_prepare_enable(i2s_path->hop_ck[dir]); if (ret) { - dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n", - __func__, ret); - mt2701_turn_off_a1sys_clock(afe); - return ret; + dev_err(afe->dev, "failed to enable I2S clock %d\n", ret); + goto err_hop_ck; } - ret = mt2701_turn_on_afe_clock(afe); - if (ret) { - dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n", - __func__, ret); - mt2701_turn_off_a1sys_clock(afe); - mt2701_turn_off_a2sys_clock(afe); - return ret; - } + return 0; - regmap_update_bits(afe->regmap, ASYS_TOP_CON, - AUDIO_TOP_CON0_A1SYS_A2SYS_ON, - AUDIO_TOP_CON0_A1SYS_A2SYS_ON); - regmap_update_bits(afe->regmap, AFE_DAC_CON0, - AFE_DAC_CON0_AFE_ON, - AFE_DAC_CON0_AFE_ON); - regmap_write(afe->regmap, PWR2_TOP_CON, - PWR2_TOP_CON_INIT_VAL); - regmap_write(afe->regmap, PWR1_ASM_CON1, - PWR1_ASM_CON1_INIT_VAL); - regmap_write(afe->regmap, PWR2_ASM_CON1, - PWR2_ASM_CON1_INIT_VAL); +err_hop_ck: + clk_disable_unprepare(i2s_path->asrco_ck); - return 0; + return ret; } -void mt2701_afe_disable_clock(struct mtk_base_afe *afe) +void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, int id, int dir) { - mt2701_turn_off_afe_clock(afe); - mt2701_turn_off_a1sys_clock(afe); - mt2701_turn_off_a2sys_clock(afe); - regmap_update_bits(afe->regmap, ASYS_TOP_CON, - AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0); - regmap_update_bits(afe->regmap, AFE_DAC_CON0, - AFE_DAC_CON0_AFE_ON, 0); + struct mt2701_afe_private *afe_priv = afe->platform_priv; + struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id]; + + clk_disable_unprepare(i2s_path->hop_ck[dir]); + clk_disable_unprepare(i2s_path->asrco_ck); } -int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe) +int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id) { struct mt2701_afe_private *afe_priv = afe->platform_priv; - int ret = 0; - - /* Set Mux */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret); - goto A1SYS_CLK_AUD_MUX1_SEL_ERR; - } - - ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL], - afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]); - if (ret) { - dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, - aud_clks[MT2701_AUD_AUD_MUX1_SEL], - aud_clks[MT2701_AUD_AUD1PLL_98M], ret); - goto A1SYS_CLK_AUD_MUX1_SEL_ERR; - } - - /* Set Divider */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, - aud_clks[MT2701_AUD_AUD_MUX1_DIV], - ret); - goto A1SYS_CLK_AUD_MUX1_DIV_ERR; - } - - ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV], - MT2701_AUD_AUD_MUX1_DIV_RATE); - if (ret) { - dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__, - aud_clks[MT2701_AUD_AUD_MUX1_DIV], - MT2701_AUD_AUD_MUX1_DIV_RATE, ret); - goto A1SYS_CLK_AUD_MUX1_DIV_ERR; - } + struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id]; - /* Enable clock gate */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret); - goto A1SYS_CLK_AUD_48K_ERR; - } + return clk_prepare_enable(i2s_path->mclk_ck); +} - /* Enable infra audio */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret); - goto A1SYS_CLK_INFRA_ERR; - } +void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id) +{ + struct mt2701_afe_private *afe_priv = afe->platform_priv; + struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id]; - return 0; + clk_disable_unprepare(i2s_path->mclk_ck); +} -A1SYS_CLK_INFRA_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); -A1SYS_CLK_AUD_48K_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]); -A1SYS_CLK_AUD_MUX1_DIV_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]); -A1SYS_CLK_AUD_MUX1_SEL_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]); +int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe) +{ + struct mt2701_afe_private *afe_priv = afe->platform_priv; - return ret; + return clk_prepare_enable(afe_priv->mrgif_ck); } -void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe) +void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe) { struct mt2701_afe_private *afe_priv = afe->platform_priv; - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]); + clk_disable_unprepare(afe_priv->mrgif_ck); } -int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe) +static int mt2701_afe_enable_audsys(struct mtk_base_afe *afe) { struct mt2701_afe_private *afe_priv = afe->platform_priv; - int ret = 0; + int ret; - /* Set Mux */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret); - goto A2SYS_CLK_AUD_MUX2_SEL_ERR; - } + /* Enable infra clock gate */ + ret = clk_prepare_enable(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]); + if (ret) + return ret; - ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL], - afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]); - if (ret) { - dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, - aud_clks[MT2701_AUD_AUD_MUX2_SEL], - aud_clks[MT2701_AUD_AUD2PLL_90M], ret); - goto A2SYS_CLK_AUD_MUX2_SEL_ERR; - } + /* Enable top a1sys clock gate */ + ret = clk_prepare_enable(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]); + if (ret) + goto err_a1sys; - /* Set Divider */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret); - goto A2SYS_CLK_AUD_MUX2_DIV_ERR; - } + /* Enable top a2sys clock gate */ + ret = clk_prepare_enable(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]); + if (ret) + goto err_a2sys; - ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV], - MT2701_AUD_AUD_MUX2_DIV_RATE); - if (ret) { - dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__, - aud_clks[MT2701_AUD_AUD_MUX2_DIV], - MT2701_AUD_AUD_MUX2_DIV_RATE, ret); - goto A2SYS_CLK_AUD_MUX2_DIV_ERR; - } + /* Internal clock gates */ + ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_AFE]); + if (ret) + goto err_afe; - /* Enable clock gate */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret); - goto A2SYS_CLK_AUD_44K_ERR; - } + ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]); + if (ret) + goto err_audio_a1sys; - /* Enable infra audio */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret); - goto A2SYS_CLK_INFRA_ERR; - } + ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]); + if (ret) + goto err_audio_a2sys; + + ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_AFE_CONN]); + if (ret) + goto err_afe_conn; return 0; -A2SYS_CLK_INFRA_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); -A2SYS_CLK_AUD_44K_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]); -A2SYS_CLK_AUD_MUX2_DIV_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]); -A2SYS_CLK_AUD_MUX2_SEL_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]); +err_afe_conn: + clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]); +err_audio_a2sys: + clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]); +err_audio_a1sys: + clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE]); +err_afe: + clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]); +err_a2sys: + clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]); +err_a1sys: + clk_disable_unprepare(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]); return ret; } -void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe) +static void mt2701_afe_disable_audsys(struct mtk_base_afe *afe) { struct mt2701_afe_private *afe_priv = afe->platform_priv; - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]); + clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE_CONN]); + clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]); + clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]); + clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE]); + clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]); + clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]); + clk_disable_unprepare(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]); } -int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe) +int mt2701_afe_enable_clock(struct mtk_base_afe *afe) { - struct mt2701_afe_private *afe_priv = afe->platform_priv; int ret; - /* enable INFRA_SYS */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret); - goto AFE_AUD_INFRA_ERR; - } - - /* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_AUDINTBUS], ret); - goto AFE_AUD_AUDINTBUS_ERR; - } - - ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS], - afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]); - if (ret) { - dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, - aud_clks[MT2701_AUD_AUDINTBUS], - aud_clks[MT2701_AUD_SYSPLL1_D4], ret); - goto AFE_AUD_AUDINTBUS_ERR; - } - - /* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]); + /* Enable audio system */ + ret = mt2701_afe_enable_audsys(afe); if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret); - goto AFE_AUD_ASM_H_ERR; - } - - ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL], - afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]); - if (ret) { - dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, - aud_clks[MT2701_AUD_ASM_H_SEL], - aud_clks[MT2701_AUD_UNIVPLL2_D2], ret); - goto AFE_AUD_ASM_H_ERR; - } - - /* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */ - ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]); - if (ret) { - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret); - goto AFE_AUD_ASM_M_ERR; + dev_err(afe->dev, "failed to enable audio system %d\n", ret); + return ret; } - ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL], - afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]); - if (ret) { - dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, - aud_clks[MT2701_AUD_ASM_M_SEL], - aud_clks[MT2701_AUD_UNIVPLL2_D4], ret); - goto AFE_AUD_ASM_M_ERR; - } + regmap_update_bits(afe->regmap, ASYS_TOP_CON, + ASYS_TOP_CON_ASYS_TIMING_ON, + ASYS_TOP_CON_ASYS_TIMING_ON); + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + AFE_DAC_CON0_AFE_ON, + AFE_DAC_CON0_AFE_ON); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUDIO_TOP_CON0_PDN_AFE, 0); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUDIO_TOP_CON0_PDN_APLL_CK, 0); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_A1SYS, 0); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_A2SYS, 0); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_AFE_CONN, 0); + /* Configure ASRC */ + regmap_write(afe->regmap, PWR1_ASM_CON1, PWR1_ASM_CON1_INIT_VAL); + regmap_write(afe->regmap, PWR2_ASM_CON1, PWR2_ASM_CON1_INIT_VAL); return 0; - -AFE_AUD_ASM_M_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]); -AFE_AUD_ASM_H_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]); -AFE_AUD_AUDINTBUS_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]); -AFE_AUD_INFRA_ERR: - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); - - return ret; } -void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe) +int mt2701_afe_disable_clock(struct mtk_base_afe *afe) { - struct mt2701_afe_private *afe_priv = afe->platform_priv; + regmap_update_bits(afe->regmap, ASYS_TOP_CON, + ASYS_TOP_CON_ASYS_TIMING_ON, 0); + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + AFE_DAC_CON0_AFE_ON, 0); + + mt2701_afe_disable_audsys(afe); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]); - - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]); - clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]); - - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUDIO_TOP_CON0_PDN_APLL_CK, - AUDIO_TOP_CON0_PDN_APLL_CK); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_A1SYS, - AUDIO_TOP_CON4_PDN_A1SYS); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_A2SYS, - AUDIO_TOP_CON4_PDN_A2SYS); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_AFE_CONN, - AUDIO_TOP_CON4_PDN_AFE_CONN); + return 0; } void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain, int mclk) { - struct mt2701_afe_private *afe_priv = afe->platform_priv; + struct mt2701_afe_private *priv = afe->platform_priv; + struct mt2701_i2s_path *i2s_path = &priv->i2s_path[id]; int ret; - int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id; - int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id; - /* Set MCLK Kx_SRC_SEL(domain) */ - ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]); - if (ret) - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[aud_src_clk_id], ret); - - if (domain == 0) { - ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id], - afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]); - if (ret) - dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", - __func__, aud_clks[aud_src_clk_id], - aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret); - } else { - ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id], - afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]); - if (ret) - dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", - __func__, aud_clks[aud_src_clk_id], - aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret); - } - clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]); + /* Set mclk source */ + if (domain == 0) + ret = clk_set_parent(i2s_path->sel_ck, + priv->base_ck[MT2701_TOP_AUD_MCLK_SRC0]); + else + ret = clk_set_parent(i2s_path->sel_ck, + priv->base_ck[MT2701_TOP_AUD_MCLK_SRC1]); - /* Set MCLK Kx_SRC_DIV(divider) */ - ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]); if (ret) - dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[aud_src_div_id], ret); + dev_err(afe->dev, "failed to set domain%d mclk source %d\n", + domain, ret); - ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk); + /* Set mclk divider */ + ret = clk_set_rate(i2s_path->div_ck, mclk); if (ret) - dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__, - aud_clks[aud_src_div_id], mclk, ret); - clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]); + dev_err(afe->dev, "failed to set mclk divider %d\n", ret); } - -MODULE_DESCRIPTION("MT2701 afe clock control"); -MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h index 6497d570cf09..15417d9d6597 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h +++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h @@ -21,16 +21,15 @@ struct mtk_base_afe; int mt2701_init_clock(struct mtk_base_afe *afe); int mt2701_afe_enable_clock(struct mtk_base_afe *afe); -void mt2701_afe_disable_clock(struct mtk_base_afe *afe); +int mt2701_afe_disable_clock(struct mtk_base_afe *afe); -int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe); -void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe); +int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir); +void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, int id, int dir); +int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id); +void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id); -int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe); -void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe); - -int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe); -void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe); +int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe); +void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe); void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain, int mclk); diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h index c19430e98adf..ae8ddeacfbfe 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-common.h +++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h @@ -16,6 +16,7 @@ #ifndef _MT_2701_AFE_COMMON_H_ #define _MT_2701_AFE_COMMON_H_ + #include <sound/soc.h> #include <linux/clk.h> #include <linux/regmap.h> @@ -25,16 +26,7 @@ #define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1) #define MT2701_PLL_DOMAIN_0_RATE 98304000 #define MT2701_PLL_DOMAIN_1_RATE 90316800 -#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2) -#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2) - -enum { - MT2701_I2S_1, - MT2701_I2S_2, - MT2701_I2S_3, - MT2701_I2S_4, - MT2701_I2S_NUM, -}; +#define MT2701_I2S_NUM 4 enum { MT2701_MEMIF_DL1, @@ -62,60 +54,23 @@ enum { }; enum { - MT2701_IRQ_ASYS_START, - MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START, + MT2701_IRQ_ASYS_IRQ1, MT2701_IRQ_ASYS_IRQ2, MT2701_IRQ_ASYS_IRQ3, MT2701_IRQ_ASYS_END, }; -/* 2701 clock def */ -enum audio_system_clock_type { - MT2701_AUD_INFRA_SYS_AUDIO, - MT2701_AUD_AUD_MUX1_SEL, - MT2701_AUD_AUD_MUX2_SEL, - MT2701_AUD_AUD_MUX1_DIV, - MT2701_AUD_AUD_MUX2_DIV, - MT2701_AUD_AUD_48K_TIMING, - MT2701_AUD_AUD_44K_TIMING, - MT2701_AUD_AUDPLL_MUX_SEL, - MT2701_AUD_APLL_SEL, - MT2701_AUD_AUD1PLL_98M, - MT2701_AUD_AUD2PLL_90M, - MT2701_AUD_HADDS2PLL_98M, - MT2701_AUD_HADDS2PLL_294M, - MT2701_AUD_AUDPLL, - MT2701_AUD_AUDPLL_D4, - MT2701_AUD_AUDPLL_D8, - MT2701_AUD_AUDPLL_D16, - MT2701_AUD_AUDPLL_D24, - MT2701_AUD_AUDINTBUS, - MT2701_AUD_CLK_26M, - MT2701_AUD_SYSPLL1_D4, - MT2701_AUD_AUD_K1_SRC_SEL, - MT2701_AUD_AUD_K2_SRC_SEL, - MT2701_AUD_AUD_K3_SRC_SEL, - MT2701_AUD_AUD_K4_SRC_SEL, - MT2701_AUD_AUD_K5_SRC_SEL, - MT2701_AUD_AUD_K6_SRC_SEL, - MT2701_AUD_AUD_K1_SRC_DIV, - MT2701_AUD_AUD_K2_SRC_DIV, - MT2701_AUD_AUD_K3_SRC_DIV, - MT2701_AUD_AUD_K4_SRC_DIV, - MT2701_AUD_AUD_K5_SRC_DIV, - MT2701_AUD_AUD_K6_SRC_DIV, - MT2701_AUD_AUD_I2S1_MCLK, - MT2701_AUD_AUD_I2S2_MCLK, - MT2701_AUD_AUD_I2S3_MCLK, - MT2701_AUD_AUD_I2S4_MCLK, - MT2701_AUD_AUD_I2S5_MCLK, - MT2701_AUD_AUD_I2S6_MCLK, - MT2701_AUD_ASM_M_SEL, - MT2701_AUD_ASM_H_SEL, - MT2701_AUD_UNIVPLL2_D4, - MT2701_AUD_UNIVPLL2_D2, - MT2701_AUD_SYSPLL_D5, - MT2701_CLOCK_NUM +enum audio_base_clock { + MT2701_INFRA_SYS_AUDIO, + MT2701_TOP_AUD_MCLK_SRC0, + MT2701_TOP_AUD_MCLK_SRC1, + MT2701_TOP_AUD_A1SYS, + MT2701_TOP_AUD_A2SYS, + MT2701_AUDSYS_AFE, + MT2701_AUDSYS_AFE_CONN, + MT2701_AUDSYS_A1SYS, + MT2701_AUDSYS_A2SYS, + MT2701_BASE_CLK_NUM, }; static const unsigned int mt2701_afe_backup_list[] = { @@ -139,12 +94,8 @@ static const unsigned int mt2701_afe_backup_list[] = { AFE_MEMIF_PBUF_SIZE, }; -struct snd_pcm_substream; -struct mtk_base_irq_data; - struct mt2701_i2s_data { int i2s_ctrl_reg; - int i2s_pwn_shift; int i2s_asrc_fs_shift; int i2s_asrc_fs_mask; }; @@ -160,12 +111,18 @@ struct mt2701_i2s_path { int mclk_rate; int on[I2S_DIR_NUM]; int occupied[I2S_DIR_NUM]; - const struct mt2701_i2s_data *i2s_data[2]; + const struct mt2701_i2s_data *i2s_data[I2S_DIR_NUM]; + struct clk *hop_ck[I2S_DIR_NUM]; + struct clk *sel_ck; + struct clk *div_ck; + struct clk *mclk_ck; + struct clk *asrco_ck; }; struct mt2701_afe_private { - struct clk *clocks[MT2701_CLOCK_NUM]; struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM]; + struct clk *base_ck[MT2701_BASE_CLK_NUM]; + struct clk *mrgif_ck; bool mrg_enable[MT2701_STREAM_DIR_NUM]; }; diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index 8fda182f849b..d68b53f7cefe 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -17,19 +17,16 @@ #include <linux/delay.h> #include <linux/module.h> +#include <linux/mfd/syscon.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/pm_runtime.h> -#include <sound/soc.h> #include "mt2701-afe-common.h" - #include "mt2701-afe-clock-ctrl.h" #include "../common/mtk-afe-platform-driver.h" #include "../common/mtk-afe-fe-dai.h" -#define AFE_IRQ_STATUS_BITS 0xff - static const struct snd_pcm_hardware mt2701_afe_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID, @@ -97,40 +94,26 @@ static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); - struct mt2701_afe_private *afe_priv = afe->platform_priv; int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); - int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num; - int ret = 0; if (i2s_num < 0) return i2s_num; - /* enable mclk */ - ret = clk_prepare_enable(afe_priv->clocks[clk_num]); - if (ret) - dev_err(afe->dev, "Failed to enable mclk for I2S: %d\n", - i2s_num); - - return ret; + return mt2701_afe_enable_mclk(afe, i2s_num); } static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, + int i2s_num, int dir_invert) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); struct mt2701_afe_private *afe_priv = afe->platform_priv; - int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); - struct mt2701_i2s_path *i2s_path; + struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num]; const struct mt2701_i2s_data *i2s_data; int stream_dir = substream->stream; - if (i2s_num < 0) - return i2s_num; - - i2s_path = &afe_priv->i2s_path[i2s_num]; - if (dir_invert) { if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) stream_dir = SNDRV_PCM_STREAM_CAPTURE; @@ -151,9 +134,9 @@ static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, /* disable i2s */ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, ASYS_I2S_CON_I2S_EN, 0); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - 1 << i2s_data->i2s_pwn_shift, - 1 << i2s_data->i2s_pwn_shift); + + mt2701_afe_disable_i2s(afe, i2s_num, stream_dir); + return 0; } @@ -165,7 +148,6 @@ static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream, struct mt2701_afe_private *afe_priv = afe->platform_priv; int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); struct mt2701_i2s_path *i2s_path; - int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num; if (i2s_num < 0) return; @@ -177,37 +159,32 @@ static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream, else goto I2S_UNSTART; - mt2701_afe_i2s_path_shutdown(substream, dai, 0); + mt2701_afe_i2s_path_shutdown(substream, dai, i2s_num, 0); /* need to disable i2s-out path when disable i2s-in */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - mt2701_afe_i2s_path_shutdown(substream, dai, 1); + mt2701_afe_i2s_path_shutdown(substream, dai, i2s_num, 1); I2S_UNSTART: /* disable mclk */ - clk_disable_unprepare(afe_priv->clocks[clk_num]); + mt2701_afe_disable_mclk(afe, i2s_num); } static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, + int i2s_num, int dir_invert) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); struct mt2701_afe_private *afe_priv = afe->platform_priv; - int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); - struct mt2701_i2s_path *i2s_path; + struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num]; const struct mt2701_i2s_data *i2s_data; struct snd_pcm_runtime * const runtime = substream->runtime; int reg, fs, w_len = 1; /* now we support bck 64bits only */ int stream_dir = substream->stream; unsigned int mask = 0, val = 0; - if (i2s_num < 0) - return i2s_num; - - i2s_path = &afe_priv->i2s_path[i2s_num]; - if (dir_invert) { if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) stream_dir = SNDRV_PCM_STREAM_CAPTURE; @@ -251,9 +228,7 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, fs << i2s_data->i2s_asrc_fs_shift); /* enable i2s */ - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - 1 << i2s_data->i2s_pwn_shift, - 0 << i2s_data->i2s_pwn_shift); + mt2701_afe_enable_i2s(afe, i2s_num, stream_dir); /* reset i2s hw status before enable */ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, @@ -300,13 +275,13 @@ static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream, mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - mt2701_i2s_path_prepare_enable(substream, dai, 0); + mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 0); } else { /* need to enable i2s-out path when enable i2s-in */ /* prepare for another direction "out" */ - mt2701_i2s_path_prepare_enable(substream, dai, 1); + mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 1); /* prepare for "in" */ - mt2701_i2s_path_prepare_enable(substream, dai, 0); + mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 0); } return 0; @@ -339,9 +314,11 @@ static int mt2701_btmrg_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); struct mt2701_afe_private *afe_priv = afe->platform_priv; + int ret; - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_MRGIF, 0); + ret = mt2701_enable_btmrg_clk(afe); + if (ret) + return ret; afe_priv->mrg_enable[substream->stream] = 1; return 0; @@ -406,9 +383,7 @@ static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream, AFE_MRGIF_CON_MRG_EN, 0); regmap_update_bits(afe->regmap, AFE_MRGIF_CON, AFE_MRGIF_CON_MRG_I2S_EN, 0); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, - AUDIO_TOP_CON4_PDN_MRGIF, - AUDIO_TOP_CON4_PDN_MRGIF); + mt2701_disable_btmrg_clk(afe); } afe_priv->mrg_enable[substream->stream] = 0; } @@ -574,7 +549,6 @@ static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = { .hw_free = mtk_afe_fe_hw_free, .prepare = mtk_afe_fe_prepare, .trigger = mtk_afe_fe_trigger, - }; static const struct snd_soc_dai_ops mt2701_dlm_memif_dai_ops = { @@ -915,31 +889,6 @@ static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s4[] = { PWR2_TOP_CON, 19, 1, 0), }; -static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc0[] = { - SOC_DAPM_SINGLE_AUTODISABLE("Asrc0 out Switch", AUDIO_TOP_CON4, 14, 1, - 1), -}; - -static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc1[] = { - SOC_DAPM_SINGLE_AUTODISABLE("Asrc1 out Switch", AUDIO_TOP_CON4, 15, 1, - 1), -}; - -static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc2[] = { - SOC_DAPM_SINGLE_AUTODISABLE("Asrc2 out Switch", PWR2_TOP_CON, 6, 1, - 1), -}; - -static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = { - SOC_DAPM_SINGLE_AUTODISABLE("Asrc3 out Switch", PWR2_TOP_CON, 7, 1, - 1), -}; - -static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc4[] = { - SOC_DAPM_SINGLE_AUTODISABLE("Asrc4 out Switch", PWR2_TOP_CON, 8, 1, - 1), -}; - static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = { /* inter-connections */ SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -999,19 +948,6 @@ static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = { SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0, mt2701_afe_multi_ch_out_i2s3, ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s3)), - - SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0, - mt2701_afe_multi_ch_out_asrc0, - ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc0)), - SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0, - mt2701_afe_multi_ch_out_asrc1, - ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc1)), - SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0, - mt2701_afe_multi_ch_out_asrc2, - ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc2)), - SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0, - mt2701_afe_multi_ch_out_asrc3, - ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc3)), }; static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = { @@ -1021,7 +957,6 @@ static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = { {"I2S0 Playback", NULL, "O15"}, {"I2S0 Playback", NULL, "O16"}, - {"I2S1 Playback", NULL, "O17"}, {"I2S1 Playback", NULL, "O18"}, {"I2S2 Playback", NULL, "O19"}, @@ -1038,7 +973,6 @@ static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = { {"I00", NULL, "I2S0 Capture"}, {"I01", NULL, "I2S0 Capture"}, - {"I02", NULL, "I2S1 Capture"}, {"I03", NULL, "I2S1 Capture"}, /* I02,03 link to UL2, also need to open I2S0 */ @@ -1046,15 +980,10 @@ static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = { {"I26", NULL, "BT Capture"}, - {"ASRC_O0", "Asrc0 out Switch", "DLM"}, - {"ASRC_O1", "Asrc1 out Switch", "DLM"}, - {"ASRC_O2", "Asrc2 out Switch", "DLM"}, - {"ASRC_O3", "Asrc3 out Switch", "DLM"}, - - {"I12I13", "Multich I2S0 Out Switch", "ASRC_O0"}, - {"I14I15", "Multich I2S1 Out Switch", "ASRC_O1"}, - {"I16I17", "Multich I2S2 Out Switch", "ASRC_O2"}, - {"I18I19", "Multich I2S3 Out Switch", "ASRC_O3"}, + {"I12I13", "Multich I2S0 Out Switch", "DLM"}, + {"I14I15", "Multich I2S1 Out Switch", "DLM"}, + {"I16I17", "Multich I2S2 Out Switch", "DLM"}, + {"I18I19", "Multich I2S3 Out Switch", "DLM"}, { "I12", NULL, "I12I13" }, { "I13", NULL, "I12I13" }, @@ -1079,7 +1008,6 @@ static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = { { "O21", "I18 Switch", "I18" }, { "O22", "I19 Switch", "I19" }, { "O31", "I35 Switch", "I35" }, - }; static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = { @@ -1386,14 +1314,12 @@ static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = { { { .i2s_ctrl_reg = ASYS_I2SO1_CON, - .i2s_pwn_shift = 6, .i2s_asrc_fs_shift = 0, .i2s_asrc_fs_mask = 0x1f, }, { .i2s_ctrl_reg = ASYS_I2SIN1_CON, - .i2s_pwn_shift = 0, .i2s_asrc_fs_shift = 0, .i2s_asrc_fs_mask = 0x1f, @@ -1402,14 +1328,12 @@ static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = { { { .i2s_ctrl_reg = ASYS_I2SO2_CON, - .i2s_pwn_shift = 7, .i2s_asrc_fs_shift = 5, .i2s_asrc_fs_mask = 0x1f, }, { .i2s_ctrl_reg = ASYS_I2SIN2_CON, - .i2s_pwn_shift = 1, .i2s_asrc_fs_shift = 5, .i2s_asrc_fs_mask = 0x1f, @@ -1418,14 +1342,12 @@ static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = { { { .i2s_ctrl_reg = ASYS_I2SO3_CON, - .i2s_pwn_shift = 8, .i2s_asrc_fs_shift = 10, .i2s_asrc_fs_mask = 0x1f, }, { .i2s_ctrl_reg = ASYS_I2SIN3_CON, - .i2s_pwn_shift = 2, .i2s_asrc_fs_shift = 10, .i2s_asrc_fs_mask = 0x1f, @@ -1434,14 +1356,12 @@ static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = { { { .i2s_ctrl_reg = ASYS_I2SO4_CON, - .i2s_pwn_shift = 9, .i2s_asrc_fs_shift = 15, .i2s_asrc_fs_mask = 0x1f, }, { .i2s_ctrl_reg = ASYS_I2SIN4_CON, - .i2s_pwn_shift = 3, .i2s_asrc_fs_shift = 15, .i2s_asrc_fs_mask = 0x1f, @@ -1449,14 +1369,6 @@ static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = { }, }; -static const struct regmap_config mt2701_afe_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = AFE_END_ADDR, - .cache_type = REGCACHE_NONE, -}; - static irqreturn_t mt2701_asys_isr(int irq_id, void *dev) { int id; @@ -1483,8 +1395,7 @@ static int mt2701_afe_runtime_suspend(struct device *dev) { struct mtk_base_afe *afe = dev_get_drvdata(dev); - mt2701_afe_disable_clock(afe); - return 0; + return mt2701_afe_disable_clock(afe); } static int mt2701_afe_runtime_resume(struct device *dev) @@ -1494,23 +1405,39 @@ static int mt2701_afe_runtime_resume(struct device *dev) return mt2701_afe_enable_clock(afe); } +static int mt2701_afe_add_component(struct mtk_base_afe *afe) +{ + struct snd_soc_component *component; + + component = kzalloc(sizeof(*component), GFP_KERNEL); + if (!component) + return -ENOMEM; + + component->regmap = afe->regmap; + + return snd_soc_add_component(afe->dev, component, + &mt2701_afe_pcm_dai_component, + mt2701_afe_pcm_dais, + ARRAY_SIZE(mt2701_afe_pcm_dais)); +} + static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt2701_afe_private *afe_priv; - struct resource *res; struct device *dev; int i, irq_id, ret; afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; + afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), GFP_KERNEL); if (!afe->platform_priv) return -ENOMEM; - afe_priv = afe->platform_priv; + afe_priv = afe->platform_priv; afe->dev = &pdev->dev; dev = afe->dev; @@ -1527,17 +1454,11 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) return ret; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - afe->base_addr = devm_ioremap_resource(&pdev->dev, res); - - if (IS_ERR(afe->base_addr)) - return PTR_ERR(afe->base_addr); - - afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, - &mt2701_afe_regmap_config); - if (IS_ERR(afe->regmap)) + afe->regmap = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(afe->regmap)) { + dev_err(dev, "could not get regmap from parent\n"); return PTR_ERR(afe->regmap); + } mutex_init(&afe->irq_alloc_lock); @@ -1545,7 +1466,6 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) afe->memif_size = MT2701_MEMIF_NUM; afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), GFP_KERNEL); - if (!afe->memif) return -ENOMEM; @@ -1558,7 +1478,6 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) afe->irqs_size = MT2701_IRQ_ASYS_END; afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), GFP_KERNEL); - if (!afe->irqs) return -ENOMEM; @@ -1576,7 +1495,6 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) afe->mtk_afe_hardware = &mt2701_afe_hardware; afe->memif_fs = mt2701_memif_fs; afe->irq_fs = mt2701_irq_fs; - afe->reg_back_up_list = mt2701_afe_backup_list; afe->reg_back_up_list_num = ARRAY_SIZE(mt2701_afe_backup_list); afe->runtime_resume = mt2701_afe_runtime_resume; @@ -1590,55 +1508,49 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, afe); - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) - goto err_pm_disable; - pm_runtime_get_sync(&pdev->dev); - ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform); + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = mt2701_afe_runtime_resume(dev); + if (ret) + goto err_pm_disable; + } + pm_runtime_get_sync(dev); + + ret = snd_soc_register_platform(dev, &mtk_afe_pcm_platform); if (ret) { dev_warn(dev, "err_platform\n"); goto err_platform; } - ret = snd_soc_register_component(&pdev->dev, - &mt2701_afe_pcm_dai_component, - mt2701_afe_pcm_dais, - ARRAY_SIZE(mt2701_afe_pcm_dais)); + ret = mt2701_afe_add_component(afe); if (ret) { dev_warn(dev, "err_dai_component\n"); goto err_dai_component; } - mt2701_afe_runtime_resume(&pdev->dev); - return 0; err_dai_component: - snd_soc_unregister_component(&pdev->dev); - + snd_soc_unregister_platform(dev); err_platform: - snd_soc_unregister_platform(&pdev->dev); - + pm_runtime_put_sync(dev); err_pm_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); return ret; } static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev) { - struct mtk_base_afe *afe = platform_get_drvdata(pdev); - + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) mt2701_afe_runtime_suspend(&pdev->dev); - pm_runtime_put_sync(&pdev->dev); snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_platform(&pdev->dev); - /* disable afe clock */ - mt2701_afe_disable_clock(afe); + return 0; } @@ -1670,4 +1582,3 @@ module_platform_driver(mt2701_afe_pcm_driver); MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701"); MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>"); MODULE_LICENSE("GPL v2"); - diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h index bb62b1c55957..18e676974f22 100644 --- a/sound/soc/mediatek/mt2701/mt2701-reg.h +++ b/sound/soc/mediatek/mt2701/mt2701-reg.h @@ -17,17 +17,6 @@ #ifndef _MT2701_REG_H_ #define _MT2701_REG_H_ -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/pm_runtime.h> -#include <sound/soc.h> -#include "mt2701-afe-common.h" - -/***************************************************************************** - * R E G I S T E R D E F I N I T I O N - *****************************************************************************/ #define AUDIO_TOP_CON0 0x0000 #define AUDIO_TOP_CON4 0x0010 #define AUDIO_TOP_CON5 0x0014 @@ -109,18 +98,6 @@ #define AFE_DAI_BASE 0x1370 #define AFE_DAI_CUR 0x137c -/* AUDIO_TOP_CON0 (0x0000) */ -#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON (0x3 << 0) -#define AUDIO_TOP_CON0_PDN_AFE (0x1 << 2) -#define AUDIO_TOP_CON0_PDN_APLL_CK (0x1 << 23) - -/* AUDIO_TOP_CON4 (0x0010) */ -#define AUDIO_TOP_CON4_I2SO1_PWN (0x1 << 6) -#define AUDIO_TOP_CON4_PDN_A1SYS (0x1 << 21) -#define AUDIO_TOP_CON4_PDN_A2SYS (0x1 << 22) -#define AUDIO_TOP_CON4_PDN_AFE_CONN (0x1 << 23) -#define AUDIO_TOP_CON4_PDN_MRGIF (0x1 << 25) - /* AFE_DAIBT_CON0 (0x001c) */ #define AFE_DAIBT_CON0_DAIBT_EN (0x1 << 0) #define AFE_DAIBT_CON0_BT_FUNC_EN (0x1 << 1) @@ -137,22 +114,8 @@ #define AFE_MRGIF_CON_I2S_MODE_MASK (0xf << 20) #define AFE_MRGIF_CON_I2S_MODE_32K (0x4 << 20) -/* ASYS_I2SO1_CON (0x061c) */ -#define ASYS_I2SO1_CON_FS (0x1f << 8) -#define ASYS_I2SO1_CON_FS_SET(x) ((x) << 8) -#define ASYS_I2SO1_CON_MULTI_CH (0x1 << 16) -#define ASYS_I2SO1_CON_SIDEGEN (0x1 << 30) -#define ASYS_I2SO1_CON_I2S_EN (0x1 << 0) -/* 0:EIAJ 1:I2S */ -#define ASYS_I2SO1_CON_I2S_MODE (0x1 << 3) -#define ASYS_I2SO1_CON_WIDE_MODE (0x1 << 1) -#define ASYS_I2SO1_CON_WIDE_MODE_SET(x) ((x) << 1) - -/* PWR2_TOP_CON (0x0634) */ -#define PWR2_TOP_CON_INIT_VAL (0xffe1ffff) - -/* ASYS_IRQ_CLR (0x07c0) */ -#define ASYS_IRQ_CLR_ALL (0xffffffff) +/* ASYS_TOP_CON (0x0600) */ +#define ASYS_TOP_CON_ASYS_TIMING_ON (0x3 << 0) /* PWR2_ASM_CON1 (0x1070) */ #define PWR2_ASM_CON1_INIT_VAL (0x492492) @@ -182,5 +145,4 @@ #define ASYS_I2S_CON_WIDE_MODE_SET(x) ((x) << 1) #define ASYS_I2S_IN_PHASE_FIX (0x1 << 31) -#define AFE_END_ADDR 0x15e0 #endif diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 8a643a35d3d4..c7f7f8add5d9 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1083,7 +1083,7 @@ static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe) static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) { int ret, i; - unsigned int irq_id; + int irq_id; struct mtk_base_afe *afe; struct mt8173_afe_private *afe_priv; struct resource *res; @@ -1105,9 +1105,9 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = &pdev->dev; irq_id = platform_get_irq(pdev, 0); - if (!irq_id) { + if (irq_id <= 0) { dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); - return -ENXIO; + return irq_id < 0 ? irq_id : -ENXIO; } ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler, 0, "Afe_ISR_Handle", (void *)afe); diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 99c15219dbc8..5a9a5482976e 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -37,8 +37,6 @@ static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = { {"Sub DMIC1R", NULL, "Int Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, - {"Headset Mic", NULL, "micbias1"}, - {"Headset Mic", NULL, "micbias2"}, {"IN1P", NULL, "Headset Mic"}, {"IN1N", NULL, "Headset Mic"}, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 42de84ca8c84..b7248085ca04 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -40,8 +40,6 @@ static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = { {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ - {"Headset Mic", NULL, "micbias1"}, - {"Headset Mic", NULL, "micbias2"}, {"IN1P", NULL, "Headset Mic"}, {"IN1N", NULL, "Headset Mic"}, {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */ diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index e69c141d8ed4..679fc8bea0a3 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -51,8 +51,6 @@ static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { {"DMIC R1", NULL, "Int Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, - {"Headset Mic", NULL, "micbias1"}, - {"Headset Mic", NULL, "micbias2"}, {"IN1P", NULL, "Headset Mic"}, {"IN1N", NULL, "Headset Mic"}, }; @@ -274,15 +272,10 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) } mt8173_rt5650_codecs[1].of_node = mt8173_rt5650_codecs[0].of_node; - if (of_find_node_by_name(platform_node, "codec-capture")) { - np = of_get_child_by_name(pdev->dev.of_node, "codec-capture"); - if (!np) { - dev_err(&pdev->dev, - "%s: Can't find codec-capture DT node\n", - __func__); - return -EINVAL; - } + np = of_get_child_by_name(pdev->dev.of_node, "codec-capture"); + if (np) { ret = snd_soc_of_get_dai_name(np, &codec_capture_dai); + of_node_put(np); if (ret < 0) { dev_err(&pdev->dev, "%s codec_capture_dai name fail %d\n", diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c index b6615affe571..81b09d740ed9 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -67,7 +67,7 @@ static unsigned short nuc900_ac97_read(struct snd_ac97 *ac97, /* polling the AC_R_FINISH */ while (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_R_FINISH) - && timeout--) + && --timeout) mdelay(1); if (!timeout) { @@ -121,7 +121,7 @@ static void nuc900_ac97_write(struct snd_ac97 *ac97, unsigned short reg, /* polling the AC_W_FINISH */ while ((AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_W_FINISH) - && timeout--) + && --timeout) mdelay(1); if (!timeout) @@ -345,11 +345,10 @@ static int nuc900_ac97_drvprobe(struct platform_device *pdev) goto out; } - nuc900_audio->irq_num = platform_get_irq(pdev, 0); - if (!nuc900_audio->irq_num) { - ret = -EBUSY; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto out; - } + nuc900_audio->irq_num = ret; nuc900_ac97_data = nuc900_audio; diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index d40219678700..cb72c1e57da0 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -105,7 +105,7 @@ static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, int pin, changed = 0; /* Refuse any mode changes if we are not able to control the codec. */ - if (!cx20442_codec->hw_write) + if (!cx20442_codec->component.card->pop_time) return -EUNATCH; if (ucontrol->value.enumerated.item[0] >= control->items) @@ -345,7 +345,7 @@ static void cx81801_receive(struct tty_struct *tty, if (!codec) return; - if (!codec->hw_write) { + if (!codec->component.card->pop_time) { /* First modem response, complete setup procedure */ /* Initialize timer used for config pulse generation */ diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index d49adc822a11..704428735e3c 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -43,7 +43,7 @@ struct apq8016_sbc_data { static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_codec *codec; + struct snd_soc_component *component; struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_card *card = rtd->card; struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card); @@ -92,7 +92,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) jack = pdata->jack.jack; - snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); @@ -102,15 +102,15 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) for (i = 0 ; i < dai_link->num_codecs; i++) { struct snd_soc_dai *dai = rtd->codec_dais[i]; - codec = dai->codec; + component = dai->component; /* Set default mclk for internal codec */ - rval = snd_soc_codec_set_sysclk(codec, 0, 0, DEFAULT_MCLK_RATE, + rval = snd_soc_component_set_sysclk(component, 0, 0, DEFAULT_MCLK_RATE, SND_SOC_CLOCK_IN); if (rval != 0 && rval != -ENOTSUPP) { dev_warn(card->dev, "Failed to set mclk: %d\n", rval); return rval; } - rval = snd_soc_codec_set_jack(codec, &pdata->jack, NULL); + rval = snd_soc_component_set_jack(component, &pdata->jack, NULL); if (rval != 0 && rval != -ENOTSUPP) { dev_warn(card->dev, "Failed to set jack: %d\n", rval); return rval; diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index d64fbbd50544..214bfc78cf5c 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -206,7 +206,8 @@ static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) return ret; } - snd_jack_set_key(rockchip_sound_jack.jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key( + rockchip_sound_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); snd_jack_set_key( rockchip_sound_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); snd_jack_set_key( @@ -367,7 +368,8 @@ static const struct snd_soc_dai_link rockchip_dais[] = { [DAILINK_RT5514_DSP] = { .name = "RT5514 DSP", .stream_name = "Wake on Voice", - .codec_dai_name = "rt5514-dsp-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", }, }; @@ -528,7 +530,18 @@ static int rockchip_sound_of_parse_dais(struct device *dev, if (index < 0) continue; - np_cpu = (index == DAILINK_CDNDP) ? np_cpu1 : np_cpu0; + switch (index) { + case DAILINK_CDNDP: + np_cpu = np_cpu1; + break; + case DAILINK_RT5514_DSP: + np_cpu = np_codec; + break; + default: + np_cpu = np_cpu0; + break; + } + if (!np_cpu) { dev_err(dev, "Missing 'rockchip,cpu' for %s\n", rockchip_dais[index].name); @@ -538,7 +551,8 @@ static int rockchip_sound_of_parse_dais(struct device *dev, dai = &card->dai_link[card->num_links++]; *dai = rockchip_dais[index]; - dai->codec_of_node = np_codec; + if (!dai->codec_name) + dai->codec_of_node = np_codec; dai->platform_of_node = np_cpu; dai->cpu_of_node = np_cpu; diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 908211e1d6fc..950823d69e9c 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -328,6 +328,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, val |= I2S_CHN_4; break; case 2: + case 1: val |= I2S_CHN_2; break; default: @@ -460,7 +461,7 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = { }, .capture = { .stream_name = "Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_S8 | @@ -504,6 +505,7 @@ static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg) case I2S_INTCR: case I2S_XFER: case I2S_CLR: + case I2S_TXDR: case I2S_RXDR: case I2S_FIFOLR: case I2S_INTSR: @@ -518,6 +520,9 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg) switch (reg) { case I2S_INTSR: case I2S_CLR: + case I2S_FIFOLR: + case I2S_TXDR: + case I2S_RXDR: return true; default: return false; @@ -527,6 +532,8 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg) static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { + case I2S_RXDR: + return true; default: return false; } @@ -654,7 +661,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) } if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) { - if (val >= 2 && val <= 8) + if (val >= 1 && val <= 8) soc_dai->capture.channels_max = val; } diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index ee5055d47d13..a89fe9b6463b 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -322,26 +322,30 @@ static int rk_spdif_probe(struct platform_device *pdev) 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 = PTR_ERR(spdif->mclk); + goto err_disable_hclk; } ret = clk_prepare_enable(spdif->mclk); if (ret) { dev_err(spdif->dev, "clock enable failed %d\n", ret); - return ret; + goto err_disable_clocks; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err_disable_clocks; + } 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); + ret = PTR_ERR(spdif->regmap); + goto err_disable_clocks; } spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; @@ -373,6 +377,10 @@ static int rk_spdif_probe(struct platform_device *pdev) err_pm_runtime: pm_runtime_disable(&pdev->dev); +err_disable_clocks: + clk_disable_unprepare(spdif->mclk); +err_disable_hclk: + clk_disable_unprepare(spdif->hclk); return ret; } diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index 34deba461ae1..0e66cd8ef2f9 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -60,13 +60,13 @@ static int bells_set_bias_level(struct snd_soc_card *card, { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *codec_dai; - struct snd_soc_codec *codec; + struct snd_soc_component *component; struct bells_drvdata *bells = card->drvdata; int ret; rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); codec_dai = rtd->codec_dai; - codec = codec_dai->codec; + component = codec_dai->component; if (dapm->dev != codec_dai->dev) return 0; @@ -76,7 +76,7 @@ static int bells_set_bias_level(struct snd_soc_card *card, if (dapm->bias_level != SND_SOC_BIAS_STANDBY) break; - ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, + ret = snd_soc_component_set_pll(component, WM5102_FLL1, ARIZONA_FLL_SRC_MCLK1, MCLK_RATE, bells->sysclk_rate); @@ -84,7 +84,7 @@ static int bells_set_bias_level(struct snd_soc_card *card, pr_err("Failed to start FLL: %d\n", ret); if (bells->asyncclk_rate) { - ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, + ret = snd_soc_component_set_pll(component, WM5102_FLL2, ARIZONA_FLL_SRC_AIF2BCLK, BCLK2_RATE, bells->asyncclk_rate); @@ -106,27 +106,27 @@ static int bells_set_bias_level_post(struct snd_soc_card *card, { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *codec_dai; - struct snd_soc_codec *codec; + struct snd_soc_component *component; struct bells_drvdata *bells = card->drvdata; int ret; rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); codec_dai = rtd->codec_dai; - codec = codec_dai->codec; + component = codec_dai->component; if (dapm->dev != codec_dai->dev) return 0; switch (level) { case SND_SOC_BIAS_STANDBY: - ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0); + ret = snd_soc_component_set_pll(component, WM5102_FLL1, 0, 0, 0); if (ret < 0) { pr_err("Failed to stop FLL: %d\n", ret); return ret; } if (bells->asyncclk_rate) { - ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, + ret = snd_soc_component_set_pll(component, WM5102_FLL2, 0, 0, 0); if (ret < 0) { pr_err("Failed to stop FLL: %d\n", ret); @@ -148,8 +148,8 @@ static int bells_late_probe(struct snd_soc_card *card) { struct bells_drvdata *bells = card->drvdata; struct snd_soc_pcm_runtime *rtd; - struct snd_soc_codec *wm0010; - struct snd_soc_codec *codec; + struct snd_soc_component *wm0010; + struct snd_soc_component *component; struct snd_soc_dai *aif1_dai; struct snd_soc_dai *aif2_dai; struct snd_soc_dai *aif3_dai; @@ -157,22 +157,22 @@ static int bells_late_probe(struct snd_soc_card *card) int ret; rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name); - wm0010 = rtd->codec; + wm0010 = rtd->codec_dai->component; rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); - codec = rtd->codec; + component = rtd->codec_dai->component; aif1_dai = rtd->codec_dai; - ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, + ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1, bells->sysclk_rate, SND_SOC_CLOCK_IN); if (ret != 0) { - dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); + dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret); return ret; } - ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0); + ret = snd_soc_component_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0); if (ret != 0) { dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret); return ret; @@ -182,20 +182,20 @@ static int bells_late_probe(struct snd_soc_card *card) if (ret != 0) dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); - ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0, + ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_OPCLK, 0, SYS_MCLK_RATE, SND_SOC_CLOCK_OUT); if (ret != 0) - dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret); + dev_err(component->dev, "Failed to set OPCLK: %d\n", ret); if (card->num_rtd == DAI_CODEC_CP) return 0; - ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK, + ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK, ARIZONA_CLK_SRC_FLL2, bells->asyncclk_rate, SND_SOC_CLOCK_IN); if (ret != 0) { - dev_err(codec->dev, "Failed to set ASYNCCLK: %d\n", ret); + dev_err(component->dev, "Failed to set ASYNCCLK: %d\n", ret); return ret; } @@ -221,7 +221,7 @@ static int bells_late_probe(struct snd_soc_card *card) return ret; } - ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK, + ret = snd_soc_component_set_sysclk(wm9081_dai->component, WM9081_SYSCLK_MCLK, 0, SYS_MCLK_RATE, 0); if (ret != 0) { dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret); diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 8ddb08714faa..4672688cac32 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -222,7 +222,7 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, NULL, &val, NULL); val = val << shift; - mask = 0xffff << shift; + mask = 0x0f1f << shift; rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val); @@ -250,7 +250,7 @@ int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, in = in << shift; out = out << shift; - mask = 0xffff << shift; + mask = 0x0f1f << shift; switch (id / 2) { case 0: @@ -380,7 +380,7 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) ckr = 0x80000000; } - rsnd_mod_bset(adg_mod, BRGCKR, 0x80FF0000, adg->ckr | ckr); + rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); rsnd_mod_write(adg_mod, BRRA, adg->rbga); rsnd_mod_write(adg_mod, BRRB, adg->rbgb); diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index c70eb2097816..64d5ecb86528 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -197,16 +197,27 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) return 0; } -int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) +int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - return runtime->channels; + /* + * params will be added when refine + * see + * __rsnd_soc_hw_rule_rate() + * __rsnd_soc_hw_rule_channels() + */ + if (params) + return params_channels(params); + else + return runtime->channels; } -int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) +int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params) { - int chan = rsnd_runtime_channel_original(io); + int chan = rsnd_runtime_channel_original_with_params(io, params); struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); if (ctu_mod) { @@ -219,12 +230,13 @@ int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) return chan; } -int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) +int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); int chan = rsnd_io_is_play(io) ? - rsnd_runtime_channel_after_ctu(io) : - rsnd_runtime_channel_original(io); + rsnd_runtime_channel_after_ctu_with_params(io, params) : + rsnd_runtime_channel_original_with_params(io, params); /* Use Multi SSI */ if (rsnd_runtime_is_ssi_multi(io)) @@ -262,10 +274,10 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); - switch (runtime->sample_bits) { + switch (snd_pcm_format_width(runtime->format)) { case 16: return 8 << 16; - case 32: + case 24: return 0 << 16; } @@ -282,11 +294,12 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); struct rsnd_mod *target; struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 val = 0x76543210; - u32 mask = ~0; /* - * *Hardware* L/R and *Software* L/R are inverted. + * *Hardware* L/R and *Software* L/R are inverted for 16bit data. + * 31..16 15...0 + * HW: [L ch] [R ch] + * SW: [R ch] [L ch] * We need to care about inversion timing to control * Playback/Capture correctly. * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R @@ -313,27 +326,13 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) target = cmd ? cmd : ssiu; } - mask <<= runtime->channels * 4; - val = val & mask; - - switch (runtime->sample_bits) { - case 16: - val |= 0x67452301 & ~mask; - break; - case 32: - val |= 0x76543210 & ~mask; - break; - } - - /* - * exchange channeles on SRC if possible, - * otherwise, R/L volume settings on DVC - * changes inverted channels - */ - if (mod == target) - return val; - else + /* Non target mod or 24bit data needs normal DALIGN */ + if ((snd_pcm_format_width(runtime->format) != 16) || + (mod != target)) return 0x76543210; + /* Target mod needs inverted DALIGN when 16bit */ + else + return 0x67452301; } u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod) @@ -363,12 +362,8 @@ u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod) * HW 24bit data is located as 0x******00 * */ - switch (runtime->sample_bits) { - case 16: + if (snd_pcm_format_width(runtime->format) == 16) return 0; - case 32: - break; - } for (i = 0; i < ARRAY_SIZE(playback_mods); i++) { tmod = rsnd_io_to_mod(io, mods[i]); @@ -616,8 +611,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - rsnd_dai_stream_init(io, substream); - ret = rsnd_dai_call(init, io, priv); if (ret < 0) goto dai_trigger_end; @@ -639,7 +632,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret |= rsnd_dai_call(quit, io, priv); - rsnd_dai_stream_quit(io); break; default: ret = -EINVAL; @@ -784,8 +776,9 @@ static int rsnd_soc_hw_rule(struct rsnd_priv *priv, return snd_interval_refine(iv, &p); } -static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int __rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule, + int is_play) { struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); @@ -793,25 +786,37 @@ static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_soc_dai *dai = rule->private; struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture; /* * possible sampling rate limitation is same as * 2ch if it supports multi ssi + * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init()) */ ic = *ic_; - if (1 < rsnd_rdai_ssi_lane_get(rdai)) { - ic.min = 2; - ic.max = 2; - } + ic.min = + ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params); return rsnd_soc_hw_rule(priv, rsnd_soc_hw_rate_list, ARRAY_SIZE(rsnd_soc_hw_rate_list), &ic, ir); } +static int rsnd_soc_hw_rule_rate_playback(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + return __rsnd_soc_hw_rule_rate(params, rule, 1); +} + +static int rsnd_soc_hw_rule_rate_capture(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + return __rsnd_soc_hw_rule_rate(params, rule, 0); +} -static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int __rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule, + int is_play) { struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); @@ -819,22 +824,34 @@ static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_soc_dai *dai = rule->private; struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture; /* * possible sampling rate limitation is same as * 2ch if it supports multi ssi + * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init()) */ ic = *ic_; - if (1 < rsnd_rdai_ssi_lane_get(rdai)) { - ic.min = 2; - ic.max = 2; - } + ic.min = + ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params); return rsnd_soc_hw_rule(priv, rsnd_soc_hw_channels_list, ARRAY_SIZE(rsnd_soc_hw_channels_list), ir, &ic); } +static int rsnd_soc_hw_rule_channels_playback(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + return __rsnd_soc_hw_rule_channels(params, rule, 1); +} + +static int rsnd_soc_hw_rule_channels_capture(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + return __rsnd_soc_hw_rule_channels(params, rule, 0); +} + static const struct snd_pcm_hardware rsnd_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | @@ -859,6 +876,8 @@ static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, int ret; int i; + rsnd_dai_stream_init(io, substream); + /* * Channel Limitation * It depends on Platform design @@ -886,11 +905,17 @@ static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, * It depends on Clock Master Mode */ if (rsnd_rdai_is_clk_master(rdai)) { + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - rsnd_soc_hw_rule_rate, dai, + is_play ? rsnd_soc_hw_rule_rate_playback : + rsnd_soc_hw_rule_rate_capture, + dai, SNDRV_PCM_HW_PARAM_CHANNELS, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - rsnd_soc_hw_rule_channels, dai, + is_play ? rsnd_soc_hw_rule_channels_playback : + rsnd_soc_hw_rule_channels_capture, + dai, SNDRV_PCM_HW_PARAM_RATE, -1); } @@ -915,6 +940,8 @@ static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, * call rsnd_dai_call without spinlock */ rsnd_dai_call(nolock_stop, io, priv); + + rsnd_dai_stream_quit(io); } static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { @@ -990,7 +1017,7 @@ of_node_compatible: static void __rsnd_dai_probe(struct rsnd_priv *priv, struct device_node *dai_np, - int dai_i, int is_graph) + int dai_i) { struct device_node *playback, *capture; struct rsnd_dai_stream *io_playback; @@ -1089,13 +1116,13 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) dai_i = 0; if (is_graph) { for_each_endpoint_of_node(dai_node, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_i, is_graph); + __rsnd_dai_probe(priv, dai_np, dai_i); rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i); dai_i++; } } else { for_each_child_of_node(dai_node, dai_np) - __rsnd_dai_probe(priv, dai_np, dai_i++, is_graph); + __rsnd_dai_probe(priv, dai_np, dai_i++); } return 0; @@ -1332,8 +1359,8 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) return snd_pcm_lib_preallocate_pages_for_all( rtd->pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), + SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); } @@ -1496,6 +1523,8 @@ static int rsnd_remove(struct platform_device *pdev) }; int ret = 0, i; + snd_soc_disconnect_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); for_each_rsnd_dai(rdai, priv, i) { diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index fd557abfe390..41de23417c4a 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -26,10 +26,7 @@ struct rsnd_dmaen { struct dma_chan *chan; dma_cookie_t cookie; - dma_addr_t dma_buf; unsigned int dma_len; - unsigned int dma_period; - unsigned int dma_cnt; }; struct rsnd_dmapp { @@ -71,69 +68,10 @@ static struct rsnd_mod mem = { /* * Audio DMAC */ -#define rsnd_dmaen_sync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 1) -#define rsnd_dmaen_unsync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 0) -static void __rsnd_dmaen_sync(struct rsnd_dmaen *dmaen, struct rsnd_dai_stream *io, - int i, int sync) -{ - struct device *dev = dmaen->chan->device->dev; - enum dma_data_direction dir; - int is_play = rsnd_io_is_play(io); - dma_addr_t buf; - int len, max; - size_t period; - - len = dmaen->dma_len; - period = dmaen->dma_period; - max = len / period; - i = i % max; - buf = dmaen->dma_buf + (period * i); - - dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - - if (sync) - dma_sync_single_for_device(dev, buf, period, dir); - else - dma_sync_single_for_cpu(dev, buf, period, dir); -} - static void __rsnd_dmaen_complete(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - bool elapsed = false; - unsigned long flags; - - /* - * Renesas sound Gen1 needs 1 DMAC, - * Gen2 needs 2 DMAC. - * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. - * But, Audio-DMAC-peri-peri doesn't have interrupt, - * and this driver is assuming that here. - */ - spin_lock_irqsave(&priv->lock, flags); - - if (rsnd_io_is_working(io)) { - rsnd_dmaen_unsync(dmaen, io, dmaen->dma_cnt); - - /* - * Next period is already started. - * Let's sync Next Next period - * see - * rsnd_dmaen_start() - */ - rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2); - - elapsed = true; - - dmaen->dma_cnt++; - } - - spin_unlock_irqrestore(&priv->lock, flags); - - if (elapsed) + if (rsnd_io_is_working(io)) rsnd_dai_period_elapsed(io); } @@ -165,14 +103,8 @@ static int rsnd_dmaen_stop(struct rsnd_mod *mod, struct rsnd_dma *dma = rsnd_mod_to_dma(mod); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - if (dmaen->chan) { - int is_play = rsnd_io_is_play(io); - + if (dmaen->chan) dmaengine_terminate_all(dmaen->chan); - dma_unmap_single(dmaen->chan->device->dev, - dmaen->dma_buf, dmaen->dma_len, - is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - } return 0; } @@ -237,11 +169,7 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; struct dma_slave_config cfg = {}; - dma_addr_t buf; - size_t len; - size_t period; int is_play = rsnd_io_is_play(io); - int i; int ret; cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; @@ -258,19 +186,10 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, if (ret < 0) return ret; - len = snd_pcm_lib_buffer_bytes(substream); - period = snd_pcm_lib_period_bytes(substream); - buf = dma_map_single(dmaen->chan->device->dev, - substream->runtime->dma_area, - len, - is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dmaen->chan->device->dev, buf)) { - dev_err(dev, "dma map failed\n"); - return -EIO; - } - desc = dmaengine_prep_dma_cyclic(dmaen->chan, - buf, len, period, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -282,18 +201,7 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, desc->callback = rsnd_dmaen_complete; desc->callback_param = rsnd_mod_get(dma); - dmaen->dma_buf = buf; - dmaen->dma_len = len; - dmaen->dma_period = period; - dmaen->dma_cnt = 0; - - /* - * synchronize this and next period - * see - * __rsnd_dmaen_complete() - */ - for (i = 0; i < 2; i++) - rsnd_dmaen_sync(dmaen, io, i); + dmaen->dma_len = snd_pcm_lib_buffer_bytes(substream); dmaen->cookie = dmaengine_submit(desc); if (dmaen->cookie < 0) { diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 57cd2bc773c2..ad6523595b0a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -399,9 +399,18 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); -int rsnd_runtime_channel_original(struct rsnd_dai_stream *io); -int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io); -int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io); +#define rsnd_runtime_channel_original(io) \ + rsnd_runtime_channel_original_with_params(io, NULL) +int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params); +#define rsnd_runtime_channel_after_ctu(io) \ + rsnd_runtime_channel_after_ctu_with_params(io, NULL) +int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params); +#define rsnd_runtime_channel_for_ssi(io) \ + rsnd_runtime_channel_for_ssi_with_params(io, NULL) +int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params); int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fece1e5f582f..97a9db892a8f 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -79,8 +79,8 @@ struct rsnd_ssi { int irq; unsigned int usrcnt; + /* for PIO */ int byte_pos; - int period_pos; int byte_per_period; int next_period_byte; }; @@ -371,11 +371,11 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, if (rsnd_io_is_play(io)) cr_own |= TRMD; - switch (runtime->sample_bits) { + switch (snd_pcm_format_width(runtime->format)) { case 16: cr_own |= DWL_16; break; - case 32: + case 24: cr_own |= DWL_24; break; } @@ -414,59 +414,6 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod) ssi->cr_en); } -static void rsnd_ssi_pointer_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - - ssi->byte_pos = 0; - ssi->period_pos = 0; - ssi->byte_per_period = runtime->period_size * - runtime->channels * - samples_to_bytes(runtime, 1); - ssi->next_period_byte = ssi->byte_per_period; -} - -static int rsnd_ssi_pointer_offset(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - int additional) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - int pos = ssi->byte_pos + additional; - - pos %= (runtime->periods * ssi->byte_per_period); - - return pos; -} - -static bool rsnd_ssi_pointer_update(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - int byte) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - ssi->byte_pos += byte; - - if (ssi->byte_pos >= ssi->next_period_byte) { - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - - ssi->period_pos++; - ssi->next_period_byte += ssi->byte_per_period; - - if (ssi->period_pos >= runtime->periods) { - ssi->byte_pos = 0; - ssi->period_pos = 0; - ssi->next_period_byte = ssi->byte_per_period; - } - - return true; - } - - return false; -} - /* * SSI mod common functions */ @@ -480,8 +427,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (!rsnd_ssi_is_run_mods(mod, io)) return 0; - rsnd_ssi_pointer_init(mod, io); - ssi->usrcnt++; rsnd_mod_power_on(mod); @@ -652,6 +597,8 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod, return 0; } +static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io); static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { @@ -670,30 +617,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, status = rsnd_ssi_status_get(mod); /* PIO only */ - if (!is_dma && (status & DIRQ)) { - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 *buf = (u32 *)(runtime->dma_area + - rsnd_ssi_pointer_offset(mod, io, 0)); - int shift = 0; - - switch (runtime->sample_bits) { - case 32: - shift = 8; - break; - } - - /* - * 8/16/32 data can be assesse to TDR/RDR register - * directly as 32bit data - * see rsnd_ssi_init() - */ - if (rsnd_io_is_play(io)) - rsnd_mod_write(mod, SSITDR, (*buf) << shift); - else - *buf = (rsnd_mod_read(mod, SSIRDR) >> shift); - - elapsed = rsnd_ssi_pointer_update(mod, io, sizeof(*buf)); - } + if (!is_dma && (status & DIRQ)) + elapsed = rsnd_ssi_pio_interrupt(mod, io); /* DMA only */ if (is_dma && (status & (UIRQ | OIRQ))) @@ -831,14 +756,78 @@ static int rsnd_ssi_common_remove(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_pointer(struct rsnd_mod *mod, +/* + * SSI PIO functions + */ +static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 *buf = (u32 *)(runtime->dma_area + ssi->byte_pos); + int shift = 0; + int byte_pos; + bool elapsed = false; + + if (snd_pcm_format_width(runtime->format) == 24) + shift = 8; + + /* + * 8/16/32 data can be assesse to TDR/RDR register + * directly as 32bit data + * see rsnd_ssi_init() + */ + if (rsnd_io_is_play(io)) + rsnd_mod_write(mod, SSITDR, (*buf) << shift); + else + *buf = (rsnd_mod_read(mod, SSIRDR) >> shift); + + byte_pos = ssi->byte_pos + sizeof(*buf); + + if (byte_pos >= ssi->next_period_byte) { + int period_pos = byte_pos / ssi->byte_per_period; + + if (period_pos >= runtime->periods) { + byte_pos = 0; + period_pos = 0; + } + + ssi->next_period_byte = (period_pos + 1) * ssi->byte_per_period; + + elapsed = true; + } + + WRITE_ONCE(ssi->byte_pos, byte_pos); + + return elapsed; +} + +static int rsnd_ssi_pio_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + if (!rsnd_ssi_is_parent(mod, io)) { + ssi->byte_pos = 0; + ssi->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + ssi->next_period_byte = ssi->byte_per_period; + } + + return rsnd_ssi_init(mod, io, priv); +} + +static int rsnd_ssi_pio_pointer(struct rsnd_mod *mod, struct rsnd_dai_stream *io, snd_pcm_uframes_t *pointer) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - *pointer = bytes_to_frames(runtime, ssi->byte_pos); + *pointer = bytes_to_frames(runtime, READ_ONCE(ssi->byte_pos)); return 0; } @@ -847,12 +836,12 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, .probe = rsnd_ssi_common_probe, .remove = rsnd_ssi_common_remove, - .init = rsnd_ssi_init, + .init = rsnd_ssi_pio_init, .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, .irq = rsnd_ssi_irq, - .pointer= rsnd_ssi_pointer, + .pointer = rsnd_ssi_pio_pointer, .pcm_new = rsnd_ssi_pcm_new, .hw_params = rsnd_ssi_hw_params, }; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 4d948757d300..6ff8a36c2c82 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -125,6 +125,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, { int hdmi = rsnd_ssi_hdmi_port(io); int ret; + u32 mode = 0; ret = rsnd_ssiu_init(mod, io, priv); if (ret < 0) @@ -136,9 +137,11 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, * see * rsnd_ssi_config_init() */ - rsnd_mod_write(mod, SSI_MODE, 0x1); + mode = 0x1; } + rsnd_mod_write(mod, SSI_MODE, mode); + if (rsnd_ssi_use_busif(io)) { rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr_bit(mod, io) | diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c index f21df28bc28e..3d7e1ff79139 100644 --- a/sound/soc/soc-acpi.c +++ b/sound/soc/soc-acpi.c @@ -16,79 +16,16 @@ #include <sound/soc-acpi.h> -static acpi_status snd_soc_acpi_find_name(acpi_handle handle, u32 level, - void *context, void **ret) -{ - struct acpi_device *adev; - const char *name = NULL; - - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - - if (adev->status.present && adev->status.functional) { - name = acpi_dev_name(adev); - *(const char **)ret = name; - return AE_CTRL_TERMINATE; - } - - return AE_OK; -} - -const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) -{ - const char *name = NULL; - acpi_status status; - - status = acpi_get_devices(hid, snd_soc_acpi_find_name, NULL, - (void **)&name); - - if (ACPI_FAILURE(status) || name[0] == '\0') - return NULL; - - return name; -} -EXPORT_SYMBOL_GPL(snd_soc_acpi_find_name_from_hid); - -static acpi_status snd_soc_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - unsigned long long sta; - acpi_status status; - - *(bool *)context = true; - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) - *(bool *)context = false; - - return AE_OK; -} - -bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) -{ - acpi_status status; - bool found = false; - - status = acpi_get_devices(hid, snd_soc_acpi_mach_match, &found, NULL); - - if (ACPI_FAILURE(status)) - return false; - - return found; -} -EXPORT_SYMBOL_GPL(snd_soc_acpi_check_hid); - struct snd_soc_acpi_mach * snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines) { struct snd_soc_acpi_mach *mach; for (mach = machines; mach->id[0]; mach++) { - if (snd_soc_acpi_check_hid(mach->id) == true) { - if (mach->machine_quirk == NULL) - return mach; - - if (mach->machine_quirk(mach) != NULL) - return mach; + if (acpi_dev_present(mach->id, NULL, -1)) { + if (mach->machine_quirk) + mach = mach->machine_quirk(mach); + return mach; } } return NULL; @@ -163,7 +100,7 @@ struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) return mach; for (i = 0; i < codec_list->num_codecs; i++) { - if (snd_soc_acpi_check_hid(codec_list->codecs[i]) != true) + if (!acpi_dev_present(codec_list->codecs[i], NULL, -1)) return NULL; } diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index d9b1e6417fb9..82402688bd8e 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -40,7 +40,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); if (ret < 0) { - dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n", + dev_err(cpu_dai->dev, + "Compress ASoC: can't open interface %s: %d\n", cpu_dai->name, ret); goto out; } @@ -49,8 +50,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream) if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { - pr_err("compress asoc: can't open platform %s\n", - platform->component.name); + dev_err(platform->dev, + "Compress ASoC: can't open platform %s: %d\n", + platform->component.name, ret); goto plat_err; } } @@ -68,8 +70,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream) __ret = component->driver->compr_ops->open(cstream); if (__ret < 0) { - pr_err("compress asoc: can't open platform %s\n", - component->name); + dev_err(component->dev, + "Compress ASoC: can't open platform %s: %d\n", + component->name, __ret); ret = __ret; } } @@ -79,7 +82,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream) if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) { ret = rtd->dai_link->compr_ops->startup(cstream); if (ret < 0) { - pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name); + dev_err(rtd->dev, + "Compress ASoC: %s startup failed: %d\n", + rtd->dai_link->name, ret); goto machine_err; } } @@ -139,18 +144,19 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); if (ret < 0) { - dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n", + dev_err(cpu_dai->dev, + "Compress ASoC: can't open interface %s: %d\n", cpu_dai->name, ret); goto out; } } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { - pr_err("compress asoc: can't open platform %s\n", - platform->component.name); + dev_err(platform->dev, + "Compress ASoC: can't open platform %s: %d\n", + platform->component.name, ret); goto plat_err; } } @@ -168,8 +174,9 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) __ret = component->driver->compr_ops->open(cstream); if (__ret < 0) { - pr_err("compress asoc: can't open platform %s\n", - component->name); + dev_err(component->dev, + "Compress ASoC: can't open platform %s: %d\n", + component->name, __ret); ret = __ret; } } @@ -179,7 +186,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { ret = fe->dai_link->compr_ops->startup(cstream); if (ret < 0) { - pr_err("compress asoc: %s startup failed\n", fe->dai_link->name); + pr_err("Compress ASoC: %s startup failed: %d\n", + fe->dai_link->name, ret); goto machine_err; } } @@ -190,7 +198,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (ret < 0) goto fe_err; else if (ret == 0) - dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", + dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n", fe->dai_link->name, stream ? "capture" : "playback"); /* calculate valid and active FE <-> BE dpcms */ @@ -265,10 +273,11 @@ static void close_delayed_work(struct work_struct *work) mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", - codec_dai->driver->playback.stream_name, - codec_dai->playback_active ? "active" : "inactive", - rtd->pop_wait ? "yes" : "no"); + dev_dbg(rtd->dev, + "Compress ASoC: pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + rtd->pop_wait ? "yes" : "no"); /* are we waiting on this codec DAI stream */ if (rtd->pop_wait == 1) { @@ -307,7 +316,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (!codec_dai->active) codec_dai->rate = 0; - if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) rtd->dai_link->compr_ops->shutdown(cstream); @@ -376,7 +384,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) ret = dpcm_be_dai_hw_free(fe, stream); if (ret < 0) - dev_err(fe->dev, "compressed hw_free failed %d\n", ret); + dev_err(fe->dev, "Compressed ASoC: hw_free failed: %d\n", ret); ret = dpcm_be_dai_shutdown(fe, stream); @@ -460,7 +468,6 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); @@ -944,7 +951,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - int ret = 0, __ret; + int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -965,10 +972,10 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, !component->driver->compr_ops->copy) continue; - __ret = component->driver->compr_ops->copy(cstream, buf, count); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->copy(cstream, buf, count); + break; } + err: mutex_unlock(&rtd->pcm_mutex); return ret; @@ -1096,7 +1103,6 @@ static struct snd_compr_ops soc_compr_dyn_ops = { */ 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; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; @@ -1109,7 +1115,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) int playback = 0, capture = 0; if (rtd->num_codecs > 1) { - dev_err(rtd->card->dev, "Multicodec not supported for compressed stream\n"); + dev_err(rtd->card->dev, + "Compress ASoC: Multicodec not supported\n"); return -EINVAL; } @@ -1127,8 +1134,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) * should be set, check for that (xor) */ if (playback + capture != 1) { - dev_err(rtd->card->dev, "Invalid direction for compress P %d, C %d\n", - playback, capture); + dev_err(rtd->card->dev, + "Compress ASoC: Invalid direction for P %d, C %d\n", + playback, capture); return -EINVAL; } @@ -1156,8 +1164,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) rtd->dai_link->dpcm_playback, rtd->dai_link->dpcm_capture, &be_pcm); if (ret < 0) { - dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n", - rtd->dai_link->name); + dev_err(rtd->card->dev, + "Compress ASoC: can't create compressed for %s: %d\n", + rtd->dai_link->name, ret); goto compr_err; } @@ -1199,8 +1208,10 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) ret = snd_compress_new(rtd->card->snd_card, num, direction, new_name, compr); if (ret < 0) { - pr_err("compress asoc: can't create compress for codec %s\n", - codec->component.name); + component = rtd->codec_dai->component; + dev_err(component->dev, + "Compress ASoC: can't create compress for codec %s: %d\n", + component->name, ret); goto compr_err; } @@ -1210,8 +1221,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) rtd->compr = compr; compr->private_data = rtd; - printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, - cpu_dai->name); + dev_info(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n", + codec_dai->name, cpu_dai->name); return ret; compr_err: diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c0edac80df34..65537074c053 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -213,7 +213,7 @@ static umode_t soc_dev_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_pmdown_time.attr) return attr->mode; /* always visible */ - return rtd->codec ? attr->mode : 0; /* enabled only with codec */ + return rtd->num_codecs ? attr->mode : 0; /* enabled only with codec */ } static const struct attribute_group soc_dapm_dev_group = { @@ -590,14 +590,23 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, { struct snd_soc_rtdcom_list *rtdcom; + if (!driver_name) + return NULL; + for_each_rtdcom(rtd, rtdcom) { - if ((rtdcom->component->driver->name == driver_name) || - strcmp(rtdcom->component->driver->name, driver_name) == 0) + const char *component_name = rtdcom->component->driver->name; + + if (!component_name) + continue; + + if ((component_name == driver_name) || + strcmp(component_name, driver_name) == 0) return rtdcom->component; } return NULL; } +EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup); struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, const char *dai_link, int stream) @@ -1392,6 +1401,17 @@ static int soc_init_dai_link(struct snd_soc_card *card, return 0; } +void snd_soc_disconnect_sync(struct device *dev) +{ + struct snd_soc_component *component = snd_soc_lookup_component(dev, NULL); + + if (!component || !component->card) + return; + + snd_card_disconnect_sync(component->card->snd_card); +} +EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync); + /** * snd_soc_add_dai_link - Add a DAI link dynamically * @card: The ASoC card to which the DAI link is added @@ -1945,7 +1965,9 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, } /* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */ - if (cpu_dai->codec) { + /* the component which has non_legacy_dai_naming is Codec */ + if (cpu_dai->codec || + cpu_dai->component->driver->non_legacy_dai_naming) { unsigned int inv_dai_fmt; inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; @@ -3149,7 +3171,7 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; - list_add(&dai->list, &component->dai_list); + list_add_tail(&dai->list, &component->dai_list); component->num_dai++; dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); @@ -3176,8 +3198,6 @@ static int snd_soc_register_dais(struct snd_soc_component *component, dev_dbg(dev, "ASoC: dai register %s #%zu\n", dev_name(dev), count); - component->dai_drv = dai_drv; - for (i = 0; i < count; i++) { dai = soc_add_dai(component, dai_drv + i, @@ -4354,6 +4374,7 @@ int snd_soc_get_dai_name(struct of_phandle_args *args, args, dai_name); } else { + struct snd_soc_dai *dai; int id = -1; switch (args->args_count) { @@ -4375,7 +4396,14 @@ int snd_soc_get_dai_name(struct of_phandle_args *args, ret = 0; - *dai_name = pos->dai_drv[id].name; + /* find target DAI */ + list_for_each_entry(dai, &pos->dai_list, list) { + if (id == 0) + break; + id--; + } + + *dai_name = dai->driver->name; if (!*dai_name) *dai_name = pos->name; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a10b21cfc31e..ee6d9d9a3c5e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2026,7 +2026,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, snd_soc_dapm_for_each_direction(dir) { rdir = SND_SOC_DAPM_DIR_REVERSE(dir); snd_soc_dapm_widget_for_each_path(w, dir, p) { - if (p->connected && !p->connected(w, p->node[rdir])) + if (p->connected && !p->connected(p->source, p->sink)) continue; if (!p->connect) diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 20340ade20a7..2bc1c4c17896 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -34,6 +34,10 @@ int snd_soc_component_read(struct snd_soc_component *component, ret = regmap_read(component->regmap, reg, val); else if (component->read) ret = component->read(component, reg, val); + else if (component->driver->read) { + *val = component->driver->read(component, reg); + ret = 0; + } else ret = -EIO; @@ -70,6 +74,8 @@ int snd_soc_component_write(struct snd_soc_component *component, return regmap_write(component->regmap, reg, val); else if (component->write) return component->write(component, reg, val); + else if (component->driver->write) + return component->driver->write(component, reg, val); else return -EIO; } diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 500f98c730b9..7144a51ddfa9 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -378,7 +378,7 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, unsigned int rshift = mc->rshift; int max = mc->max; int min = mc->min; - int mask = (1 << (fls(min + max) - 1)) - 1; + unsigned int mask = (1 << (fls(min + max) - 1)) - 1; unsigned int val; int ret; @@ -423,7 +423,7 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, unsigned int rshift = mc->rshift; int max = mc->max; int min = mc->min; - int mask = (1 << (fls(min + max) - 1)) - 1; + unsigned int mask = (1 << (fls(min + max) - 1)) - 1; int err = 0; unsigned int val, val_mask, val2 = 0; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 8075856668c2..084125463d10 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -144,7 +144,7 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - ignore &= !component->driver->pmdown_time; + ignore &= !component->driver->use_pmdown_time; } /* this will be removed */ @@ -2831,10 +2831,9 @@ static void soc_pcm_private_free(struct snd_pcm *pcm) struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; + /* need to sync the delayed work before releasing resources */ + flush_delayed_work(&rtd->delayed_work); for_each_rtdcom(rtd, rtdcom) { - /* need to sync the delayed work before releasing resources */ - - flush_delayed_work(&rtd->delayed_work); component = rtdcom->component; if (component->pcm_free) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index e30aacbcfc29..bcd3da2739e2 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -288,7 +288,7 @@ static const struct snd_soc_platform_driver dummy_platform = { .ops = &dummy_dma_ops, }; -static struct snd_soc_codec_driver dummy_codec; +static const struct snd_soc_codec_driver dummy_codec; #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 3398e6c57f37..48f9ddd94016 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -2,7 +2,7 @@ menu "STMicroelectronics STM32 SOC audio support" config SND_SOC_STM32_SAI tristate "STM32 SAI interface (Serial Audio Interface) support" - depends on ARCH_STM32 || COMPILE_TEST + depends on (ARCH_STM32 && OF) || COMPILE_TEST depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO @@ -11,7 +11,7 @@ config SND_SOC_STM32_SAI config SND_SOC_STM32_I2S tristate "STM32 I2S interface (SPI/I2S block) support" - depends on ARCH_STM32 || COMPILE_TEST + depends on (ARCH_STM32 && OF) || COMPILE_TEST depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO @@ -20,7 +20,7 @@ config SND_SOC_STM32_I2S config SND_SOC_STM32_SPDIFRX tristate "STM32 S/PDIF receiver (SPDIFRX) support" - depends on ARCH_STM32 || COMPILE_TEST + depends on (ARCH_STM32 && OF) || COMPILE_TEST depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO @@ -28,4 +28,16 @@ config SND_SOC_STM32_SPDIFRX help Say Y if you want to enable S/PDIF capture for STM32 +config SND_SOC_STM32_DFSDM + tristate "SoC Audio support for STM32 DFSDM" + depends on ARCH_STM32 || COMPILE_TEST + depends on SND_SOC + depends on STM32_DFSDM_ADC + select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_SOC_DMIC + select IIO_BUFFER_CB + help + Select this option to enable the STM32 Digital Filter + for Sigma Delta Modulators (DFSDM) driver used + in various STM32 series for digital microphone capture. endmenu diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile index 5b7f0fab0bd6..3143c0b47042 100644 --- a/sound/soc/stm/Makefile +++ b/sound/soc/stm/Makefile @@ -13,3 +13,6 @@ obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o # SPDIFRX snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o + +#DFSDM +obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c new file mode 100644 index 000000000000..7306e3eca9e1 --- /dev/null +++ b/sound/soc/stm/stm32_adfsdm.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of STM32 DFSDM ASoC DAI driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> + * Olivier Moysan <olivier.moysan@st.com> + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <linux/iio/iio.h> +#include <linux/iio/consumer.h> +#include <linux/iio/adc/stm32-dfsdm-adc.h> + +#include <sound/pcm.h> +#include <sound/soc.h> + +#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm" + +#define DFSDM_MAX_PERIOD_SIZE (PAGE_SIZE / 2) +#define DFSDM_MAX_PERIODS 6 + +struct stm32_adfsdm_priv { + struct snd_soc_dai_driver dai_drv; + struct snd_pcm_substream *substream; + struct device *dev; + + /* IIO */ + struct iio_channel *iio_ch; + struct iio_cb_buffer *iio_cb; + bool iio_active; + + /* PCM buffer */ + unsigned char *pcm_buff; + unsigned int pos; +}; + +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + + .rate_min = 8000, + .rate_max = 32000, + + .channels_min = 1, + .channels_max = 1, + + .periods_min = 2, + .periods_max = DFSDM_MAX_PERIODS, + + .period_bytes_max = DFSDM_MAX_PERIOD_SIZE, + .buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE +}; + +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai); + + if (priv->iio_active) { + iio_channel_stop_all_cb(priv->iio_cb); + priv->iio_active = false; + } +} + +static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = iio_write_channel_attribute(priv->iio_ch, + substream->runtime->rate, 0, + IIO_CHAN_INFO_SAMP_FREQ); + if (ret < 0) { + dev_err(dai->dev, "%s: Failed to set %d sampling rate\n", + __func__, substream->runtime->rate); + return ret; + } + + if (!priv->iio_active) { + ret = iio_channel_start_all_cb(priv->iio_cb); + if (!ret) + priv->iio_active = true; + else + dev_err(dai->dev, "%s: IIO channel start failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai); + ssize_t size; + char str_freq[10]; + + dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq); + + /* Set IIO frequency if CODEC is master as clock comes from SPI_IN */ + + snprintf(str_freq, sizeof(str_freq), "%d\n", freq); + size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq", + str_freq, sizeof(str_freq)); + if (size != sizeof(str_freq)) { + dev_err(dai->dev, "%s: Failed to set SPI clock\n", + __func__); + return -EINVAL; + } + return 0; +} + +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = { + .shutdown = stm32_adfsdm_shutdown, + .prepare = stm32_adfsdm_dai_prepare, + .set_sysclk = stm32_adfsdm_set_sysclk, +}; + +static const struct snd_soc_dai_driver stm32_adfsdm_dai = { + .capture = { + .channels_min = 1, + .channels_max = 1, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000), + }, + .ops = &stm32_adfsdm_dai_ops, +}; + +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = { + .name = "stm32_dfsdm_audio", +}; + +static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private) +{ + struct stm32_adfsdm_priv *priv = private; + struct snd_soc_pcm_runtime *rtd = priv->substream->private_data; + u8 *pcm_buff = priv->pcm_buff; + u8 *src_buff = (u8 *)data; + unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream); + unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream); + unsigned int old_pos = priv->pos; + unsigned int cur_size = size; + + dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %zu\n", + __func__, &pcm_buff[priv->pos], priv->pos, size); + + if ((priv->pos + size) > buff_size) { + memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos); + cur_size -= buff_size - priv->pos; + priv->pos = 0; + } + + memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size); + priv->pos = (priv->pos + cur_size) % buff_size; + + if (cur_size != size || (old_pos && (old_pos % period_size < size))) + snd_pcm_period_elapsed(priv->substream); + + return 0; +} + +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct stm32_adfsdm_priv *priv = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + priv->pos = 0; + return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev, + stm32_afsdm_pcm_cb, priv); + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev); + } + + return -EINVAL; +} + +static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + int ret; + + ret = snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw); + if (!ret) + priv->substream = substream; + + return ret; +} + +static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct stm32_adfsdm_priv *priv = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + + snd_pcm_lib_free_pages(substream); + priv->substream = NULL; + + return 0; +} + +static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct stm32_adfsdm_priv *priv = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + + return bytes_to_frames(substream->runtime, priv->pos); +} + +static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct stm32_adfsdm_priv *priv = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + int ret; + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + priv->pcm_buff = substream->runtime->dma_area; + + return iio_channel_cb_set_buffer_watermark(priv->iio_cb, + params_period_size(params)); +} + +static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static struct snd_pcm_ops stm32_adfsdm_pcm_ops = { + .open = stm32_adfsdm_pcm_open, + .close = stm32_adfsdm_pcm_close, + .hw_params = stm32_adfsdm_pcm_hw_params, + .hw_free = stm32_adfsdm_pcm_hw_free, + .trigger = stm32_adfsdm_trigger, + .pointer = stm32_adfsdm_pcm_pointer, +}; + +static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct stm32_adfsdm_priv *priv = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE; + + return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + priv->dev, size, size); +} + +static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_soc_pcm_runtime *rtd; + struct stm32_adfsdm_priv *priv; + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) { + rtd = substream->private_data; + priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + + snd_pcm_lib_preallocate_free_for_all(pcm); + } +} + +static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = { + .ops = &stm32_adfsdm_pcm_ops, + .pcm_new = stm32_adfsdm_pcm_new, + .pcm_free = stm32_adfsdm_pcm_free, +}; + +static const struct of_device_id stm32_adfsdm_of_match[] = { + {.compatible = "st,stm32h7-dfsdm-dai"}, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match); + +static int stm32_adfsdm_probe(struct platform_device *pdev) +{ + struct stm32_adfsdm_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->dai_drv = stm32_adfsdm_dai; + + dev_set_drvdata(&pdev->dev, priv); + + ret = devm_snd_soc_register_component(&pdev->dev, + &stm32_adfsdm_dai_component, + &priv->dai_drv, 1); + if (ret < 0) + return ret; + + /* Associate iio channel */ + priv->iio_ch = devm_iio_channel_get_all(&pdev->dev); + if (IS_ERR(priv->iio_ch)) + return PTR_ERR(priv->iio_ch); + + priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL); + if (IS_ERR(priv->iio_cb)) + return PTR_ERR(priv->iio_cb); + + ret = devm_snd_soc_register_platform(&pdev->dev, + &stm32_adfsdm_soc_platform); + if (ret < 0) + dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", + __func__); + + return ret; +} + +static struct platform_driver stm32_adfsdm_driver = { + .driver = { + .name = STM32_ADFSDM_DRV_NAME, + .of_match_table = stm32_adfsdm_of_match, + }, + .probe = stm32_adfsdm_probe, +}; + +module_platform_driver(stm32_adfsdm_driver); + +MODULE_DESCRIPTION("stm32 DFSDM DAI driver"); +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index d6f71a3406e9..d743b7dd52fb 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -28,16 +28,6 @@ #include "stm32_sai.h" -static LIST_HEAD(sync_providers); -static DEFINE_MUTEX(sync_mutex); - -struct sync_provider { - struct list_head link; - struct device_node *node; - int (*sync_conf)(void *data, int synco); - void *data; -}; - static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = SAI_STM32F4, }; @@ -70,9 +60,8 @@ static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) return 0; } -static int stm32_sai_sync_conf_provider(void *data, int synco) +static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) { - struct stm32_sai_data *sai = (struct stm32_sai_data *)data; u32 prev_synco; int ret; @@ -103,83 +92,42 @@ static int stm32_sai_sync_conf_provider(void *data, int synco) return 0; } -static int stm32_sai_set_sync_provider(struct device_node *np, int synco) +static int stm32_sai_set_sync(struct stm32_sai_data *sai_client, + struct device_node *np_provider, + int synco, int synci) { - struct sync_provider *provider; + struct platform_device *pdev = of_find_device_by_node(np_provider); + struct stm32_sai_data *sai_provider; int ret; - mutex_lock(&sync_mutex); - list_for_each_entry(provider, &sync_providers, link) { - if (provider->node == np) { - ret = provider->sync_conf(provider->data, synco); - mutex_unlock(&sync_mutex); - return ret; - } + if (!pdev) { + dev_err(&sai_client->pdev->dev, + "Device not found for node %s\n", np_provider->name); + return -ENODEV; } - mutex_unlock(&sync_mutex); - /* SAI sync provider not found */ - return -ENODEV; -} - -static int stm32_sai_set_sync(struct stm32_sai_data *sai, - struct device_node *np_provider, - int synco, int synci) -{ - int ret; + sai_provider = platform_get_drvdata(pdev); + if (!sai_provider) { + dev_err(&sai_client->pdev->dev, + "SAI sync provider data not found\n"); + return -EINVAL; + } /* Configure sync client */ - stm32_sai_sync_conf_client(sai, synci); + ret = stm32_sai_sync_conf_client(sai_client, synci); + if (ret < 0) + return ret; /* Configure sync provider */ - ret = stm32_sai_set_sync_provider(np_provider, synco); - - return ret; -} - -static int stm32_sai_sync_add_provider(struct platform_device *pdev, - void *data) -{ - struct sync_provider *sp; - - sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); - if (!sp) - return -ENOMEM; - - sp->node = of_node_get(pdev->dev.of_node); - sp->data = data; - sp->sync_conf = &stm32_sai_sync_conf_provider; - - mutex_lock(&sync_mutex); - list_add(&sp->link, &sync_providers); - mutex_unlock(&sync_mutex); - - return 0; -} - -static void stm32_sai_sync_del_provider(struct device_node *np) -{ - struct sync_provider *sp; - - mutex_lock(&sync_mutex); - list_for_each_entry(sp, &sync_providers, link) { - if (sp->node == np) { - list_del(&sp->link); - of_node_put(sp->node); - break; - } - } - mutex_unlock(&sync_mutex); + return stm32_sai_sync_conf_provider(sai_provider, synco); } static int stm32_sai_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct stm32_sai_data *sai; struct reset_control *rst; struct resource *res; const struct of_device_id *of_id; - int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) @@ -231,28 +179,11 @@ static int stm32_sai_probe(struct platform_device *pdev) reset_control_deassert(rst); } - ret = stm32_sai_sync_add_provider(pdev, sai); - if (ret < 0) - return ret; - sai->set_sync = &stm32_sai_set_sync; - sai->pdev = pdev; + sai->set_sync = &stm32_sai_set_sync; platform_set_drvdata(pdev, sai); - ret = of_platform_populate(np, NULL, NULL, &pdev->dev); - if (ret < 0) - stm32_sai_sync_del_provider(np); - - return ret; -} - -static int stm32_sai_remove(struct platform_device *pdev) -{ - of_platform_depopulate(&pdev->dev); - - stm32_sai_sync_del_provider(pdev->dev.of_node); - - return 0; + return devm_of_platform_populate(&pdev->dev); } MODULE_DEVICE_TABLE(of, stm32_sai_ids); @@ -263,7 +194,6 @@ static struct platform_driver stm32_sai_driver = { .of_match_table = stm32_sai_ids, }, .probe = stm32_sai_probe, - .remove = stm32_sai_remove, }; module_platform_driver(stm32_sai_driver); diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 5da4efe7a550..886281673972 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -590,12 +590,28 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, hwrate); } + +static unsigned int sun4i_codec_src_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, + 44100, 48000, 96000, 192000 +}; + + +static struct snd_pcm_hw_constraint_list sun4i_codec_constraints = { + .count = ARRAY_SIZE(sun4i_codec_src_rates), + .list = sun4i_codec_src_rates, +}; + + 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); + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &sun4i_codec_constraints); + /* * Stop issuing DRQ when we have room for less than 16 samples * in our TX FIFO @@ -633,9 +649,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { .channels_max = 2, .rate_min = 8000, .rate_max = 192000, - .rates = SNDRV_PCM_RATE_8000_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 24, @@ -645,11 +659,8 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { .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, + .rate_max = 48000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 24, @@ -1128,7 +1139,7 @@ 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_RATES SNDRV_PCM_RATE_CONTINUOUS #define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 04f92583a969..dca1143c1150 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -269,10 +269,11 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) return false; } -static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, +static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, unsigned int rate, unsigned int word_size) { + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); unsigned int oversample_rate, clk_rate; int bclk_div, mclk_div; int ret; @@ -300,6 +301,7 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, break; default: + dev_err(dai->dev, "Unsupported sample rate: %u\n", rate); return -EINVAL; } @@ -308,18 +310,25 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, return ret; oversample_rate = i2s->mclk_freq / rate; - if (!sun4i_i2s_oversample_is_valid(oversample_rate)) + if (!sun4i_i2s_oversample_is_valid(oversample_rate)) { + dev_err(dai->dev, "Unsupported oversample rate: %d\n", + oversample_rate); return -EINVAL; + } bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, word_size); - if (bclk_div < 0) + if (bclk_div < 0) { + dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div); return -EINVAL; + } mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, clk_rate, rate); - if (mclk_div < 0) + if (mclk_div < 0) { + dev_err(dai->dev, "Unsupported MCLK divider: %d\n", mclk_div); return -EINVAL; + } /* Adjust the clock division values if needed */ bclk_div += i2s->variant->bclk_offset; @@ -349,8 +358,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, u32 width; channels = params_channels(params); - if (channels != 2) + if (channels != 2) { + dev_err(dai->dev, "Unsupported number of channels: %d\n", + channels); return -EINVAL; + } if (i2s->variant->has_chcfg) { regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, @@ -382,6 +394,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; default: + dev_err(dai->dev, "Unsupported physical sample width: %d\n", + params_physical_width(params)); return -EINVAL; } i2s->playback_dma_data.addr_width = width; @@ -393,6 +407,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, break; default: + dev_err(dai->dev, "Unsupported sample width: %d\n", + params_width(params)); return -EINVAL; } @@ -401,7 +417,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, regmap_field_write(i2s->field_fmt_sr, sr + i2s->variant->fmt_offset); - return sun4i_i2s_set_clk_rate(i2s, params_rate(params), + return sun4i_i2s_set_clk_rate(dai, params_rate(params), params_width(params)); } @@ -426,6 +442,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) val = SUN4I_I2S_FMT0_FMT_RIGHT_J; break; default: + dev_err(dai->dev, "Unsupported format: %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); return -EINVAL; } @@ -464,6 +482,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_NB_NF: break; default: + dev_err(dai->dev, "Unsupported clock polarity: %d\n", + fmt & SND_SOC_DAIFMT_INV_MASK); return -EINVAL; } @@ -482,6 +502,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) val = SUN4I_I2S_CTRL_MODE_SLAVE; break; default: + dev_err(dai->dev, "Unsupported slave setting: %d\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; } regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, @@ -504,6 +526,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) val = 0; break; default: + dev_err(dai->dev, "Unsupported slave setting: %d\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; } regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, @@ -897,6 +921,23 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), }; +static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { + .has_reset = true, + .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, + .sun4i_i2s_regmap = &sun4i_i2s_regmap_config, + .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7), + .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3), + .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), + .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), + .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), + .has_slave_select_bit = true, + .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), + .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31), + .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), + .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), + .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), +}; + static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .has_reset = true, .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, @@ -1121,6 +1162,10 @@ static const struct of_device_id sun4i_i2s_match[] = { .data = &sun6i_a31_i2s_quirks, }, { + .compatible = "allwinner,sun8i-a83t-i2s", + .data = &sun8i_a83t_i2s_quirks, + }, + { .compatible = "allwinner,sun8i-h3-i2s", .data = &sun8i_h3_i2s_quirks, }, diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 3dd183be08a4..7a15df924316 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -37,9 +37,11 @@ #define SUN8I_SYSCLK_CTL_SYSCLK_SRC 0 #define SUN8I_MOD_CLK_ENA 0x010 #define SUN8I_MOD_CLK_ENA_AIF1 15 +#define SUN8I_MOD_CLK_ENA_ADC 3 #define SUN8I_MOD_CLK_ENA_DAC 2 #define SUN8I_MOD_RST_CTL 0x014 #define SUN8I_MOD_RST_CTL_AIF1 15 +#define SUN8I_MOD_RST_CTL_ADC 3 #define SUN8I_MOD_RST_CTL_DAC 2 #define SUN8I_SYS_SR_CTRL 0x018 #define SUN8I_SYS_SR_CTRL_AIF1_FS 12 @@ -54,9 +56,25 @@ #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4) #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2 +#define SUN8I_AIF1_ADCDAT_CTRL 0x044 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA 15 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA 14 #define SUN8I_AIF1_DACDAT_CTRL 0x048 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 +#define SUN8I_AIF1_MXR_SRC 0x04c +#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L 15 +#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL 14 +#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL 13 +#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR 12 +#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R 11 +#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 +#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 +#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8 +#define SUN8I_ADC_DIG_CTRL 0x100 +#define SUN8I_ADC_DIG_CTRL_ENDA 15 +#define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 +#define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 #define SUN8I_DAC_DIG_CTRL 0x120 #define SUN8I_DAC_DIG_CTRL_ENDA 15 #define SUN8I_DAC_MXR_SRC 0x130 @@ -338,10 +356,30 @@ static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0), }; +static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = { + SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch", + SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0), + SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0), + SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch", + SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0), + SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch", + SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), +}; + static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { - /* Digital parts of the DACs */ + /* Digital parts of the DACs and ADC */ SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA, + 0, NULL, 0), /* Analog DAC AIF */ SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0, @@ -351,17 +389,31 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), - /* DAC Mixers */ + /* Analog ADC AIF */ + SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left ADC", "Capture", 0, + SUN8I_AIF1_ADCDAT_CTRL, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0), + SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right ADC", "Capture", 0, + SUN8I_AIF1_ADCDAT_CTRL, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0), + + /* DAC and ADC Mixers */ SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, sun8i_dac_mixer_controls), SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0, sun8i_dac_mixer_controls), + SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0, + sun8i_input_mixer_controls), + SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0, + sun8i_input_mixer_controls), /* Clocks */ SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA, SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA, SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA, + SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL, SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL, @@ -378,6 +430,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL, SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL, + SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0), + + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), + }; static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { @@ -387,11 +445,16 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "RST AIF1", NULL, "AIF1 PLL" }, { "MODCLK AFI1", NULL, "RST AIF1" }, { "DAC", NULL, "MODCLK AFI1" }, + { "ADC", NULL, "MODCLK AFI1" }, { "RST DAC", NULL, "SYSCLK" }, { "MODCLK DAC", NULL, "RST DAC" }, { "DAC", NULL, "MODCLK DAC" }, + { "RST ADC", NULL, "SYSCLK" }, + { "MODCLK ADC", NULL, "RST ADC" }, + { "ADC", NULL, "MODCLK ADC" }, + /* DAC Routes */ { "AIF1 Slot 0 Right", NULL, "DAC" }, { "AIF1 Slot 0 Left", NULL, "DAC" }, @@ -401,6 +464,12 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { "AIF1 Slot 0 Left"}, { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 Slot 0 Right"}, + + /* ADC routes */ + { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", + "AIF1 Slot 0 Left ADC" }, + { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", + "AIF1 Slot 0 Right ADC" }, }; static const struct snd_soc_dai_ops sun8i_codec_dai_ops = { @@ -418,6 +487,15 @@ static struct snd_soc_dai_driver sun8i_codec_dai = { .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + /* capture capabilities */ + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .sig_bits = 24, + }, /* pcm operations */ .ops = &sun8i_codec_dai_ops, }; diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig new file mode 100644 index 000000000000..02886a457eaf --- /dev/null +++ b/sound/soc/uniphier/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +config SND_SOC_UNIPHIER + tristate "ASoC support for UniPhier" + depends on (ARCH_UNIPHIER || COMPILE_TEST) + help + Say Y or M if you want to add support for the Socionext + UniPhier SoC audio interfaces. You will also need to select the + audio interfaces to support below. + If unsure select "N". + +config SND_SOC_UNIPHIER_EVEA_CODEC + tristate "UniPhier SoC internal audio codec" + depends on SND_SOC_UNIPHIER + select REGMAP_MMIO + help + This adds Codec driver for Socionext UniPhier LD11/20 SoC + internal DAC. This driver supports Line In / Out and HeadPhone. + Select Y if you use such device. + If unsure select "N". diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile new file mode 100644 index 000000000000..3be00d72f5e5 --- /dev/null +++ b/sound/soc/uniphier/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-soc-uniphier-evea-objs := evea.o +obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o diff --git a/sound/soc/uniphier/evea.c b/sound/soc/uniphier/evea.c new file mode 100644 index 000000000000..0cc9efff1d9a --- /dev/null +++ b/sound/soc/uniphier/evea.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Socionext UniPhier EVEA ADC/DAC codec driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * 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; version 2 + * of the License. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#define DRV_NAME "evea" +#define EVEA_RATES SNDRV_PCM_RATE_48000 +#define EVEA_FORMATS SNDRV_PCM_FMTBIT_S32_LE + +#define AADCPOW(n) (0x0078 + 0x04 * (n)) +#define AADCPOW_AADC_POWD BIT(0) +#define AHPOUTPOW 0x0098 +#define AHPOUTPOW_HP_ON BIT(4) +#define ALINEPOW 0x009c +#define ALINEPOW_LIN2_POWD BIT(3) +#define ALINEPOW_LIN1_POWD BIT(4) +#define ALO1OUTPOW 0x00a8 +#define ALO1OUTPOW_LO1_ON BIT(4) +#define ALO2OUTPOW 0x00ac +#define ALO2OUTPOW_ADAC2_MUTE BIT(0) +#define ALO2OUTPOW_LO2_ON BIT(4) +#define AANAPOW 0x00b8 +#define AANAPOW_A_POWD BIT(4) +#define ADACSEQ1(n) (0x0144 + 0x40 * (n)) +#define ADACSEQ1_MMUTE BIT(1) +#define ADACSEQ2(n) (0x0160 + 0x40 * (n)) +#define ADACSEQ2_ADACIN_FIX BIT(0) +#define ADAC1ODC 0x0200 +#define ADAC1ODC_HP_DIS_RES_MASK GENMASK(2, 1) +#define ADAC1ODC_HP_DIS_RES_OFF (0x0 << 1) +#define ADAC1ODC_HP_DIS_RES_ON (0x3 << 1) +#define ADAC1ODC_ADAC_RAMPCLT_MASK GENMASK(8, 7) +#define ADAC1ODC_ADAC_RAMPCLT_NORMAL (0x0 << 7) +#define ADAC1ODC_ADAC_RAMPCLT_REDUCE (0x1 << 7) + +struct evea_priv { + struct clk *clk, *clk_exiv; + struct reset_control *rst, *rst_exiv, *rst_adamv; + struct regmap *regmap; + + int switch_lin; + int switch_lo; + int switch_hp; +}; + +static const struct snd_soc_dapm_widget evea_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("LIN1_LP"), + SND_SOC_DAPM_INPUT("LIN1_RP"), + SND_SOC_DAPM_INPUT("LIN2_LP"), + SND_SOC_DAPM_INPUT("LIN2_RP"), + SND_SOC_DAPM_INPUT("LIN3_LP"), + SND_SOC_DAPM_INPUT("LIN3_RP"), + + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("HP1_L"), + SND_SOC_DAPM_OUTPUT("HP1_R"), + SND_SOC_DAPM_OUTPUT("LO2_L"), + SND_SOC_DAPM_OUTPUT("LO2_R"), +}; + +static const struct snd_soc_dapm_route evea_routes[] = { + { "ADC", NULL, "LIN1_LP" }, + { "ADC", NULL, "LIN1_RP" }, + { "ADC", NULL, "LIN2_LP" }, + { "ADC", NULL, "LIN2_RP" }, + { "ADC", NULL, "LIN3_LP" }, + { "ADC", NULL, "LIN3_RP" }, + + { "HP1_L", NULL, "DAC" }, + { "HP1_R", NULL, "DAC" }, + { "LO2_L", NULL, "DAC" }, + { "LO2_R", NULL, "DAC" }, +}; + +static void evea_set_power_state_on(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD, + AANAPOW_A_POWD); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK, + ADAC1ODC_ADAC_RAMPCLT_REDUCE); + + regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, 0); + regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, 0); + regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, 0); +} + +static void evea_set_power_state_off(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0); + regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, 0); + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0); +} + +static int evea_update_switch_lin(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_lin) { + regmap_update_bits(map, ALINEPOW, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD); + + regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, + AADCPOW_AADC_POWD); + regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, + AADCPOW_AADC_POWD); + } else { + regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, 0); + regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, 0); + + regmap_update_bits(map, ALINEPOW, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, 0); + } + + return 0; +} + +static int evea_update_switch_lo(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_lo) { + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, 0); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, 0); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, + ALO1OUTPOW_LO1_ON); + regmap_update_bits(map, ALO2OUTPOW, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON); + } else { + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0); + regmap_update_bits(map, ALO2OUTPOW, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, + 0); + } + + return 0; +} + +static int evea_update_switch_hp(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_hp) { + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, 0); + + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, + AHPOUTPOW_HP_ON); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_OFF); + } else { + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0); + } + + return 0; +} + +static void evea_update_switch_all(struct evea_priv *evea) +{ + evea_update_switch_lin(evea); + evea_update_switch_lo(evea); + evea_update_switch_hp(evea); +} + +static int evea_get_switch_lin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_lin; + + return 0; +} + +static int evea_set_switch_lin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_lin == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_lin = ucontrol->value.integer.value[0]; + + return evea_update_switch_lin(evea); +} + +static int evea_get_switch_lo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_lo; + + return 0; +} + +static int evea_set_switch_lo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_lo == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_lo = ucontrol->value.integer.value[0]; + + return evea_update_switch_lo(evea); +} + +static int evea_get_switch_hp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_hp; + + return 0; +} + +static int evea_set_switch_hp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_hp == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_hp = ucontrol->value.integer.value[0]; + + return evea_update_switch_hp(evea); +} + +static const struct snd_kcontrol_new eva_controls[] = { + SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0, + evea_get_switch_lin, evea_set_switch_lin), + SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0, + evea_get_switch_lo, evea_set_switch_lo), + SOC_SINGLE_BOOL_EXT("Headphone Playback Switch", 0, + evea_get_switch_hp, evea_set_switch_hp), +}; + +static int evea_codec_probe(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + evea->switch_lin = 1; + evea->switch_lo = 1; + evea->switch_hp = 1; + + evea_set_power_state_on(evea); + evea_update_switch_all(evea); + + return 0; +} + +static int evea_codec_suspend(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + evea_set_power_state_off(evea); + + reset_control_assert(evea->rst_adamv); + reset_control_assert(evea->rst_exiv); + reset_control_assert(evea->rst); + + clk_disable_unprepare(evea->clk_exiv); + clk_disable_unprepare(evea->clk); + + return 0; +} + +static int evea_codec_resume(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = clk_prepare_enable(evea->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(evea->clk_exiv); + if (ret) + goto err_out_clock; + + ret = reset_control_deassert(evea->rst); + if (ret) + goto err_out_clock_exiv; + + ret = reset_control_deassert(evea->rst_exiv); + if (ret) + goto err_out_reset; + + ret = reset_control_deassert(evea->rst_adamv); + if (ret) + goto err_out_reset_exiv; + + evea_set_power_state_on(evea); + evea_update_switch_all(evea); + + return 0; + +err_out_reset_exiv: + reset_control_assert(evea->rst_exiv); + +err_out_reset: + reset_control_assert(evea->rst); + +err_out_clock_exiv: + clk_disable_unprepare(evea->clk_exiv); + +err_out_clock: + clk_disable_unprepare(evea->clk); + + return ret; +} + +static struct snd_soc_codec_driver soc_codec_evea = { + .probe = evea_codec_probe, + .suspend = evea_codec_suspend, + .resume = evea_codec_resume, + + .component_driver = { + .dapm_widgets = evea_widgets, + .num_dapm_widgets = ARRAY_SIZE(evea_widgets), + .dapm_routes = evea_routes, + .num_dapm_routes = ARRAY_SIZE(evea_routes), + .controls = eva_controls, + .num_controls = ARRAY_SIZE(eva_controls), + }, +}; + +static struct snd_soc_dai_driver soc_dai_evea[] = { + { + .name = DRV_NAME "-line1", + .playback = { + .stream_name = "Line Out 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Line In 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, + { + .name = DRV_NAME "-hp1", + .playback = { + .stream_name = "Headphone 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, + { + .name = DRV_NAME "-lo2", + .playback = { + .stream_name = "Line Out 2", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, +}; + +static const struct regmap_config evea_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xffc, + .cache_type = REGCACHE_NONE, +}; + +static int evea_probe(struct platform_device *pdev) +{ + struct evea_priv *evea; + struct resource *res; + void __iomem *preg; + int ret; + + evea = devm_kzalloc(&pdev->dev, sizeof(struct evea_priv), GFP_KERNEL); + if (!evea) + return -ENOMEM; + + evea->clk = devm_clk_get(&pdev->dev, "evea"); + if (IS_ERR(evea->clk)) + return PTR_ERR(evea->clk); + + evea->clk_exiv = devm_clk_get(&pdev->dev, "exiv"); + if (IS_ERR(evea->clk_exiv)) + return PTR_ERR(evea->clk_exiv); + + evea->rst = devm_reset_control_get_shared(&pdev->dev, "evea"); + if (IS_ERR(evea->rst)) + return PTR_ERR(evea->rst); + + evea->rst_exiv = devm_reset_control_get_shared(&pdev->dev, "exiv"); + if (IS_ERR(evea->rst_exiv)) + return PTR_ERR(evea->rst_exiv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + preg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(preg)) + return PTR_ERR(preg); + + evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg, + &evea_regmap_config); + if (IS_ERR(evea->regmap)) + return PTR_ERR(evea->regmap); + + ret = clk_prepare_enable(evea->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(evea->clk_exiv); + if (ret) + goto err_out_clock; + + ret = reset_control_deassert(evea->rst); + if (ret) + goto err_out_clock_exiv; + + ret = reset_control_deassert(evea->rst_exiv); + if (ret) + goto err_out_reset; + + /* ADAMV will hangup if EXIV reset is asserted */ + evea->rst_adamv = devm_reset_control_get_shared(&pdev->dev, "adamv"); + if (IS_ERR(evea->rst_adamv)) { + ret = PTR_ERR(evea->rst_adamv); + goto err_out_reset_exiv; + } + + ret = reset_control_deassert(evea->rst_adamv); + if (ret) + goto err_out_reset_exiv; + + platform_set_drvdata(pdev, evea); + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_evea, + soc_dai_evea, ARRAY_SIZE(soc_dai_evea)); + if (ret) + goto err_out_reset_adamv; + + return 0; + +err_out_reset_adamv: + reset_control_assert(evea->rst_adamv); + +err_out_reset_exiv: + reset_control_assert(evea->rst_exiv); + +err_out_reset: + reset_control_assert(evea->rst); + +err_out_clock_exiv: + clk_disable_unprepare(evea->clk_exiv); + +err_out_clock: + clk_disable_unprepare(evea->clk); + + return ret; +} + +static int evea_remove(struct platform_device *pdev) +{ + struct evea_priv *evea = platform_get_drvdata(pdev); + + snd_soc_unregister_codec(&pdev->dev); + + reset_control_assert(evea->rst_adamv); + reset_control_assert(evea->rst_exiv); + reset_control_assert(evea->rst); + + clk_disable_unprepare(evea->clk_exiv); + clk_disable_unprepare(evea->clk); + + return 0; +} + +static const struct of_device_id evea_of_match[] = { + { .compatible = "socionext,uniphier-evea", }, + {} +}; +MODULE_DEVICE_TABLE(of, evea_of_match); + +static struct platform_driver evea_codec_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(evea_of_match), + }, + .probe = evea_probe, + .remove = evea_remove, +}; +module_platform_driver(evea_codec_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); +MODULE_DESCRIPTION("UniPhier EVEA codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 070a6880980e..c60a57797640 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -163,3 +163,7 @@ static struct platform_driver snd_soc_mop500_driver = { }; module_platform_driver(snd_soc_mop500_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC MOP500 board driver"); +MODULE_AUTHOR("Ola Lilja"); diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index f12c01dddc8d..d35ba7700f46 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -165,3 +165,8 @@ int ux500_pcm_unregister_platform(struct platform_device *pdev) return 0; } EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); + +MODULE_AUTHOR("Ola Lilja"); +MODULE_AUTHOR("Roger Nilsson"); +MODULE_DESCRIPTION("ASoC UX500 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 0537c6322990..2b4ceda36291 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -204,6 +204,10 @@ static int snd_usb_copy_string_desc(struct mixer_build *state, int index, char *buf, int maxlen) { int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + + if (len < 0) + return 0; + buf[len] = 0; return len; } @@ -1476,9 +1480,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, return -EINVAL; } csize = hdr->bControlSize; - if (csize <= 1) { + if (!csize) { usb_audio_dbg(state->chip, - "unit %u: invalid bControlSize <= 1\n", + "unit %u: invalid bControlSize == 0\n", unitid); return -EINVAL; } @@ -2169,19 +2173,25 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, kctl->private_value = (unsigned long)namelist; kctl->private_free = usb_mixer_selector_elem_free; - nameid = uac_selector_unit_iSelector(desc); + /* check the static mapping table at first */ len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); - if (len) - ; - else if (nameid) - snd_usb_copy_string_desc(state, nameid, kctl->id.name, - sizeof(kctl->id.name)); - else { - len = get_term_name(state, &state->oterm, + if (!len) { + /* no mapping ? */ + /* if iSelector is given, use it */ + nameid = uac_selector_unit_iSelector(desc); + if (nameid) + len = snd_usb_copy_string_desc(state, nameid, + kctl->id.name, + sizeof(kctl->id.name)); + /* ... or pick up the terminal name at next */ + if (!len) + len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 0); + /* ... or use the fixed string "USB" as the last resort */ if (!len) strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); + /* and add the proper suffix */ if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) append_ctl_name(kctl, " Clock Source"); else if ((state->oterm.type & 0xff00) == 0x0100) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 77eecaa4db1f..a66ef5777887 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1166,10 +1166,11 @@ static bool is_marantz_denon_dac(unsigned int id) /* TEAC UD-501/UD-503/NT-503 USB DACs need a vendor cmd to switch * between PCM/DOP and native DSD mode */ -static bool is_teac_50X_dac(unsigned int id) +static bool is_teac_dsd_dac(unsigned int id) { switch (id) { case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-503/NT-503 */ + case USB_ID(0x0644, 0x8044): /* Esoteric D-05X */ return true; } return false; @@ -1202,7 +1203,7 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, break; } mdelay(20); - } else if (is_teac_50X_dac(subs->stream->chip->usb_id)) { + } else if (is_teac_dsd_dac(subs->stream->chip->usb_id)) { /* Vendor mode switch cmd is required. */ switch (fmt->altsetting) { case 3: /* DSD mode (DSD_U32) requested */ @@ -1392,7 +1393,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, } /* TEAC devices with USB DAC functionality */ - if (is_teac_50X_dac(chip->usb_id)) { + if (is_teac_dsd_dac(chip->usb_id)) { if (fp->altsetting == 3) return SNDRV_PCM_FMTBIT_DSD_U32_BE; } |