linux-stable/sound/soc/codecs/hda.c

394 lines
11 KiB
C
Raw Normal View History

ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_i915.h>
#include <sound/hda_codec.h>
#include "hda.h"
static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count,
struct snd_soc_dai_driver **drivers)
{
struct device *dev = &codec->core.dev;
struct snd_soc_dai_driver *drvs;
struct hda_pcm *pcm;
int i;
drvs = devm_kcalloc(dev, pcm_count, sizeof(*drvs), GFP_KERNEL);
if (!drvs)
return -ENOMEM;
pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
struct snd_soc_pcm_stream *stream;
int dir;
dev_info(dev, "creating for %s %d\n", pcm->name, i);
drvs[i].id = i;
drvs[i].name = pcm->name;
drvs[i].ops = &snd_soc_hda_codec_dai_ops;
dir = SNDRV_PCM_STREAM_PLAYBACK;
stream = &drvs[i].playback;
if (!pcm->stream[dir].substreams) {
dev_info(dev, "skipping playback dai for %s\n", pcm->name);
goto capture_dais;
}
stream->stream_name =
devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
snd_pcm_direction_name(dir));
if (!stream->stream_name)
return -ENOMEM;
stream->channels_min = pcm->stream[dir].channels_min;
stream->channels_max = pcm->stream[dir].channels_max;
stream->rates = pcm->stream[dir].rates;
stream->formats = pcm->stream[dir].formats;
stream->sig_bits = pcm->stream[dir].maxbps;
capture_dais:
dir = SNDRV_PCM_STREAM_CAPTURE;
stream = &drvs[i].capture;
if (!pcm->stream[dir].substreams) {
dev_info(dev, "skipping capture dai for %s\n", pcm->name);
continue;
}
stream->stream_name =
devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
snd_pcm_direction_name(dir));
if (!stream->stream_name)
return -ENOMEM;
stream->channels_min = pcm->stream[dir].channels_min;
stream->channels_max = pcm->stream[dir].channels_max;
stream->rates = pcm->stream[dir].rates;
stream->formats = pcm->stream[dir].formats;
stream->sig_bits = pcm->stream[dir].maxbps;
}
*drivers = drvs;
return 0;
}
static int hda_codec_register_dais(struct hda_codec *codec, struct snd_soc_component *component)
{
struct snd_soc_dai_driver *drvs = NULL;
struct snd_soc_dapm_context *dapm;
struct hda_pcm *pcm;
int ret, pcm_count = 0;
if (list_empty(&codec->pcm_list_head))
return -EINVAL;
list_for_each_entry(pcm, &codec->pcm_list_head, list)
pcm_count++;
ret = hda_codec_create_dais(codec, pcm_count, &drvs);
if (ret < 0)
return ret;
dapm = snd_soc_component_get_dapm(component);
list_for_each_entry(pcm, &codec->pcm_list_head, list) {
struct snd_soc_dai *dai;
dai = snd_soc_register_dai(component, drvs, false);
if (!dai) {
dev_err(component->dev, "register dai for %s failed\n", pcm->name);
return -EINVAL;
}
ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
if (ret < 0) {
dev_err(component->dev, "create widgets failed: %d\n", ret);
snd_soc_unregister_dai(dai);
return ret;
}
snd_soc_dai_init_dma_data(dai, &pcm->stream[0], &pcm->stream[1]);
drvs++;
}
return 0;
}
static void hda_codec_unregister_dais(struct hda_codec *codec,
struct snd_soc_component *component)
{
struct snd_soc_dai *dai, *save;
struct hda_pcm *pcm;
for_each_component_dais_safe(component, dai, save) {
int stream;
ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
list_for_each_entry(pcm, &codec->pcm_list_head, list) {
if (strcmp(dai->driver->name, pcm->name))
continue;
for_each_pcm_streams(stream)
snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream));
ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
snd_soc_unregister_dai(dai);
break;
}
}
}
int hda_codec_probe_complete(struct hda_codec *codec)
{
struct hdac_device *hdev = &codec->core;
struct hdac_bus *bus = hdev->bus;
int ret;
ret = snd_hda_codec_build_controls(codec);
if (ret < 0) {
dev_err(&hdev->dev, "unable to create controls %d\n", ret);
goto out;
}
/* Bus suspended codecs as it does not manage their pm */
pm_runtime_set_active(&hdev->dev);
/* rpm was forbidden in snd_hda_codec_device_new() */
snd_hda_codec_set_power_save(codec, 2000);
snd_hda_codec_register(codec);
out:
/* Complement pm_runtime_get_sync(bus) in probe */
pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
return ret;
}
EXPORT_SYMBOL_GPL(hda_codec_probe_complete);
/* Expects codec with usage_count=1 and status=suspended */
static int hda_codec_probe(struct snd_soc_component *component)
{
struct hda_codec *codec = dev_to_hda_codec(component->dev);
struct hdac_device *hdev = &codec->core;
struct hdac_bus *bus = hdev->bus;
struct hdac_ext_link *hlink;
hda_codec_patch_t patch;
int ret;
#ifdef CONFIG_PM
WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
!pm_runtime_status_suspended(&hdev->dev));
#endif
hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr);
ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
if (!hlink) {
dev_err(&hdev->dev, "hdac link not found\n");
return -EIO;
}
pm_runtime_get_sync(bus->dev);
if (hda_codec_is_display(codec))
snd_hdac_display_power(bus, hdev->addr, true);
snd_hdac_ext_bus_link_get(bus, hlink);
ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec,
false);
if (ret < 0) {
dev_err(&hdev->dev, "create hda codec failed: %d\n", ret);
goto device_new_err;
}
ret = snd_hda_codec_set_name(codec, codec->preset->name);
if (ret < 0) {
dev_err(&hdev->dev, "name failed %s\n", codec->preset->name);
goto err;
}
ret = snd_hdac_regmap_init(&codec->core);
if (ret < 0) {
dev_err(&hdev->dev, "regmap init failed\n");
goto err;
}
patch = (hda_codec_patch_t)codec->preset->driver_data;
if (!patch) {
dev_err(&hdev->dev, "no patch specified\n");
ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
ret = -EINVAL;
goto err;
}
ret = patch(codec);
if (ret < 0) {
dev_err(&hdev->dev, "patch failed %d\n", ret);
goto err;
}
ret = snd_hda_codec_parse_pcms(codec);
if (ret < 0) {
dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
goto parse_pcms_err;
}
ret = hda_codec_register_dais(codec, component);
if (ret < 0) {
dev_err(&hdev->dev, "update dais failed: %d\n", ret);
goto parse_pcms_err;
}
if (!hda_codec_is_display(codec)) {
ret = hda_codec_probe_complete(codec);
if (ret < 0)
goto complete_err;
}
codec->core.lazy_cache = true;
return 0;
complete_err:
hda_codec_unregister_dais(codec, component);
parse_pcms_err:
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
err:
snd_hda_codec_cleanup_for_unbind(codec);
device_new_err:
if (hda_codec_is_display(codec))
snd_hdac_display_power(bus, hdev->addr, false);
snd_hdac_ext_bus_link_put(bus, hlink);
pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
return ret;
}
/* Leaves codec with usage_count=1 and status=suspended */
static void hda_codec_remove(struct snd_soc_component *component)
{
struct hda_codec *codec = dev_to_hda_codec(component->dev);
struct hdac_device *hdev = &codec->core;
struct hdac_bus *bus = hdev->bus;
struct hdac_ext_link *hlink;
bool was_registered = codec->core.registered;
ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
/* Don't allow any more runtime suspends */
pm_runtime_forbid(&hdev->dev);
hda_codec_unregister_dais(codec, component);
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
snd_hda_codec_cleanup_for_unbind(codec);
pm_runtime_put_noidle(&hdev->dev);
/* snd_hdac_device_exit() is only called on bus remove */
pm_runtime_set_suspended(&hdev->dev);
if (hda_codec_is_display(codec))
snd_hdac_display_power(bus, hdev->addr, false);
hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr);
ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
if (hlink)
snd_hdac_ext_bus_link_put(bus, hlink);
/*
* HDMI card's hda_codec_probe_complete() (see late_probe()) may
* not be called due to early error, leaving bus uc unbalanced
*/
if (!was_registered) {
pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
}
#ifdef CONFIG_PM
WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
!pm_runtime_status_suspended(&hdev->dev));
#endif
}
static const struct snd_soc_dapm_route 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 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 struct snd_soc_dai_driver card_binder_dai = {
.id = -1,
.name = "codec-probing-DAI",
};
static int hda_hdev_attach(struct hdac_device *hdev)
{
struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
struct snd_soc_component_driver *comp_drv;
comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL);
if (!comp_drv)
return -ENOMEM;
/*
* It's save to rely on dev_name() rather than a copy as component
* driver's lifetime is directly tied to hda codec one
*/
comp_drv->name = dev_name(&hdev->dev);
comp_drv->probe = hda_codec_probe;
comp_drv->remove = hda_codec_remove;
comp_drv->idle_bias_on = false;
if (!hda_codec_is_display(codec)) {
comp_drv->dapm_widgets = hda_dapm_widgets;
comp_drv->num_dapm_widgets = ARRAY_SIZE(hda_dapm_widgets);
comp_drv->dapm_routes = hda_dapm_routes;
comp_drv->num_dapm_routes = ARRAY_SIZE(hda_dapm_routes);
}
return snd_soc_register_component(&hdev->dev, comp_drv, &card_binder_dai, 1);
}
static int hda_hdev_detach(struct hdac_device *hdev)
{
struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
if (codec->core.registered)
ASoC: codecs: Add HD-Audio codec driver Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing hda_codec.c handlers to prevent code duplication within the newly added code. Number of DAIs created is dependent on capabilities exposed by the codec itself. Because of this, single solution can be applied to support every single HD-Audio codec type. At the same time, through the ASoC topology, platform drivers may limit the number of endpoints available to the userspace as codec driver exposes BE DAIs only. Both hda_codec_probe() and hda_codec_remove() declare their expectations on device's usage_count and suspended-status. This is to catch any unexpected behavior as PM-related code for HD-Audio has been changing quite a bit throughout the years. In order for codec DAI list to reflect its actual PCM capabilities, PCMs need to be built and that can only happen once codec device is constructed. To do that, a valid component->card->snd_card pointer is needed. Said pointer will be provided by the framework once all card components are accounted for and their probing can begin. Usage of "binder" BE DAI solves the problem - codec can be listed as one of HD-Audio card components without declaring any actual BE DAIs statically. Relation with hdac_hda: Addition of parallel solution is motivated by behavioral differences between hdac_hda.c and its legacy equivalent found in sound/pci/hda e.g.: lack of dynamic, based on codec capabilities, resource allocation and high cost of removing such differences on actively used targets. Major goal of codec driver presented here is to follow HD-Audio legacy behavior in 1:1 fashion by becoming a wrapper. Doing so increases code coverage of the legacy code and reduces the maintenance cost for both solutions. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2022-05-11 16:23:51 +00:00
cancel_delayed_work_sync(&codec->jackpoll_work);
snd_soc_unregister_component(&hdev->dev);
return 0;
}
const struct hdac_ext_bus_ops soc_hda_ext_bus_ops = {
.hdev_attach = hda_hdev_attach,
.hdev_detach = hda_hdev_detach,
};
EXPORT_SYMBOL_GPL(soc_hda_ext_bus_ops);
MODULE_DESCRIPTION("HD-Audio codec driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");