struct hda_codec;
struct hda_pcm;
struct hda_pcm_stream;
+struct hda_codec_ops;
/*
* codec bus
#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
#define HDA_CODEC_ID_GENERIC 0x00000201
+#define HDA_CODEC_ID_REV_MODEL(_vid, _rev, _name, _model) \
+ { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+ .api_version = HDA_DEV_LEGACY, .driver_data = (_model) }
+#define HDA_CODEC_ID_MODEL(_vid, _name, _model) \
+ HDA_CODEC_ID_REV_MODEL(_vid, 0, _name, _model)
+#define HDA_CODEC_ID_REV(_vid, _rev, _name) \
+ HDA_CODEC_ID_REV_MODEL(_vid, _rev, _name, 0)
+#define HDA_CODEC_ID(_vid, _name) \
+ HDA_CODEC_ID_REV(_vid, 0, _name)
+
+/* old macros for patch_ops -- to be deprecated */
#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
{ .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
.api_version = HDA_DEV_LEGACY, \
struct hda_codec_driver {
struct hdac_driver core;
const struct hda_device_id *id;
+ const struct hda_codec_ops *ops;
};
+#define hda_codec_to_driver(codec) \
+ container_of((codec)->core.dev.driver, struct hda_codec_driver, core.driver)
+
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
struct module *owner);
#define hda_codec_driver_register(drv) \
/* ops set by the preset patch */
struct hda_codec_ops {
+ int (*probe)(struct hda_codec *codec, const struct hda_device_id *id);
+ void (*remove)(struct hda_codec *codec);
int (*build_controls)(struct hda_codec *codec);
int (*build_pcms)(struct hda_codec *codec);
int (*init)(struct hda_codec *codec);
/* set by patch */
struct hda_codec_ops patch_ops;
- /* PCM to create, set by patch_ops.build_pcms callback */
+ /* PCM to create, set by hda_codec_ops.build_pcms callback */
struct list_head pcm_list_head;
refcount_t pcm_ref;
wait_queue_head_t remove_sleep;
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
- if (codec->patch_ops.check_power_status)
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ if (driver->ops && driver->ops->check_power_status)
+ return driver->ops->check_power_status(codec, nid);
+ else if (codec->patch_ops.check_power_status)
return codec->patch_ops.check_power_status(codec, nid);
return 0;
}
static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
/* ignore unsol events during shutdown */
if (codec->card->shutdown || codec->bus->shutdown)
if (codec->core.dev.power.power_state.event != PM_EVENT_ON)
return;
- if (codec->patch_ops.unsol_event)
+ if (driver->ops && driver->ops->unsol_event)
+ driver->ops->unsol_event(codec, ev);
+ else if (codec->patch_ops.unsol_event)
codec->patch_ops.unsol_event(codec, ev);
}
{
struct hda_codec *codec = dev_to_hda_codec(dev);
struct module *owner = dev->driver->owner;
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
hda_codec_patch_t patch;
int err;
goto error;
}
- patch = (hda_codec_patch_t)codec->preset->driver_data;
- if (patch) {
- err = patch(codec);
+ if (driver->ops && driver->ops->probe) {
+ err = driver->ops->probe(codec, codec->preset);
if (err < 0)
goto error_module_put;
+ } else {
+ patch = (hda_codec_patch_t)codec->preset->driver_data;
+ if (patch) {
+ err = patch(codec);
+ if (err < 0)
+ goto error_module_put;
+ }
}
err = snd_hda_codec_build_pcms(codec);
return 0;
error_module:
- if (codec->patch_ops.free)
+ if (driver->ops && driver->ops->remove)
+ driver->ops->remove(codec);
+ else if (codec->patch_ops.free)
codec->patch_ops.free(codec);
error_module_put:
module_put(owner);
static int hda_codec_driver_remove(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
if (codec->bus->core.ext_ops) {
if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach))
wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
snd_power_sync_ref(codec->bus->card);
- if (codec->patch_ops.free)
+ if (driver->ops && driver->ops->remove)
+ driver->ops->remove(codec);
+ else if (codec->patch_ops.free)
codec->patch_ops.free(codec);
snd_hda_codec_cleanup_for_unbind(codec);
codec->preset = NULL;
u32 stream_tag,
int channel_id, int format)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hda_codec *c;
struct hda_cvt_setup *p;
int type;
if (!p)
return;
- if (codec->patch_ops.stream_pm)
+ if (driver->ops && driver->ops->stream_pm)
+ driver->ops->stream_pm(codec, nid, true);
+ else if (codec->patch_ops.stream_pm)
codec->patch_ops.stream_pm(codec, nid, true);
if (codec->pcm_format_first)
update_pcm_format(codec, p, nid, format);
static void really_cleanup_stream(struct hda_codec *codec,
struct hda_cvt_setup *q)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
hda_nid_t nid = q->nid;
+
if (q->stream_tag || q->channel_id)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
if (q->format_id)
);
memset(q, 0, sizeof(*q));
q->nid = nid;
- if (codec->patch_ops.stream_pm)
+ if (driver->ops && driver->ops->stream_pm)
+ driver->ops->stream_pm(codec, nid, false);
+ else if (codec->patch_ops.stream_pm)
codec->patch_ops.stream_pm(codec, nid, false);
}
static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
int count;
unsigned int state;
/* repeat power states setting at most 10 times*/
for (count = 0; count < 10; count++) {
- if (codec->patch_ops.set_power_state)
+ /* might be called before binding to driver, too */
+ if (driver && driver->ops && driver->ops->set_power_state)
+ driver->ops->set_power_state(codec, fg, power_state);
+ else if (codec->patch_ops.set_power_state)
codec->patch_ops.set_power_state(codec, fg,
power_state);
else {
*/
static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
unsigned int state;
snd_hdac_enter_pm(&codec->core);
- if (codec->patch_ops.suspend)
+ if (driver->ops && driver->ops->suspend)
+ driver->ops->suspend(codec);
+ else if (codec->patch_ops.suspend)
codec->patch_ops.suspend(codec);
if (!codec->no_stream_clean_at_suspend)
hda_cleanup_all_streams(codec);
*/
static void hda_call_codec_resume(struct hda_codec *codec)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
snd_hdac_enter_pm(&codec->core);
if (codec->core.regmap)
regcache_mark_dirty(codec->core.regmap);
restore_shutup_pins(codec);
hda_exec_init_verbs(codec);
snd_hda_jack_set_dirty_all(codec);
- if (codec->patch_ops.resume)
+ if (driver->ops && driver->ops->resume)
+ driver->ops->resume(codec);
+ else if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
else {
- if (codec->patch_ops.init)
- codec->patch_ops.init(codec);
+ snd_hda_codec_init(codec);
snd_hda_regmap_sync(codec);
}
int snd_hda_codec_build_controls(struct hda_codec *codec)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
int err = 0;
+
hda_exec_init_verbs(codec);
/* continue to initialize... */
- if (codec->patch_ops.init)
- err = codec->patch_ops.init(codec);
- if (!err && codec->patch_ops.build_controls)
- err = codec->patch_ops.build_controls(codec);
- if (err < 0)
- return err;
+ err = snd_hda_codec_init(codec);
+ if (!err) {
+ if (driver->ops && driver->ops->build_controls)
+ err = driver->ops->build_controls(codec);
+ else if (codec->patch_ops.build_controls)
+ err = codec->patch_ops.build_controls(codec);
+ if (err < 0)
+ return err;
+ }
/* we create chmaps here instead of build_pcms */
err = add_std_chmaps(codec);
/* call build_pcms ops of the given codec and set up the default parameters */
int snd_hda_codec_parse_pcms(struct hda_codec *codec)
{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hda_pcm *cpcm;
int err;
if (!list_empty(&codec->pcm_list_head))
return 0; /* already parsed */
- if (!codec->patch_ops.build_pcms)
+ if (driver->ops && driver->ops->build_pcms)
+ err = driver->ops->build_pcms(codec);
+ else if (codec->patch_ops.build_pcms)
+ err = codec->patch_ops.build_pcms(codec);
+ else
return 0;
- err = codec->patch_ops.build_pcms(codec);
if (err < 0) {
codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
codec->core.addr, err);
void snd_hda_codec_shutdown(struct hda_codec *codec);
+static inline int snd_hda_codec_init(struct hda_codec *codec)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ if (driver->ops && driver->ops->init)
+ return driver->ops->init(codec);
+ else if (codec->patch_ops.init)
+ return codec->patch_ops.init(codec);
+ return 0;
+}
+
/*
* AMP control callbacks
*/
static int hda_codec_probe(struct snd_soc_component *component)
{
struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hdac_device *hdev = &codec->core;
struct hdac_bus *bus = hdev->bus;
struct hdac_ext_link *hlink;
goto err;
}
- patch = (hda_codec_patch_t)codec->preset->driver_data;
- if (!patch) {
- dev_err(&hdev->dev, "no patch specified\n");
- ret = -EINVAL;
- goto err;
+ if (driver->ops && driver->ops->probe) {
+ ret = driver->ops->probe(codec, codec->preset);
+ } else {
+ patch = (hda_codec_patch_t)codec->preset->driver_data;
+ if (!patch) {
+ dev_err(&hdev->dev, "no patch specified\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = patch(codec);
}
- ret = patch(codec);
if (ret < 0) {
dev_err(&hdev->dev, "codec init failed: %d\n", ret);
goto err;
complete_err:
hda_codec_unregister_dais(codec, component);
parse_pcms_err:
- if (codec->patch_ops.free)
+ if (driver->ops && driver->ops->remove)
+ driver->ops->remove(codec);
+ else if (codec->patch_ops.free)
codec->patch_ops.free(codec);
err:
snd_hda_codec_cleanup_for_unbind(codec);
static void hda_codec_remove(struct snd_soc_component *component)
{
struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hdac_device *hdev = &codec->core;
struct hdac_bus *bus = hdev->bus;
struct hdac_ext_link *hlink;
hda_codec_unregister_dais(codec, component);
- if (codec->patch_ops.free)
+ if (driver->ops && driver->ops->remove)
+ driver->ops->remove(codec);
+ else if (codec->patch_ops.free)
codec->patch_ops.free(codec);
snd_hda_codec_cleanup_for_unbind(codec);
snd_soc_component_get_dapm(component);
struct hdac_device *hdev = &hda_pvt->codec->core;
struct hda_codec *hcodec = hda_pvt->codec;
+ struct hda_codec_driver *driver = hda_codec_to_driver(hcodec);
struct hdac_ext_link *hlink;
hda_codec_patch_t patch;
int ret;
goto error_pm;
}
- patch = (hda_codec_patch_t)hcodec->preset->driver_data;
- if (patch) {
- ret = patch(hcodec);
+ if (driver->ops && driver->ops->probe) {
+ ret = driver->ops->probe(hcodec, hcodec->preset);
if (ret < 0) {
- dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret);
+ dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret);
goto error_regmap;
}
} else {
- dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__);
+ patch = (hda_codec_patch_t)hcodec->preset->driver_data;
+ if (patch) {
+ ret = patch(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret);
+ goto error_regmap;
+ }
+ } else {
+ dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__);
+ }
}
ret = snd_hda_codec_parse_pcms(hcodec);
return 0;
error_patch:
- if (hcodec->patch_ops.free)
+ if (driver->ops && driver->ops->remove)
+ driver->ops->remove(hcodec);
+ else if (hcodec->patch_ops.free)
hcodec->patch_ops.free(hcodec);
error_regmap:
snd_hdac_regmap_exit(hdev);
snd_soc_component_get_drvdata(component);
struct hdac_device *hdev = &hda_pvt->codec->core;
struct hda_codec *codec = hda_pvt->codec;
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hdac_ext_link *hlink = NULL;
hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
pm_runtime_disable(&hdev->dev);
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
- if (codec->patch_ops.free)
+ if (driver->ops && driver->ops->remove)
+ driver->ops->remove(codec);
+ else if (codec->patch_ops.free)
codec->patch_ops.free(codec);
snd_hda_codec_cleanup_for_unbind(codec);