Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Rakesh A Ughreja | 2149 | 73.39% | 5 | 10.87% |
Kai Vehmanen | 234 | 7.99% | 6 | 13.04% |
Rander Wang | 156 | 5.33% | 1 | 2.17% |
Bard Liao | 125 | 4.27% | 3 | 6.52% |
Pierre-Louis Bossart | 59 | 2.02% | 4 | 8.70% |
Jeeja KP | 54 | 1.84% | 5 | 10.87% |
Peter Ujfalusi | 34 | 1.16% | 1 | 2.17% |
Takashi Iwai | 33 | 1.13% | 6 | 13.04% |
Sathyanarayana Nujella | 20 | 0.68% | 1 | 2.17% |
tangmeng | 18 | 0.61% | 1 | 2.17% |
Charles Keepax | 11 | 0.38% | 1 | 2.17% |
Cezary Rojewski | 7 | 0.24% | 2 | 4.35% |
Jaroslav Kysela | 6 | 0.20% | 1 | 2.17% |
Vinod Koul | 6 | 0.20% | 2 | 4.35% |
Keyon Jie | 5 | 0.17% | 1 | 2.17% |
Kuninori Morimoto | 4 | 0.14% | 2 | 4.35% |
Pardha Saradhi K | 3 | 0.10% | 1 | 2.17% |
Dharageswari R | 2 | 0.07% | 1 | 2.17% |
Stephen Hemminger | 1 | 0.03% | 1 | 2.17% |
Julia Lawall | 1 | 0.03% | 1 | 2.17% |
Total | 2928 | 46 |
// SPDX-License-Identifier: GPL-2.0 // Copyright(c) 2015-18 Intel Corporation. /* * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers * with ASoC platform drivers. These APIs are called by the legacy HDA * codec drivers using hdac_ext_bus_ops ops. */ #include <linux/firmware.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/hdaudio_ext.h> #include <sound/hda_i915.h> #include <sound/hda_codec.h> #include <sound/hda_register.h> #include "hdac_hda.h" #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_U8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_U16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_U24_LE | \ SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_U32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) #define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ SNDRV_PCM_RATE_192000) #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *loadable_patch[HDA_MAX_CODECS]; module_param_array_named(patch, loadable_patch, charp, NULL, 0444); MODULE_PARM_DESC(patch, "Patch file array for Intel HD audio interface. The array index is the codec address."); #endif static int hdac_hda_dai_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static void hdac_hda_dai_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream, int direction); static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct snd_soc_dai *dai); static const struct snd_soc_dai_ops hdac_hda_dai_ops = { .startup = hdac_hda_dai_open, .shutdown = hdac_hda_dai_close, .prepare = hdac_hda_dai_prepare, .hw_params = hdac_hda_dai_hw_params, .hw_free = hdac_hda_dai_hw_free, .set_stream = hdac_hda_dai_set_stream, }; static struct snd_soc_dai_driver hdac_hda_dais[] = { { .id = HDAC_ANALOG_DAI_ID, .name = "Analog Codec DAI", .ops = &hdac_hda_dai_ops, .playback = { .stream_name = "Analog Codec Playback", .channels_min = 1, .channels_max = 16, .rates = SNDRV_PCM_RATE_8000_192000, .formats = STUB_FORMATS, .sig_bits = 24, }, .capture = { .stream_name = "Analog Codec Capture", .channels_min = 1, .channels_max = 16, .rates = SNDRV_PCM_RATE_8000_192000, .formats = STUB_FORMATS, .sig_bits = 24, }, }, { .id = HDAC_DIGITAL_DAI_ID, .name = "Digital Codec DAI", .ops = &hdac_hda_dai_ops, .playback = { .stream_name = "Digital Codec Playback", .channels_min = 1, .channels_max = 16, .rates = SNDRV_PCM_RATE_8000_192000, .formats = STUB_FORMATS, .sig_bits = 24, }, .capture = { .stream_name = "Digital Codec Capture", .channels_min = 1, .channels_max = 16, .rates = SNDRV_PCM_RATE_8000_192000, .formats = STUB_FORMATS, .sig_bits = 24, }, }, { .id = HDAC_ALT_ANALOG_DAI_ID, .name = "Alt Analog Codec DAI", .ops = &hdac_hda_dai_ops, .playback = { .stream_name = "Alt Analog Codec Playback", .channels_min = 1, .channels_max = 16, .rates = SNDRV_PCM_RATE_8000_192000, .formats = STUB_FORMATS, .sig_bits = 24, }, .capture = { .stream_name = "Alt Analog Codec Capture", .channels_min = 1, .channels_max = 16, .rates = SNDRV_PCM_RATE_8000_192000, .formats = STUB_FORMATS, .sig_bits = 24, }, }, }; static struct snd_soc_dai_driver hdac_hda_hdmi_dais[] = { { .id = HDAC_HDMI_0_DAI_ID, .name = "intel-hdmi-hifi1", .ops = &hdac_hda_dai_ops, .playback = { .stream_name = "hifi1", .channels_min = 1, .channels_max = 32, .rates = STUB_HDMI_RATES, .formats = STUB_FORMATS, .sig_bits = 24, }, }, { .id = HDAC_HDMI_1_DAI_ID, .name = "intel-hdmi-hifi2", .ops = &hdac_hda_dai_ops, .playback = { .stream_name = "hifi2", .channels_min = 1, .channels_max = 32, .rates = STUB_HDMI_RATES, .formats = STUB_FORMATS, .sig_bits = 24, }, }, { .id = HDAC_HDMI_2_DAI_ID, .name = "intel-hdmi-hifi3", .ops = &hdac_hda_dai_ops, .playback = { .stream_name = "hifi3", .channels_min = 1, .channels_max = 32, .rates = STUB_HDMI_RATES, .formats = STUB_FORMATS, .sig_bits = 24, }, }, { .id = HDAC_HDMI_3_DAI_ID, .name = "intel-hdmi-hifi4", .ops = &hdac_hda_dai_ops, .playback = { .stream_name = "hifi4", .channels_min = 1, .channels_max = 32, .rates = STUB_HDMI_RATES, .formats = STUB_FORMATS, .sig_bits = 24, }, }, }; static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream, int direction) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; struct hdac_hda_pcm *pcm; struct hdac_stream *hstream; if (!stream) return -EINVAL; hda_pvt = snd_soc_component_get_drvdata(component); pcm = &hda_pvt->pcm[dai->id]; hstream = (struct hdac_stream *)stream; pcm->stream_tag[direction] = hstream->stream_tag; return 0; } static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; unsigned int format_val; unsigned int maxbps; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) maxbps = dai->driver->playback.sig_bits; else maxbps = dai->driver->capture.sig_bits; hda_pvt = snd_soc_component_get_drvdata(component); format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), params_format(params), maxbps, 0); if (!format_val) { dev_err(dai->dev, "invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n", params_rate(params), params_channels(params), params_format(params), maxbps); return -EINVAL; } hda_pvt->pcm[dai->id].format_val[substream->stream] = format_val; return 0; } static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; struct hda_pcm_stream *hda_stream; struct hda_pcm *pcm; hda_pvt = snd_soc_component_get_drvdata(component); pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); if (!pcm) return -EINVAL; hda_stream = &pcm->stream[substream->stream]; snd_hda_codec_cleanup(hda_pvt->codec, hda_stream, substream); return 0; } static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct hda_pcm_stream *hda_stream; struct hdac_hda_priv *hda_pvt; struct hdac_device *hdev; unsigned int format_val; struct hda_pcm *pcm; unsigned int stream; int ret = 0; hda_pvt = snd_soc_component_get_drvdata(component); hdev = &hda_pvt->codec->core; pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); if (!pcm) return -EINVAL; hda_stream = &pcm->stream[substream->stream]; stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream]; format_val = hda_pvt->pcm[dai->id].format_val[substream->stream]; ret = snd_hda_codec_prepare(hda_pvt->codec, hda_stream, stream, format_val, substream); if (ret < 0) dev_err(&hdev->dev, "codec prepare failed %d\n", ret); return ret; } static int hdac_hda_dai_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; struct hda_pcm_stream *hda_stream; struct hda_pcm *pcm; hda_pvt = snd_soc_component_get_drvdata(component); pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); if (!pcm) return -EINVAL; snd_hda_codec_pcm_get(pcm); hda_stream = &pcm->stream[substream->stream]; return hda_stream->ops.open(hda_stream, hda_pvt->codec, substream); } static void hdac_hda_dai_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; struct hda_pcm_stream *hda_stream; struct hda_pcm *pcm; hda_pvt = snd_soc_component_get_drvdata(component); pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); if (!pcm) return; hda_stream = &pcm->stream[substream->stream]; hda_stream->ops.close(hda_stream, hda_pvt->codec, substream); snd_hda_codec_pcm_put(pcm); } static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct snd_soc_dai *dai) { struct hda_codec *hcodec = hda_pvt->codec; struct hda_pcm *cpcm; const char *pcm_name; /* * map DAI ID to the closest matching PCM name, using the naming * scheme used by hda-codec snd_hda_gen_build_pcms() and for * HDMI in hda_codec patch_hdmi.c) */ switch (dai->id) { case HDAC_ANALOG_DAI_ID: pcm_name = "Analog"; break; case HDAC_DIGITAL_DAI_ID: pcm_name = "Digital"; break; case HDAC_ALT_ANALOG_DAI_ID: pcm_name = "Alt Analog"; break; case HDAC_HDMI_0_DAI_ID: pcm_name = "HDMI 0"; break; case HDAC_HDMI_1_DAI_ID: pcm_name = "HDMI 1"; break; case HDAC_HDMI_2_DAI_ID: pcm_name = "HDMI 2"; break; case HDAC_HDMI_3_DAI_ID: pcm_name = "HDMI 3"; break; default: dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id); return NULL; } list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { if (strstr(cpcm->name, pcm_name)) { if (strcmp(pcm_name, "Analog") == 0) { if (strstr(cpcm->name, "Alt Analog")) continue; } return cpcm; } } dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name); return NULL; } static bool is_hdmi_codec(struct hda_codec *hcodec) { struct hda_pcm *cpcm; list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI) return true; } return false; } static int hdac_hda_codec_probe(struct snd_soc_component *component) { struct hdac_hda_priv *hda_pvt = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct hdac_device *hdev = &hda_pvt->codec->core; struct hda_codec *hcodec = hda_pvt->codec; struct hdac_ext_link *hlink; hda_codec_patch_t patch; int ret; hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } snd_hdac_ext_bus_link_get(hdev->bus, hlink); /* * Ensure any HDA display is powered at codec probe. * After snd_hda_codec_device_new(), display power is * managed by runtime PM. */ if (hda_pvt->need_display_power) snd_hdac_display_power(hdev->bus, HDA_CODEC_IDX_CONTROLLER, true); ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card, hdev->addr, hcodec, true); if (ret < 0) { dev_err(&hdev->dev, "failed to create hda codec %d\n", ret); goto error_no_pm; } #ifdef CONFIG_SND_HDA_PATCH_LOADER if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { const struct firmware *fw; dev_info(&hdev->dev, "Applying patch firmware '%s'\n", loadable_patch[hda_pvt->dev_index]); ret = request_firmware(&fw, loadable_patch[hda_pvt->dev_index], &hdev->dev); if (ret < 0) goto error_no_pm; if (fw) { ret = snd_hda_load_patch(hcodec->bus, fw->size, fw->data); if (ret < 0) { dev_err(&hdev->dev, "failed to load hda patch %d\n", ret); goto error_no_pm; } release_firmware(fw); } } #endif /* * Overwrite type to HDA_DEV_ASOC since it is a ASoC driver * hda_codec.c will check this flag to determine if unregister * device is needed. */ hdev->type = HDA_DEV_ASOC; /* * snd_hda_codec_device_new decrements the usage count so call get pm * else the device will be powered off */ pm_runtime_get_noresume(&hdev->dev); hcodec->bus->card = dapm->card->snd_card; ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name); if (ret < 0) { dev_err(&hdev->dev, "name failed %s\n", hcodec->preset->name); goto error_pm; } ret = snd_hdac_regmap_init(&hcodec->core); if (ret < 0) { dev_err(&hdev->dev, "regmap init failed\n"); goto error_pm; } patch = (hda_codec_patch_t)hcodec->preset->driver_data; if (patch) { ret = patch(hcodec); if (ret < 0) { dev_err(&hdev->dev, "patch failed %d\n", ret); goto error_regmap; } } else { dev_dbg(&hdev->dev, "no patch file found\n"); } ret = snd_hda_codec_parse_pcms(hcodec); if (ret < 0) { dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); goto error_patch; } /* HDMI controls need to be created in machine drivers */ if (!is_hdmi_codec(hcodec)) { ret = snd_hda_codec_build_controls(hcodec); if (ret < 0) { dev_err(&hdev->dev, "unable to create controls %d\n", ret); goto error_patch; } } hcodec->core.lazy_cache = true; if (hda_pvt->need_display_power) snd_hdac_display_power(hdev->bus, HDA_CODEC_IDX_CONTROLLER, false); /* match for forbid call in snd_hda_codec_device_new() */ pm_runtime_allow(&hdev->dev); /* * 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 is also called during codec registeration */ pm_runtime_put(&hdev->dev); pm_runtime_suspend(&hdev->dev); return 0; error_patch: if (hcodec->patch_ops.free) hcodec->patch_ops.free(hcodec); error_regmap: snd_hdac_regmap_exit(hdev); error_pm: pm_runtime_put(&hdev->dev); error_no_pm: snd_hdac_ext_bus_link_put(hdev->bus, hlink); return ret; } static void hdac_hda_codec_remove(struct snd_soc_component *component) { struct hdac_hda_priv *hda_pvt = snd_soc_component_get_drvdata(component); struct hdac_device *hdev = &hda_pvt->codec->core; struct hda_codec *codec = hda_pvt->codec; struct hdac_ext_link *hlink = NULL; hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { dev_err(&hdev->dev, "hdac link not found\n"); return; } pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); } static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { {"AIF1TX", NULL, "Codec Input Pin1"}, {"AIF2TX", NULL, "Codec Input Pin2"}, {"AIF3TX", NULL, "Codec Input Pin3"}, {"Codec Output Pin1", NULL, "AIF1RX"}, {"Codec Output Pin2", NULL, "AIF2RX"}, {"Codec Output Pin3", NULL, "AIF3RX"}, }; static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = { /* Audio Interface */ SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0), /* Input Pins */ SND_SOC_DAPM_INPUT("Codec Input Pin1"), SND_SOC_DAPM_INPUT("Codec Input Pin2"), SND_SOC_DAPM_INPUT("Codec Input Pin3"), /* Output Pins */ SND_SOC_DAPM_OUTPUT("Codec Output Pin1"), SND_SOC_DAPM_OUTPUT("Codec Output Pin2"), SND_SOC_DAPM_OUTPUT("Codec Output Pin3"), }; static const struct snd_soc_component_driver hdac_hda_codec = { .probe = hdac_hda_codec_probe, .remove = hdac_hda_codec_remove, .dapm_widgets = hdac_hda_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets), .dapm_routes = hdac_hda_dapm_routes, .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes), .idle_bias_on = false, .endianness = 1, }; static const struct snd_soc_component_driver hdac_hda_hdmi_codec = { .probe = hdac_hda_codec_probe, .remove = hdac_hda_codec_remove, .idle_bias_on = false, .endianness = 1, }; static int hdac_hda_dev_probe(struct hdac_device *hdev) { struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev); struct hdac_ext_link *hlink; int ret; /* hold the ref while we probe */ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } snd_hdac_ext_bus_link_get(hdev->bus, hlink); /* ASoC specific initialization */ if (hda_pvt->need_display_power) ret = devm_snd_soc_register_component(&hdev->dev, &hdac_hda_hdmi_codec, hdac_hda_hdmi_dais, ARRAY_SIZE(hdac_hda_hdmi_dais)); else ret = devm_snd_soc_register_component(&hdev->dev, &hdac_hda_codec, hdac_hda_dais, ARRAY_SIZE(hdac_hda_dais)); if (ret < 0) { dev_err(&hdev->dev, "failed to register HDA codec %d\n", ret); return ret; } snd_hdac_ext_bus_link_put(hdev->bus, hlink); return ret; } static int hdac_hda_dev_remove(struct hdac_device *hdev) { /* * Resources are freed in hdac_hda_codec_remove(). This * function is kept to keep hda_codec_driver_remove() happy. */ return 0; } static struct hdac_ext_bus_ops hdac_ops = { .hdev_attach = hdac_hda_dev_probe, .hdev_detach = hdac_hda_dev_remove, }; struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void) { return &hdac_ops; } EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers"); MODULE_AUTHOR("Rakesh Ughreja<rakesh.a.ughreja@intel.com>");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1