From da9364ecf4af151bddf0af66ed8d1b4350d8ad6a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:50:37 +0100 Subject: [PATCH 001/298] ASoC: topology: Give more data to clients via callbacks Give topology clients more access to the topology data by passing index, pcm, link_config and dai_driver to clients. This allows clients to fully instantiate and track topology objects. The SOF driver is the first user of these new APIs and needs them to build component topology driver and FW objects. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit c60b613a7097cff20fdd05e2891ce69542f0d5a3) --- include/sound/soc-topology.h | 23 +++++++++++-------- sound/soc/intel/skylake/skl-pcm.c | 7 +++--- sound/soc/intel/skylake/skl-topology.c | 5 +++-- sound/soc/intel/skylake/skl-topology.h | 5 +++-- sound/soc/soc-topology.c | 31 +++++++++++++++----------- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index f552c3f56368c4..e1f265e21ee128 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -30,6 +30,8 @@ struct snd_soc_dapm_context; struct snd_soc_card; struct snd_kcontrol_new; struct snd_soc_dai_link; +struct snd_soc_dai_driver; +struct snd_soc_dai; /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ @@ -109,35 +111,38 @@ struct snd_soc_tplg_widget_events { struct snd_soc_tplg_ops { /* external kcontrol init - used for any driver specific init */ - int (*control_load)(struct snd_soc_component *, + int (*control_load)(struct snd_soc_component *, int index, struct snd_kcontrol_new *, struct snd_soc_tplg_ctl_hdr *); int (*control_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* external widget init - used for any driver specific init */ - int (*widget_load)(struct snd_soc_component *, + int (*widget_load)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, struct snd_soc_tplg_dapm_widget *); - int (*widget_ready)(struct snd_soc_component *, + int (*widget_ready)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, struct snd_soc_tplg_dapm_widget *); int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* FE DAI - used for any driver specific init */ - int (*dai_load)(struct snd_soc_component *, - struct snd_soc_dai_driver *dai_drv); + int (*dai_load)(struct snd_soc_component *, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); + int (*dai_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* DAI link - used for any driver specific init */ - int (*link_load)(struct snd_soc_component *, - struct snd_soc_dai_link *link); + int (*link_load)(struct snd_soc_component *, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg); int (*link_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* callback to handle vendor bespoke data */ - int (*vendor_load)(struct snd_soc_component *, + int (*vendor_load)(struct snd_soc_component *, int index, struct snd_soc_tplg_hdr *); int (*vendor_unload)(struct snd_soc_component *, struct snd_soc_tplg_hdr *); @@ -146,7 +151,7 @@ struct snd_soc_tplg_ops { void (*complete)(struct snd_soc_component *); /* manifest - optional to inform component of manifest */ - int (*manifest)(struct snd_soc_component *, + int (*manifest)(struct snd_soc_component *, int index, struct snd_soc_tplg_manifest *); /* vendor specific kcontrol handlers available for binding */ diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index afa86b9e4dcf31..1f4dd08d36c55e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1017,10 +1017,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }; -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai) +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - pcm_dai->ops = &skl_pcm_dai_ops; + dai_drv->ops = &skl_pcm_dai_ops; return 0; } diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index fcdc716754b634..647e52aecdc334 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3024,7 +3024,7 @@ void skl_cleanup_resources(struct skl *skl) * information to the driver about module and pipeline parameters which DSP * FW expects like ids, resource values, formats etc */ -static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { @@ -3131,6 +3131,7 @@ static int skl_init_enum_data(struct device *dev, struct soc_enum *se, } static int skl_tplg_control_load(struct snd_soc_component *cmpnt, + int index, struct snd_kcontrol_new *kctl, struct snd_soc_tplg_ctl_hdr *hdr) { @@ -3619,7 +3620,7 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, return 0; } -static int skl_manifest_load(struct snd_soc_component *cmpnt, +static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_manifest *manifest) { struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 6d7e0569695f92..af198ea0379e64 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -512,8 +512,9 @@ int skl_pcm_host_dma_prepare(struct device *dev, int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai); +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); void skl_tplg_add_moduleid_in_bind_params(struct skl *skl, struct snd_soc_dapm_widget *w); #endif diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 53f121a50c97bd..9b33260fd53735 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -259,7 +259,7 @@ static int soc_tplg_vendor_load_(struct soc_tplg *tplg, int ret = 0; if (tplg->comp && tplg->ops && tplg->ops->vendor_load) - ret = tplg->ops->vendor_load(tplg->comp, hdr); + ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); else { dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", hdr->vendor_type); @@ -291,7 +291,8 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_load) - return tplg->ops->widget_load(tplg->comp, w, tplg_w); + return tplg->ops->widget_load(tplg->comp, tplg->index, w, + tplg_w); return 0; } @@ -302,27 +303,30 @@ static int soc_tplg_widget_ready(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_ready) - return tplg->ops->widget_ready(tplg->comp, w, tplg_w); + return tplg->ops->widget_ready(tplg->comp, tplg->index, w, + tplg_w); return 0; } /* pass DAI configurations to component driver for extra initialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, - struct snd_soc_dai_driver *dai_drv) + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { if (tplg->comp && tplg->ops && tplg->ops->dai_load) - return tplg->ops->dai_load(tplg->comp, dai_drv); + return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, + pcm, dai); return 0; } /* pass link configurations to component driver for extra initialization */ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, - struct snd_soc_dai_link *link) + struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { if (tplg->comp && tplg->ops && tplg->ops->link_load) - return tplg->ops->link_load(tplg->comp, link); + return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); return 0; } @@ -643,7 +647,8 @@ static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { if (tplg->comp && tplg->ops && tplg->ops->control_load) - return tplg->ops->control_load(tplg->comp, k, hdr); + return tplg->ops->control_load(tplg->comp, tplg->index, k, + hdr); return 0; } @@ -1702,7 +1707,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, dai_drv->compress_new = snd_soc_new_compress; /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); kfree(dai_drv); @@ -1772,7 +1777,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, set_link_flags(link, pcm->flag_mask, pcm->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); kfree(link); @@ -2080,7 +2085,7 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, set_link_flags(link, cfg->flag_mask, cfg->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, cfg); if (ret < 0) { dev_err(tplg->dev, "ASoC: physical link loading failed\n"); return ret; @@ -2202,7 +2207,7 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg, set_dai_flags(dai_drv, d->flag_mask, d->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); return ret; @@ -2311,7 +2316,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, _manifest); + return tplg->ops->manifest(tplg->comp, tplg->index, _manifest); if (!abi_match) /* free the duplicated one */ kfree(_manifest); From 14cac8bdcad3be8b2af46c0d313d5ba1c0d187f6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:53:59 +0100 Subject: [PATCH 002/298] ASoC: topology: Add callback for DAPM route load/unload Add a callback fro clients for notification about DAPM route loading and unloading. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit 503e79b793fea5de626db73accf8e8994bc4289d) --- include/sound/soc-topology.h | 7 +++++++ sound/soc/soc-topology.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index e1f265e21ee128..401ef2c45d6c76 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -32,6 +32,7 @@ struct snd_kcontrol_new; struct snd_soc_dai_link; struct snd_soc_dai_driver; struct snd_soc_dai; +struct snd_soc_dapm_route; /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ @@ -116,6 +117,12 @@ struct snd_soc_tplg_ops { int (*control_unload)(struct snd_soc_component *, struct snd_soc_dobj *); + /* DAPM graph route element loading and unloading */ + int (*dapm_route_load)(struct snd_soc_component *, int index, + struct snd_soc_dapm_route *route); + int (*dapm_route_unload)(struct snd_soc_component *, + struct snd_soc_dobj *); + /* external widget init - used for any driver specific init */ int (*widget_load)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 9b33260fd53735..05d177d689e264 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1105,6 +1105,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, return 0; } +/* optionally pass new dynamic kcontrol to component driver. */ +static int soc_tplg_add_route(struct soc_tplg *tplg, + struct snd_soc_dapm_route *route) +{ + if (tplg->comp && tplg->ops && tplg->ops->dapm_route_load) + return tplg->ops->dapm_route_load(tplg->comp, tplg->index, + route); + + return 0; +} + static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { @@ -1153,6 +1164,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, else route.control = elem->control; + soc_tplg_add_route(tplg, &route); + /* add route, but keep going if some fail */ snd_soc_dapm_add_routes(dapm, &route, 1); } From ec0b19042337a70b0dc3365b3789842177ecbede Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:26:42 +0100 Subject: [PATCH 003/298] ASoC: dapm: Fix potential DAI widget pointer deref when linking DAIs Sometime a component or topology may configure a DAI widget with no private data leading to a dev_dbg() dereferencne of this data. Fix this to check for non NULL private data and let users know if widget is missing DAI. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit e01b4f624278d5efe5fb5da585ca371947b16680) --- sound/soc/soc-dapm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 229c123498030b..a099c3e4550478 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4073,6 +4073,13 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) continue; } + /* let users know there is no DAI to link */ + if (!dai_w->priv) { + dev_dbg(card->dev, "dai widget %s has no DAI\n", + dai_w->name); + continue; + } + dai = dai_w->priv; /* ...find all widgets with the same stream and link them */ From f21a5cb8df34cdddb21e42bd50df1c29ef3b8655 Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Mon, 18 Jun 2018 13:29:35 -0500 Subject: [PATCH 004/298] ASoC: Intel: broxton: reduce machine name for bxt_da7219_max98357a Use truncated names in bxt id table and bxt_da7219_max98357a machine as platform device id table expects names to be less then 20chars. Signed-off-by: Naveen Manohar Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 95555f580dca21fac5ea35c10fa92fa034bd403f) --- sound/soc/intel/boards/bxt_da7219_max98357a.c | 4 ++-- sound/soc/intel/skylake/skl.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 40eb979d5ac1d9..3aba5bcf806aad 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -586,7 +586,7 @@ static int broxton_audio_probe(struct platform_device *pdev) static struct platform_driver broxton_audio = { .probe = broxton_audio_probe, .driver = { - .name = "bxt_da7219_max98357a_i2s", + .name = "bxt_da7219_max98357a", .pm = &snd_soc_pm_ops, }, }; @@ -599,4 +599,4 @@ MODULE_AUTHOR("Rohit Ainapure "); MODULE_AUTHOR("Harsha Priya "); MODULE_AUTHOR("Conrad Cooke "); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s"); +MODULE_ALIAS("platform:bxt_da7219_max98357a"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f0d9793f872aba..0a8f0768e98726 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1093,7 +1093,7 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { }, { .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a_i2s", + .drv_name = "bxt_da7219_max98357a", .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, From 6e35ff995490e4ee83a4cde6e1f577b77ba2b4a6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:36 -0500 Subject: [PATCH 005/298] ASoC: Intel: Skylake: cleanup before moving ACPI tables There is no need to deal with DMICs if the DSP is not present and there is no ACPI machine ID found. Simplify before moving these ACPI tables to sound/soc/intel/common Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 5f15f267daf81a4c7c2a1cd2a0d6743ec7fc8b59) --- sound/soc/intel/skylake/skl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 0a8f0768e98726..6dec748e8949b7 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -500,10 +500,12 @@ static int skl_find_machine(struct skl *skl, void *driver_data) skl->mach = mach; skl->fw_name = mach->fw_filename; - pdata = skl->mach->pdata; + pdata = mach->pdata; - if (mach->pdata) + if (pdata) { skl->use_tplg_pcm = pdata->use_tplg_pcm; + pdata->dmic_num = skl_get_dmic_geo(skl); + } return 0; } @@ -930,8 +932,6 @@ static int skl_probe(struct pci_dev *pci, pci_set_drvdata(skl->pci, ebus); - skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); - /* check if dsp is there */ if (bus->ppcap) { /* create device for dsp clk */ From 1b1f92c6add4f75be55111bcd70872b944681bff Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:37 -0500 Subject: [PATCH 006/298] ASoC: Intel: move SKL+ codec ACPI tables to common directory No functionality change, just move to common tables to make it easier to deal with SOF and share the same machine drivers - as done previously for BYT/CHT/HSW/BDW. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit cbaa7f0bdbee1969bb311c641abbd0d2af6ba861) --- include/sound/soc-acpi-intel-match.h | 5 + sound/soc/intel/common/Makefile | 6 +- .../intel/common/soc-acpi-intel-bxt-match.c | 35 ++++ .../intel/common/soc-acpi-intel-cnl-match.c | 29 ++++ .../intel/common/soc-acpi-intel-glk-match.c | 23 +++ .../intel/common/soc-acpi-intel-kbl-match.c | 91 ++++++++++ .../intel/common/soc-acpi-intel-skl-match.c | 47 +++++ sound/soc/intel/skylake/skl.c | 162 +----------------- 8 files changed, 241 insertions(+), 157 deletions(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-bxt-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-cnl-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-glk-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-kbl-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-skl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 9da6388c20a1cc..917ddd0f2762ea 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -29,5 +29,10 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 7379d8830c391e..915a34cdc8acbb 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -3,7 +3,11 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o -snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \ + soc-acpi-intel-hsw-bdw-match.o \ + soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ + soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ + soc-acpi-intel-cnl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c new file mode 100644 index 00000000000000..569f1de97e82da --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-bxt-match.c - tables and support for BXT ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include + +static struct snd_soc_acpi_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &bxt_codecs, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c new file mode 100644 index 00000000000000..b83ee053d41767 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-cnl-match.c - tables and support for CNL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { + { + .id = "INT34C2", + .drv_name = "cnl_rt274", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c new file mode 100644 index 00000000000000..dee09439e7cb16 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-glk-match.c - tables and support for GLK ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include + +struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c new file mode 100644 index 00000000000000..0ee173ca437dd3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-kbl-match.c - tables and support for KBL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + +static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "10EC5663", + .drv_name = "kbl_rt5663", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98357_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c new file mode 100644 index 00000000000000..0c9c0edd35b34d --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-skl-match.c - tables and support for SKL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[] = { + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_skl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 6dec748e8949b7..670ff9aaca55de 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,8 +37,6 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" -static struct skl_machine_pdata skl_dmic_data; - /* * initialize the PCI registers */ @@ -1026,172 +1025,23 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct snd_soc_acpi_codecs skl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs kbl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs bxt_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { - .num_codecs = 1, - .codecs = {"10EC5663"} -}; - -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { - .num_codecs = 2, - .codecs = {"10EC5663", "10EC5514"} -}; - -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct skl_machine_pdata cnl_pdata = { - .use_tplg_pcm = true, -}; - -static struct snd_soc_acpi_mach sst_skl_devdata[] = { - { - .id = "INT343A", - .drv_name = "skl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_release.bin", - }, - { - .id = "INT343B", - .drv_name = "skl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "skl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - {} -}; - -static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { - { - .id = "INT343A", - .drv_name = "bxt_alc298s_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - }, - { - .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &bxt_codecs, - }, - {} -}; - -static struct snd_soc_acpi_mach sst_kbl_devdata[] = { - { - .id = "INT343A", - .drv_name = "kbl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "INT343B", - .drv_name = "kbl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "kbl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_r5514_5663_max", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_5663_5514_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_rt5663_m98927", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_poppy_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "10EC5663", - .drv_name = "kbl_rt5663", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "DLGS7219", - .drv_name = "kbl_da7219_max98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_7219_98357_codecs, - .pdata = &skl_dmic_data - }, - - {} -}; - -static struct snd_soc_acpi_mach sst_glk_devdata[] = { - { - .id = "INT343A", - .drv_name = "glk_alc298s_i2s", - .fw_filename = "intel/dsp_fw_glk.bin", - }, - {} -}; - -static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { - { - .id = "INT34C2", - .drv_name = "cnl_rt274", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = (unsigned long)&sst_skl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = (unsigned long)&sst_bxtp_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, /* KBL */ { PCI_DEVICE(0x8086, 0x9D71), - .driver_data = (unsigned long)&sst_kbl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, /* GLK */ { PCI_DEVICE(0x8086, 0x3198), - .driver_data = (unsigned long)&sst_glk_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = (unsigned long)&sst_cnl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); From 3b0636483c356fda2d653b89044237f4be1e48c8 Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Mon, 18 Jun 2018 13:29:38 -0500 Subject: [PATCH 007/298] ASoC: Intel: common: Add Geminilake Dialog+Maxim machine driver entry This patch adds da7219_max98357a machine driver entry into machine table Signed-off-by: Naveen Manohar Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 65a33883c778befcb85ef45285763fd8ac1b2ba3) --- sound/soc/intel/common/soc-acpi-intel-glk-match.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index dee09439e7cb16..5902aa1d0ee38f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -9,12 +9,24 @@ #include #include +static struct snd_soc_acpi_codecs glk_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", }, + { + .id = "DLGS7219", + .drv_name = "glk_da7219_max98357a", + .fw_filename = "intel/dsp_fw_glk.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &glk_codecs, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); From 55382efba814c117f7dc7167ee1bdf1ba1aad060 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:39 -0500 Subject: [PATCH 008/298] ASoC: Intel: common: add firmware/topology information for SOF No functionality change for Skylake driver, add relevant names needed by SOF for BXT/APL, GLK and CNL. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit e6d298fd4a4454dd121343323e3f00a27f8819a4) --- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 3 +++ sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 3 +++ sound/soc/intel/common/soc-acpi-intel-glk-match.c | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 569f1de97e82da..50869eddbb3b00 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -26,6 +26,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index b83ee053d41767..ec8e28e7b937aa 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -20,6 +20,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .drv_name = "cnl_rt274", .fw_filename = "intel/dsp_fw_cnl.bin", .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index 5902aa1d0ee38f..305875af71caee 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -19,6 +19,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-alc298.tplg", + .asoc_plat_name = "0000:00:0e.0", }, { .id = "DLGS7219", @@ -26,6 +29,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .fw_filename = "intel/dsp_fw_glk.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", }, {}, }; From 03378af3847d7cd1448399932d726a6bbf425138 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:41 -0500 Subject: [PATCH 009/298] ASoC: Intel: common: add entries for SOF-based machine drivers While we are at it, add entries for machine drivers that are used on SOF-based platforms. The drivers will be submitted upstream after the core SOF patches, but there's no harm in adding these references now. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit b45350135b9241b64cc91ccc8dddca2ee4dc25d7) --- .../intel/common/soc-acpi-intel-bxt-match.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 50869eddbb3b00..f39386e540d322 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -30,6 +30,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_tplg_filename = "intel/sof-apl-da7219.tplg", .asoc_plat_name = "0000:00:0e.0", }, + { + .id = "104C5122", + .drv_name = "bxt-pcm512x", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-pcm512x.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "1AEC8804", + .drv_name = "bxt-wm8804", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "INT34C3", + .drv_name = "bxt_tdf8532", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); From 3a9c180668d1c4a3cb0680c5966339f2b01c0b6e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:42 -0500 Subject: [PATCH 010/298] ASoC: Intel: common: fix copy/paste issue with SOF/broadwell topology file There are two commercially-available Broadwell platforms based on I2S (Dell XPS13 and 'Samus' Pixel 2015 Chromebook). Fix a copy/paste issue to allow each platform to enable different features if needed when SOF is enabled Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit f0d9034b290d8bad590e843c2a1081eb47d813ad) --- sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index e0e8c8c27528cb..53e7acdfbb81ac 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -45,7 +45,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_tplg_filename = "intel/reef-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { From 6ca3f51ccd0e2c07acf9cd495b5bfb3ea6c863ea Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 19 Jun 2018 14:00:37 -0500 Subject: [PATCH 011/298] ASoC: Intel: common: rename 'reef' to 'sof' in ACPI matching table Align with firmware tools, no functionality change Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 4f722a6a736765310bc95dd3879f1545e5f64303) --- .../intel/common/soc-acpi-intel-byt-match.c | 40 +++++++------- .../intel/common/soc-acpi-intel-cht-match.c | 52 +++++++++---------- .../common/soc-acpi-intel-hsw-bdw-match.c | 16 +++--- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index bfe1ca68a54234..4daa8a4f0c0c69 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -59,8 +59,8 @@ static struct snd_soc_acpi_mach byt_thinkpad_10 = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5670.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -98,8 +98,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", .machine_quirk = byt_quirk, - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -107,8 +107,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -116,8 +116,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -125,8 +125,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5651.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -134,8 +134,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -143,8 +143,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ @@ -153,8 +153,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -162,8 +162,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* use CHT driver to Baytrail Chromebooks */ @@ -172,8 +172,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-max98090.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index ad1eb2d644beab..3c3f2f8585d719 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -44,8 +44,8 @@ static struct snd_soc_acpi_mach cht_surface_mach = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -68,8 +68,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -77,8 +77,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -86,8 +86,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -95,8 +95,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -104,8 +104,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -113,8 +113,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-max98090.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -131,8 +131,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -140,8 +140,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -149,8 +149,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-es8316.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-es8316.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ @@ -160,8 +160,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", .machine_quirk = cht_quirk, - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -169,8 +169,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ @@ -179,8 +179,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5651.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index 53e7acdfbb81ac..494a0ea9b02903 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -23,8 +23,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST1.bin", - .sof_fw_filename = "intel/reef-hsw.ri", - .sof_tplg_filename = "intel/reef-hsw.tplg", + .sof_fw_filename = "intel/sof-hsw.ri", + .sof_tplg_filename = "intel/sof-hsw.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} @@ -36,24 +36,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .id = "INT343A", .drv_name = "broadwell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt286.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5677.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5640.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5640.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} From 55bd79ed60b8051f6797a76eded540e9e59b83be Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 21 Jun 2018 15:47:35 -0500 Subject: [PATCH 012/298] ASoC: Intel: common: fix missing rename from 'reef' to 'sof' Somehow I missed the Nau8824 support which was added in 4.17. Oops Fixes: 4f722a6a736 ("ASoC: Intel: common: rename 'reef' to 'sof' in ACPI matching table") Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit bb450fa59c0772310ebcc704722dd8a0313bf8ed) --- sound/soc/intel/common/soc-acpi-intel-cht-match.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index 3c3f2f8585d719..91bb99b69601dd 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -122,8 +122,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-nau8824", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-nau8824.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-nau8824.tplg", .asoc_plat_name = "sst-mfld-platform", }, { From f31530c535e853295d7011bb581a8ce20643fac1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Jul 2018 16:59:54 +0100 Subject: [PATCH 013/298] ASoC: core: Allow topology to override machine driver FE DAI link config. Machine drivers statically define a number of DAI links that currently cannot be changed or removed by topology. This means PCMs and platform components cannot be changed by topology at runtime AND machine drivers are tightly coupled to topology. This patch allows topology to override the machine driver DAI link config in order to reuse machine drivers with different topologies and platform components. The patch supports :- 1) create new FE PCMs with a topology defined PCM ID. 2) destroy existing static FE PCMs 3) change the platform component driver. 4) assign any new HW params fixups. 5) assign a new card name prefix to differentiate this topology to userspace. The patch requires no changes to the machine drivers, but does add some platform component flags that the platform component driver can assign before loading topologies. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit a655de808cbde6c58b3298e704d786b53f59fb5d) --- include/sound/soc.h | 13 ++++++ sound/soc/soc-core.c | 101 +++++++++++++++++++++++++++++++++++++++++-- sound/soc/soc-pcm.c | 12 +++++ 3 files changed, 123 insertions(+), 3 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 1378dcd2128aa3..c24e2fad860201 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -806,6 +806,14 @@ struct snd_soc_component_driver { unsigned int use_pmdown_time:1; /* care pmdown_time at stop */ unsigned int endianness:1; unsigned int non_legacy_dai_naming:1; + + /* this component uses topology and ignore machine driver FEs */ + const char *ignore_machine; + const char *topology_name_prefix; + int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); + bool use_dai_pcm_id; /* use the DAI link PCM ID as PCM device number */ + int be_pcm_base; /* base device ID for all BE PCMs */ }; struct snd_soc_component { @@ -961,6 +969,9 @@ struct snd_soc_dai_link { /* pmdown_time is ignored at stop */ unsigned int ignore_pmdown_time:1; + /* Do not create a PCM for this DAI link (Backend link) */ + unsigned int ignore:1; + struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ }; @@ -1000,6 +1011,7 @@ struct snd_soc_card { const char *long_name; const char *driver_name; char dmi_longname[80]; + char topology_shortname[32]; struct device *dev; struct snd_card *snd_card; @@ -1009,6 +1021,7 @@ struct snd_soc_card { struct mutex dapm_mutex; bool instantiated; + bool topology_shortname_created; int (*probe)(struct snd_soc_card *card); int (*late_probe)(struct snd_soc_card *card); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4663de3cf49527..38bf7d01894b8b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -852,6 +852,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, const char *platform_name; int i; + if (dai_link->ignore) + return 0; + dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); if (soc_is_dai_link_bound(card, dai_link)) { @@ -1461,7 +1464,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, { struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + int i, ret, num; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); @@ -1507,9 +1512,28 @@ static int soc_probe_link_dais(struct snd_soc_card *card, soc_dpcm_debugfs_add(rtd); #endif + num = rtd->num; + + /* + * most drivers will register their PCMs using DAI link ordering but + * topology based drivers can use the DAI link id field to set PCM + * device number and then use rtd + a base offset of the BEs. + */ + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->use_dai_pcm_id) + continue; + + if (rtd->dai_link->no_pcm) + num += component->driver->be_pcm_base; + else + num = rtd->dai_link->id; + } + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = cpu_dai->driver->compress_new(rtd, rtd->num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); @@ -1519,7 +1543,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, if (!dai_link->params) { /* create the pcm */ - ret = soc_new_pcm(rtd, rtd->num); + ret = soc_new_pcm(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", dai_link->stream_name, ret); @@ -1846,6 +1870,74 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); #endif /* CONFIG_DMI */ +static void soc_check_tplg_fes(struct snd_soc_card *card) +{ + struct snd_soc_component *component; + const struct snd_soc_component_driver *comp_drv; + struct snd_soc_dai_link *dai_link; + int i; + + list_for_each_entry(component, &component_list, list) { + + /* does this component override FEs ? */ + if (!component->driver->ignore_machine) + continue; + + /* for this machine ? */ + if (strcmp(component->driver->ignore_machine, + card->dev->driver->name)) + continue; + + /* machine matches, so override the rtd data */ + for (i = 0; i < card->num_links; i++) { + + dai_link = &card->dai_link[i]; + + /* ignore this FE */ + if (dai_link->dynamic) { + dai_link->ignore = true; + continue; + } + + dev_info(card->dev, "info: override FE DAI link %s\n", + card->dai_link[i].name); + + /* override platform component */ + dai_link->platform_name = component->name; + + /* convert non BE into BE */ + dai_link->no_pcm = 1; + + /* override any BE fixups */ + dai_link->be_hw_params_fixup = + component->driver->be_hw_params_fixup; + + /* most BE links don't set stream name, so set it to + * dai link name if it's NULL to help bind widgets. + */ + if (!dai_link->stream_name) + dai_link->stream_name = dai_link->name; + } + + /* Inform userspace we are using alternate topology */ + if (component->driver->topology_name_prefix) { + + /* topology shortname created ? */ + if (!card->topology_shortname_created) { + comp_drv = component->driver; + + snprintf(card->topology_shortname, 32, "%s-%s", + comp_drv->topology_name_prefix, + card->name); + card->topology_shortname_created = true; + } + + /* use topology shortname */ + card->name = card->topology_shortname; + } + } +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -1855,6 +1947,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + /* check whether any platform is ignore machine FE and using topology */ + soc_check_tplg_fes(card); + /* bind DAIs */ for (i = 0; i < card->num_links; i++) { ret = soc_bind_dai_link(card, &card->dai_link[i]); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 5e7ae47a9658c3..f9d2659949a82f 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -860,8 +860,20 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret; + /* perform any topology hw_params fixups before DAI */ + if (rtd->dai_link->be_hw_params_fixup) { + ret = rtd->dai_link->be_hw_params_fixup(rtd, params); + if (ret < 0) { + dev_err(rtd->dev, + "ASoC: hw_params topology fixup failed %d\n", + ret); + return ret; + } + } + if (dai->driver->ops->hw_params) { ret = dai->driver->ops->hw_params(substream, params, dai); if (ret < 0) { From 711da466c9d6e492bb46a322d133e20c931d56be Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 6 Jul 2018 13:50:36 +0100 Subject: [PATCH 014/298] ALSA: pcm: Allow drivers to set R/W wait time. Currently ALSA core blocks userspace for about 10 seconds for PCM R/W IO. This needs to be configurable for modern hardware like DSPs where no pointer update in milliseconds can indicate terminal DSP errors. Add a substream variable to set the wait time in ms. This allows userspace and drivers to recover more quickly from terminal DSP errors. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai (cherry picked from commit d64c5cf8e89d124355924c513a42b16f0d7d3a03) --- include/sound/pcm.h | 1 + sound/core/pcm_lib.c | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e054c583d3b339..fcdf358a25f050 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -462,6 +462,7 @@ struct snd_pcm_substream { /* -- timer section -- */ struct snd_timer *timer; /* timer */ unsigned timer_running: 1; /* time is running */ + long wait_time; /* time in ms for R/W to wait for avail */ /* -- next substream -- */ struct snd_pcm_substream *next; /* -- linked substreams -- */ diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 44b5ae83308215..eecbfdc7a2c949 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1832,12 +1832,19 @@ static int wait_for_avail(struct snd_pcm_substream *substream, if (runtime->no_period_wakeup) wait_time = MAX_SCHEDULE_TIMEOUT; else { - wait_time = 10; - if (runtime->rate) { - long t = runtime->period_size * 2 / runtime->rate; - wait_time = max(t, wait_time); + /* use wait time from substream if available */ + if (substream->wait_time) { + wait_time = substream->wait_time; + } else { + wait_time = 10; + + if (runtime->rate) { + long t = runtime->period_size * 2 / + runtime->rate; + wait_time = max(t, wait_time); + } + wait_time = msecs_to_jiffies(wait_time * 1000); } - wait_time = msecs_to_jiffies(wait_time * 1000); } for (;;) { From 536a9a1f212956e51cb4d420e269568ae9645538 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 22 Jun 2018 15:47:24 -0500 Subject: [PATCH 015/298] ASoC: Intel: common: add ACPI matching tables for ICL Signed-off-by: Pierre-Louis Bossart --- include/sound/soc-acpi-intel-match.h | 1 + sound/soc/intel/common/Makefile | 2 +- .../intel/common/soc-acpi-intel-icl-match.c | 32 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-icl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 917ddd0f2762ea..1de9ed784c1b23 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -34,5 +34,6 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 915a34cdc8acbb..fd52419f1c814a 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c new file mode 100644 index 00000000000000..33b441dca4d308 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-icl-match.c - tables and support for ICL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata icl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { + { + .id = "INT34C2", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + .sof_fw_filename = "intel/sof-icl.ri", + .sof_tplg_filename = "intel/sof-icl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); From 9b824e66911fb7ddffaa73207f57d759cb2694ec Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 12 Jun 2018 18:52:58 -0500 Subject: [PATCH 016/298] ASoC: Intel: common: add ACPI match for CannonLake MX98373 is used on some CNL boards Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index ec8e28e7b937aa..ae3be064e75e99 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -24,6 +24,15 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", .asoc_plat_name = "0000:00:1f.3", }, + { + .id = "MX98373", + .drv_name = "cnl_max98373", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); From 9b6888e6651377c942aaed3571c71555863ced1d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 31 Jul 2018 19:43:13 -0500 Subject: [PATCH 017/298] ASoC: core: support driver alias names for FE topology overrides When the same machine driver is reused between platforms but with a different alias, using the driver name is not enough. Add additional fallback case to use the card device name. Tested on GeminiLake with bxt_da7219_max98357a machine driver Signed-off-by: Pierre-Louis Bossart --- sound/soc/soc-core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 38bf7d01894b8b..71fdac067a47bd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1884,10 +1884,13 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) continue; /* for this machine ? */ + if (!strcmp(component->driver->ignore_machine, + card->dev->driver->name)) + goto match; if (strcmp(component->driver->ignore_machine, - card->dev->driver->name)) + dev_name(card->dev))) continue; - +match: /* machine matches, so override the rtd data */ for (i = 0; i < card->num_links; i++) { From 4dec7c9e75506f9dc104f4a0dee9bf750775567c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:30:59 +0000 Subject: [PATCH 018/298] ASoC: SOF: Add Sound Open Firmware driver core The Sound Open Firmware driver core is a generic architecture independent layer that allows SOF to be used on many different different architectures and platforms. It abstracts DSP operations and IO methods so that the target DSP can be an internal memory mapped or external SPI or I2C based device. This abstraction also allows SOF to be run on many different VMs on the same physical HW. SOF also requires some data in ASoC PCM runtime data for looking up SOF data during ASoC PCM operations. Signed-off-by: Liam Girdwood --- include/sound/soc.h | 3 + include/sound/sof.h | 82 +++++++ sound/soc/sof/core.c | 383 +++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 517 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 985 insertions(+) create mode 100644 include/sound/sof.h create mode 100644 sound/soc/sof/core.c create mode 100644 sound/soc/sof/sof-priv.h diff --git a/include/sound/soc.h b/include/sound/soc.h index c24e2fad860201..198cfd5e990a9a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1132,6 +1132,9 @@ struct snd_soc_pcm_runtime { /* runtime devices */ struct snd_pcm *pcm; struct snd_compr *compr; + struct snd_sof_pcm *sof; + struct snd_soc_codec *codec; + struct snd_soc_platform *platform; /* will be removed */ struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; diff --git a/include/sound/sof.h b/include/sound/sof.h new file mode 100644 index 00000000000000..445d0eddc551fc --- /dev/null +++ b/include/sound/sof.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __INCLUDE_SOUND_SOF_H +#define __INCLUDE_SOUND_SOF_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_sof_dsp_ops; + +/* SOF probe type */ +enum sof_device_type { + SOF_DEVICE_PCI = 0, + SOF_DEVICE_APCI, + SOF_DEVICE_SPI +}; + +/* + * SOF Platform data. + */ +struct snd_sof_pdata { + u32 id; /* PCI/ACPI ID */ + const struct firmware *fw; + const char *drv_name; + const char *name; + + /* parent device */ + struct device *dev; + enum sof_device_type type; + + /* descriptor */ + const struct sof_dev_desc *desc; + + /* machine */ + struct platform_device *pdev_mach; + const struct snd_soc_acpi_mach *machine; +}; + +/* + * Descriptor used for setting up SOF platform data. This is used when + * ACPI/PCI data is missing or mapped differently. + */ +struct sof_dev_desc { + /* list of machines using this configuration */ + struct snd_soc_acpi_mach *machines; + + /* Platform resource indexes in BAR / ACPI resources. */ + /* Must set to -1 if not used - add new items to end */ + int resindex_lpe_base; + int resindex_pcicfg_base; + int resindex_imr_base; + int irqindex_host_ipc; + int resindex_dma_base; + + /* DMA only valid when resindex_dma_base != -1*/ + int dma_engine; + int dma_size; + + /* IPC timeouts in ms */ + int ipc_timeout; + int boot_timeout; + + /* defaults for no codec mode */ + const char *nocodec_fw_filename; + const char *nocodec_tplg_filename; +}; + +#endif diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c new file mode 100644 index 00000000000000..e6e40d4e7f3017 --- /dev/null +++ b/sound/soc/sof/core.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* SOF defaults if not provided by the platform in ms */ +#define TIMEOUT_DEFAULT_IPC 5 +#define TIMEOUT_DEFAULT_BOOT 100 + +/* + * Generic object lookup APIs. + */ + +struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (strcmp(spcm->pcm.dai_name, name) == 0) + return spcm; + + if (strcmp(spcm->pcm.caps[0].name, name) == 0) + return spcm; + + if (strcmp(spcm->pcm.caps[1].name, name) == 0) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id == + comp_id) { + *direction = SNDRV_PCM_STREAM_PLAYBACK; + return spcm; + } + if (spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id == comp_id) { + *direction = SNDRV_PCM_STREAM_CAPTURE; + return spcm; + } + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, + unsigned int pcm_id) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id) + return spcm; + } + + return NULL; +} + +struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_widget *swidget = NULL; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (strcmp(name, swidget->widget->name) == 0) + return swidget; + } + + return NULL; +} + +struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_dai *dai = NULL; + + list_for_each_entry(dai, &sdev->dai_list, list) { + if (!dai->name) + continue; + + if (strcmp(name, dai->name) == 0) + return dai; + } + + return NULL; +} + +static inline unsigned int sof_get_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + +/* + * FW Panic/fault handling. + */ + +struct sof_panic_msg { + u32 id; + const char *msg; +}; + +/* standard FW panic types */ +static const struct sof_panic_msg panic_msg[] = { + {SOF_IPC_PANIC_MEM, "out of memory"}, + {SOF_IPC_PANIC_WORK, "work subsystem init failed"}, + {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"}, + {SOF_IPC_PANIC_ARCH, "arch init failed"}, + {SOF_IPC_PANIC_PLATFORM, "platform init failed"}, + {SOF_IPC_PANIC_TASK, "scheduler init failed"}, + {SOF_IPC_PANIC_EXCEPTION, "runtime exception"}, + {SOF_IPC_PANIC_DEADLOCK, "deadlock"}, + {SOF_IPC_PANIC_STACK, "stack overflow"}, + {SOF_IPC_PANIC_IDLE, "can't enter idle"}, +}; + +int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, + u32 tracep_code, void *oops, void *stack, + size_t stack_words) +{ + u32 code; + int i; + + /* is firmware dead ? */ + if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { + dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n", + panic_code, tracep_code); + return 0; /* no fault ? */ + } + + code = panic_code & + (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK); + + for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { + if (panic_msg[i].id == code) { + dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg); + dev_err(sdev->dev, "error: trace point %8.8x\n", + tracep_code); + goto out; + } + } + + /* unknown error */ + dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code); + dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); + +out: + sof_oops(sdev, oops); + sof_stack(sdev, oops, stack, stack_words); + return -EFAULT; +} +EXPORT_SYMBOL(snd_sof_get_status); + +/* + * Generic buffer page table creation. + */ + +int snd_sof_create_page_table(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = snd_sgbuf_aligned_pages(size); + + dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + u32 idx = (((i << 2) + i)) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u32 *pg_table; + + dev_dbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u32 *)(page_table + idx); + + if (i & 1) + *pg_table |= (pfn << 4); + else + *pg_table |= pfn; + } + + return pages; +} + +/* + * SOF Driver enumeration. + */ + +static int sof_probe(struct platform_device *pdev) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(&pdev->dev); + struct snd_sof_dev *sdev; + int ret; + + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + dev_dbg(&pdev->dev, "probing SOF DSP device....\n"); + + /* initialize sof device */ + sdev->dev = &pdev->dev; + sdev->parent = plat_data->dev; + if (plat_data->type == SOF_DEVICE_PCI) + sdev->pci = container_of(plat_data->dev, struct pci_dev, dev); + sdev->ops = plat_data->machine->pdata; + + sdev->pdata = plat_data; + INIT_LIST_HEAD(&sdev->pcm_list); + INIT_LIST_HEAD(&sdev->kcontrol_list); + INIT_LIST_HEAD(&sdev->widget_list); + INIT_LIST_HEAD(&sdev->dai_list); + INIT_LIST_HEAD(&sdev->route_list); + dev_set_drvdata(&pdev->dev, sdev); + spin_lock_init(&sdev->ipc_lock); + spin_lock_init(&sdev->hw_lock); + + /* set up platform component driver */ + snd_sof_new_platform_drv(sdev); + + /* set default timeouts if none provided */ + if (plat_data->desc->ipc_timeout == 0) + sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC; + else + sdev->ipc_timeout = plat_data->desc->ipc_timeout; + if (plat_data->desc->boot_timeout == 0) + sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT; + else + sdev->boot_timeout = plat_data->desc->boot_timeout; + + /* probe the DSP hardware */ + ret = snd_sof_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); + return ret; + } + + /* register any debug/trace capabilities */ + ret = snd_sof_dbg_init(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n", + ret); + goto dbg_err; + } + + /* init the IPC */ + sdev->ipc = snd_sof_ipc_init(sdev); + if (!sdev->ipc) { + dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); + goto ipc_err; + } + + /* load the firmware */ + ret = snd_sof_load_firmware(sdev, plat_data->fw, true); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", + ret); + goto fw_load_err; + } + + /* boot the firmware */ + ret = snd_sof_run_firmware(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", + ret); + goto fw_run_err; + } + + /* now register audio DSP platform driver and dai */ + ret = snd_soc_register_component(&pdev->dev, &sdev->plat_drv, + sdev->ops->drv, + sdev->ops->num_drv); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to register DSP DAI driver %d\n", ret); + goto comp_err; + } + + /* init DMA trace */ + ret = snd_sof_init_trace(sdev); + if (ret < 0) { + /* non fatal */ + dev_warn(sdev->dev, + "warning: failed to initialize trace %d\n", ret); + } + + /* autosuspend sof device */ + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + + /* autosuspend pci/acpi/spi device */ + pm_runtime_mark_last_busy(plat_data->dev); + pm_runtime_put_autosuspend(plat_data->dev); + + return 0; + +comp_err: + snd_soc_unregister_component(&pdev->dev); + snd_sof_free_topology(sdev); +fw_run_err: + snd_sof_fw_unload(sdev); +fw_load_err: + snd_sof_ipc_free(sdev); +ipc_err: + snd_sof_free_debug(sdev); +dbg_err: + snd_sof_remove(sdev); + + return ret; +} + +static int sof_remove(struct platform_device *pdev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + snd_sof_fw_unload(sdev); + snd_sof_ipc_free(sdev); + snd_sof_free_debug(sdev); + snd_sof_release_trace(sdev); + snd_sof_remove(sdev); + return 0; +} + +void snd_sof_shutdown(struct device *dev) +{ +} +EXPORT_SYMBOL(snd_sof_shutdown); + +static struct platform_driver sof_driver = { + .driver = { + .name = "sof-audio", + }, + + .probe = sof_probe, + .remove = sof_remove, +}; +module_platform_driver(sof_driver); + +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:sof-audio"); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h new file mode 100644 index 00000000000000..42020c0aeeb3da --- /dev/null +++ b/sound/soc/sof/sof-priv.h @@ -0,0 +1,517 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOUND_SOC_SOF_PRIV_H +#define __SOUND_SOC_SOF_PRIV_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* debug flags */ +#define SOF_DBG_REGS BIT(1) +#define SOF_DBG_MBOX BIT(2) +#define SOF_DBG_TEXT BIT(3) +#define SOF_DBG_PCI BIT(4) + +/* max BARs mmaped devices can use */ +#define SND_SOF_BARS 8 + +/* time in ms for runtime suspend delay */ +#define SND_SOF_SUSPEND_DELAY 2000 + +/* DMA buffer size for trace */ +#define DMA_BUF_SIZE_FOR_TRACE (PAGE_SIZE * 16) + +/* max number of FE PCMs before BEs */ +#define SOF_BE_PCM_BASE 16 + +#define SOF_IPC_DSP_REPLY 0 +#define SOF_IPC_HOST_REPLY 1 + +/* convenience constructor for DAI driver streams */ +#define SOF_DAI_STREAM(sname, scmin, scmax, srates, sfmt) \ + {.stream_name = sname, .channels_min = scmin, .channels_max = scmax, \ + .rates = srates, .formats = sfmt} + +#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) + +struct snd_sof_dev; +struct snd_sof_ipc_msg; +struct snd_sof_ipc; +struct snd_sof_debugfs_map; +struct snd_soc_tplg_ops; +struct snd_soc_component; +struct sof_intel_hda_dev; +struct snd_sof_pdata; + +/* + * SOF DSP HW abstraction operations. + * Used to abstract DSP HW architecture and any IO busses between host CPU + * and DSP device(s). + */ +struct snd_sof_dsp_ops { + /* probe and remove */ + int (*remove)(struct snd_sof_dev *sof_dev); + int (*probe)(struct snd_sof_dev *sof_dev); + + /* DSP core boot / reset */ + int (*run)(struct snd_sof_dev *sof_dev); + int (*stall)(struct snd_sof_dev *sof_dev); + int (*reset)(struct snd_sof_dev *sof_dev); + + /* DSP PM */ + int (*suspend)(struct snd_sof_dev *sof_dev, int state); + int (*resume)(struct snd_sof_dev *sof_dev); + + /* DSP clocking */ + int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); + + /* Register IO */ + void (*write)(struct snd_sof_dev *sof_dev, void __iomem *addr, + u32 value); + u32 (*read)(struct snd_sof_dev *sof_dev, void __iomem *addr); + void (*write64)(struct snd_sof_dev *sof_dev, void __iomem *addr, + u64 value); + u64 (*read64)(struct snd_sof_dev *sof_dev, void __iomem *addr); + + /* memcpy IO */ + void (*block_read)(struct snd_sof_dev *sof_dev, + u32 offset, void *dest, size_t size); + void (*block_write)(struct snd_sof_dev *sof_dev, + u32 offset, void *src, size_t size); + + /* doorbell */ + irqreturn_t (*irq_handler)(int irq, void *context); + irqreturn_t (*irq_thread)(int irq, void *context); + + /* mailbox */ + void (*mailbox_read)(struct snd_sof_dev *sof_dev, u32 offset, + void *addr, size_t bytes); + void (*mailbox_write)(struct snd_sof_dev *sof_dev, u32 offset, + void *addr, size_t bytes); + + /* ipc */ + int (*send_msg)(struct snd_sof_dev *sof_dev, + struct snd_sof_ipc_msg *msg); + int (*get_reply)(struct snd_sof_dev *sof_dev, + struct snd_sof_ipc_msg *msg); + int (*is_ready)(struct snd_sof_dev *sof_dev); + int (*cmd_done)(struct snd_sof_dev *sof_dev, int dir); + + /* debug */ + const struct snd_sof_debugfs_map *debug_map; + int debug_map_count; + void (*dbg_dump)(struct snd_sof_dev *sof_dev, u32 flags); + + /* connect pcm substream to a host stream */ + int (*pcm_open)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); + /* disconnect pcm substream to a host stream */ + int (*pcm_close)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); + + /* host stream hw params */ + int (*pcm_hw_params)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + + /* host stream trigger */ + int (*pcm_trigger)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd); + + /* FW loading */ + int (*load_firmware)(struct snd_sof_dev *sof_dev, + const struct firmware *fw, bool first_boot); + int (*load_module)(struct snd_sof_dev *sof_dev, + struct snd_sof_mod_hdr *hdr); + int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); + + /* host DMA trace initialization */ + int (*trace_init)(struct snd_sof_dev *sdev, u32 *stream_tag); + int (*trace_release)(struct snd_sof_dev *sdev); + int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); + + /* DAI ops */ + struct snd_soc_dai_driver *drv; + int num_drv; +}; + +/* DSP architecture specific callbacks for oops and stack dumps */ +struct sof_arch_ops { + void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); + void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, + u32 *stack, u32 stack_words); +}; + +/* DSP device HW descriptor mapping between bus ID and ops */ +struct sof_ops_table { + const struct sof_dev_desc *desc; + struct snd_sof_dsp_ops *ops; + struct platform_device *(*new_data)(struct snd_sof_pdata *pdata); +}; + +/* FS entry for debug files that can expose DSP memories, registers */ +struct snd_sof_dfsentry_io { + struct dentry *dfsentry; + size_t size; + void __iomem *buf; + struct snd_sof_dev *sdev; +}; + +struct snd_sof_dfsentry_buf { + struct dentry *dfsentry; + size_t size; + void *buf; + struct snd_sof_dev *sdev; +}; + +/* Debug mapping for any DSP memory or registers that can used for debug */ +struct snd_sof_debugfs_map { + const char *name; + u32 bar; + u32 offset; + u32 size; +}; + +/* mailbox descriptor, used for host <-> DSP IPC */ +struct snd_sof_mailbox { + u32 offset; + size_t size; +}; + +/* IPC message descriptor for host <-> DSP IO */ +struct snd_sof_ipc_msg { + struct list_head list; + + /* message data */ + u32 header; + void *msg_data; + void *reply_data; + size_t msg_size; + size_t reply_size; + + wait_queue_head_t waitq; + bool complete; +}; + +/* PCM stream, mapped to FW component */ +struct snd_sof_pcm_stream { + u32 comp_id; + struct snd_dma_buffer page_table; + struct sof_ipc_stream_posn posn; + struct snd_pcm_substream *substream; +}; + +/* ASLA SOF PCM device */ +struct snd_sof_pcm { + struct snd_sof_dev *sdev; + struct snd_soc_tplg_pcm pcm; + struct snd_sof_pcm_stream stream[2]; + u32 posn_offset[2]; + struct mutex mutex; /* access mutex */ + struct list_head list; /* list in sdev pcm list */ + struct snd_pcm_hw_params params[2]; + int restore_stream[2]; /* restore hw_params for paused stream */ +}; + +/* ALSA SOF Kcontrol device */ +struct snd_sof_control { + struct snd_sof_dev *sdev; + int comp_id; + int num_channels; + u32 readback_offset; /* offset to mmaped data if used */ + struct sof_ipc_ctrl_data *control_data; + u32 size; /* cdata size */ + enum sof_ipc_ctrl_cmd cmd; + + struct mutex mutex; /* access mutex */ + struct list_head list; /* list in sdev control list */ +}; + +/* ASoC SOF DAPM widget */ +struct snd_sof_widget { + struct snd_sof_dev *sdev; + int comp_id; + int pipeline_id; + int complete; + int id; + + struct snd_soc_dapm_widget *widget; + struct mutex mutex; /* access mutex */ + struct list_head list; /* list in sdev widget list */ + + void *private; /* core does not touch this */ +}; + +/* ASoC SOF DAPM route */ +struct snd_sof_route { + struct snd_sof_dev *sdev; + + struct snd_soc_dapm_route *route; + struct list_head list; /* list in sdev route list */ + + void *private; +}; + +/* ASoC DAI device */ +struct snd_sof_dai { + struct snd_sof_dev *sdev; + const char *name; + + struct sof_ipc_comp_dai comp_dai; + struct sof_ipc_dai_config dai_config; + struct list_head list; /* list in sdev dai list */ +}; + +/* + * SOF Device Level. + */ +struct snd_sof_dev { + struct device *dev; + struct device *parent; + spinlock_t ipc_lock; /* lock for IPC users */ + spinlock_t hw_lock; /* lock for HW IO access */ + struct pci_dev *pci; + + /* ASoC components */ + struct snd_soc_component_driver plat_drv; + + /* DSP firmware boot */ + wait_queue_head_t boot_wait; + bool boot_complete; + + /* DSP HW differentiation */ + struct snd_sof_pdata *pdata; + const struct snd_sof_dsp_ops *ops; + struct sof_intel_hda_dev *hda; /* for HDA based DSP HW */ + const struct sof_arch_ops *arch_ops; + + /* IPC */ + struct snd_sof_ipc *ipc; + struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */ + struct snd_sof_mailbox host_box; /* Host initiated IPC */ + struct snd_sof_mailbox stream_box; /* Stream position update */ + u64 irq_status; + int ipc_irq; + u32 next_comp_id; /* monotonic - reset during S3 */ + + /* memory bases for mmaped DSPs - set by dsp_init() */ + void __iomem *bar[SND_SOF_BARS]; /* DSP base address */ + int mmio_bar; + int mailbox_bar; + size_t dsp_oops_offset; + + /* debug */ + struct dentry *debugfs_root; + + /* firmware loader */ + int cl_bar; + struct snd_dma_buffer dmab; + struct sof_ipc_fw_ready fw_ready; + + /* topology */ + struct snd_soc_tplg_ops *tplg_ops; + struct list_head pcm_list; + struct list_head kcontrol_list; + struct list_head widget_list; + struct list_head dai_list; + struct list_head route_list; + struct snd_soc_component *component; + + /* FW configuration */ + struct sof_ipc_dma_buffer_data *info_buffer; + struct sof_ipc_window *info_window; + + /* IPC timeouts in ms */ + int ipc_timeout; + int boot_timeout; + + /* Wait queue for code loading */ + wait_queue_head_t waitq; + int code_loading; + + /* DMA for Trace */ + struct snd_dma_buffer dmatb; + struct snd_dma_buffer dmatp; + int dma_trace_pages; + wait_queue_head_t trace_sleep; + u32 host_offset; + bool dtrace_is_enabled; + + void *private; /* core does not touch this */ +}; + +/* + * SOF platform private struct used as drvdata of + * platform dev (e.g. pci/acpi/spi...) drvdata. + */ +struct sof_platform_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +/* + * Device Level. + */ +void snd_sof_shutdown(struct device *dev); +int snd_sof_runtime_suspend(struct device *dev); +int snd_sof_runtime_resume(struct device *dev); +int snd_sof_resume(struct device *dev); +int snd_sof_suspend(struct device *dev); +int snd_sof_suspend_late(struct device *dev); + +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); + +int snd_sof_create_page_table(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size); + +/* + * Firmware loading. + */ +int snd_sof_load_firmware(struct snd_sof_dev *sdev, + const struct firmware *fw, bool first_boot); +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, + const struct firmware *fw, bool first_boot); +int snd_sof_run_firmware(struct snd_sof_dev *sdev); +int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, + struct snd_sof_mod_hdr *module); +void snd_sof_fw_unload(struct snd_sof_dev *sdev); +int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset); + +/* + * IPC low level APIs. + */ +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); +void snd_sof_ipc_free(struct snd_sof_dev *sdev); +int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev); +int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, + struct sof_ipc_pcm_params *params); +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size); +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *tx_data, + size_t tx_bytes, void *rx_data, size_t rx_bytes); +struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd); +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction); +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, + unsigned int pcm_id); +void sof_ipc_drop_all(struct snd_sof_ipc *ipc); + +/* + * Stream IPC + */ +int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, int direction, + struct sof_ipc_stream_posn *posn); + +/* + * Mixer IPC + */ +int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd); +int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd); + +/* + * Topology. + */ +int snd_sof_init_topology(struct snd_sof_dev *sdev, + struct snd_soc_tplg_ops *ops); +int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file); +void snd_sof_free_topology(struct snd_sof_dev *sdev); +int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget); + +/* + * Trace/debug + */ +int snd_sof_init_trace(struct snd_sof_dev *sdev); +void snd_sof_release_trace(struct snd_sof_dev *sdev); +int snd_sof_dbg_init(struct snd_sof_dev *sdev); +void snd_sof_free_debug(struct snd_sof_dev *sdev); +int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, + void __iomem *base, size_t size, + const char *name); +int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn); +void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); +int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, + u32 tracep_code, void *oops, void *stack, + size_t stack_size); +int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); + +/* + * Platform specific ops. + */ +extern struct snd_compr_ops sof_compressed_ops; + +/* + * Kcontrols. + */ + +int snd_sof_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +/* + * DSP Architectures. + */ +static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, + u32 stack_words) +{ + if (sdev->arch_ops->dsp_stack) + sdev->arch_ops->dsp_stack(sdev, oops, stack, stack_words); +} + +static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) +{ + if (sdev->arch_ops->dsp_oops) + sdev->arch_ops->dsp_oops(sdev, oops); +} + +extern const struct sof_arch_ops sof_xtensa_arch_ops; +#endif From a5ba292f2be6b5ebabd5a8029254bac5d9093dcf Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:31:31 +0000 Subject: [PATCH 019/298] ASoC: SOF: Add Sound Open Firmware KControl support SOF exposes regular ALSA Kcontrols that are defined by topology. This patch converts the Kcontrol IO to DSP IPC. Signed-off-by: Liam Girdwood --- sound/soc/sof/control.c | 218 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 sound/soc/sof/control.c diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c new file mode 100644 index 00000000000000..5effaea6fe5f97 --- /dev/null +++ b/sound/soc/sof/control.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* Mixer Controls */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) +{ + if (value >= size) + return volume_map[size - 1]; + else + return volume_map[value]; +} + +static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (volume_map[i] >= value) + return i; + } + + return i - 1; +} + +int snd_sof_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_VOLUME); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = + ipc_to_mixer(cdata->chanv[i].value, + scontrol->volume_table, sm->max + 1); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* update each channel */ + for (i = 0; i < channels; i++) { + cdata->chanv[i].value = + mixer_to_ipc(ucontrol->value.integer.value[i], + scontrol->volume_table, sm->max + 1); + cdata->chanv[i].channel = i; + } + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_VOLUME); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; + struct snd_sof_control *scontrol = se->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_ENUM); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; + struct snd_sof_control *scontrol = se->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* update each channel */ + for (i = 0; i < channels; i++) + cdata->chanv[i].value = ucontrol->value.integer.value[i]; + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + SOF_CTRL_CMD_ENUM); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_abi_hdr *data = cdata->data; + size_t size; + int ret = 0; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_DATA, + SOF_CTRL_TYPE_DATA_GET, scontrol->cmd); + size = data->size + sizeof(*data); + if (size > be->max) { + dev_err(sdev->dev, "error: DSP sent %zu bytes max is %d\n", + size, be->max); + ret = -EINVAL; + goto out; + } + + /* copy back to kcontrol */ + memcpy(ucontrol->value.bytes.data, data, size); + +out: + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return ret; +} + +int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_abi_hdr *data = cdata->data; + int ret = 0; + + pm_runtime_get_sync(sdev->dev); + + if (data->size > be->max) { + dev_err(sdev->dev, "error: size too big %d bytes max is %d\n", + data->size, be->max); + ret = -EINVAL; + goto out; + } + + /* copy from kcontrol */ + memcpy(data, ucontrol->value.bytes.data, data->size); + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_DATA, + SOF_CTRL_TYPE_DATA_SET, scontrol->cmd); + +out: + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return ret; +} From dc4e3584b467ee970802d6a705ed1295fda58699 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:31:44 +0000 Subject: [PATCH 020/298] ASoC: SOF: Add driver debug support. Add debugFS files that can be used to expose DSP memories and and peripherals to userspace to assist with firmware debugging. Signed-off-by: Liam Girdwood --- sound/soc/sof/debug.c | 147 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 sound/soc/sof/debug.c diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c new file mode 100644 index 00000000000000..c604decd3a6615 --- /dev/null +++ b/sound/soc/sof/debug.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Yan Wang + * + * Generic debug routines used to export DSP MMIO and memories to userspace + * for firmware debugging. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int sof_dfsentry_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry_io *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + int size; + u32 *buf; + loff_t pos = *ppos; + size_t ret; + + size = dfse->size; + + /* validate position & count */ + if (pos < 0) + return -EINVAL; + if (pos >= size || !count) + return 0; + if (count > size - pos) + count = size - pos; + + /* intermediate buffer size must be u32 multiple */ + size = (count + 3) & ~3; + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* copy from DSP MMIO */ + pm_runtime_get(sdev->dev); + memcpy_fromio(buf, dfse->buf + pos, size); + pm_runtime_put(sdev->dev); + + /* copy to userspace */ + ret = copy_to_user(buffer, buf, count); + kfree(buf); + + /* update count & position if copy succeeded */ + if (ret == count) + return -EFAULT; + count -= ret; + *ppos = pos + count; + + return count; +} + +static const struct file_operations sof_dfs_fops = { + .open = sof_dfsentry_open, + .read = sof_dfsentry_read, + .llseek = default_llseek, +}; + +int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, + void __iomem *base, size_t size, + const char *name) +{ + struct snd_sof_dfsentry_io *dfse; + + if (!sdev) + return -EINVAL; + + dfse = kzalloc(sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->buf = base; + dfse->size = size; + dfse->sdev = sdev; + + dfse->dfsentry = debugfs_create_file(name, 0444, sdev->debugfs_root, + dfse, &sof_dfs_fops); + if (!dfse->dfsentry) { + dev_err(sdev->dev, "cannot create debugfs entry.\n"); + kfree(dfse); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_debugfs_create_item); + +int snd_sof_dbg_init(struct snd_sof_dev *sdev) +{ + const struct snd_sof_dsp_ops *ops = sdev->ops; + const struct snd_sof_debugfs_map *map; + int err = 0, i; + + /* use "sof" as top level debugFS dir */ + sdev->debugfs_root = debugfs_create_dir("sof", NULL); + if (IS_ERR_OR_NULL(sdev->debugfs_root)) { + dev_err(sdev->dev, "error: failed to create debugfs directory\n"); + return -EINVAL; + } + + /* create debugFS files for platform specific MMIO/DSP memories */ + for (i = 0; i < ops->debug_map_count; i++) { + map = &ops->debug_map[i]; + + err = snd_sof_debugfs_create_item(sdev, sdev->bar[map->bar] + + map->offset, map->size, + map->name); + if (err < 0) + dev_err(sdev->dev, "cannot create debugfs for %s\n", + map->name); + } + + return err; +} +EXPORT_SYMBOL(snd_sof_dbg_init); + +void snd_sof_free_debug(struct snd_sof_dev *sdev) +{ + debugfs_remove_recursive(sdev->debugfs_root); +} +EXPORT_SYMBOL(snd_sof_free_debug); From 66f963da190970f28731a2144d33ab5c3107c111 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:32:12 +0000 Subject: [PATCH 021/298] ASoC: SOF: Add support for IPC IO between DSP and Host Define an IPC ABI for all host <--> DSP communication. This ABI should be transport agnostic. i.e. it should work on MMIO and SPI/I2C style interfaces. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-ipc.h | 938 +++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc.c | 788 +++++++++++++++++++++++++++++ 2 files changed, 1726 insertions(+) create mode 100644 include/uapi/sound/sof-ipc.h create mode 100644 sound/soc/sof/ipc.c diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h new file mode 100644 index 00000000000000..b520e3b24ed6c7 --- /dev/null +++ b/include/uapi/sound/sof-ipc.h @@ -0,0 +1,938 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ + +#ifndef __INCLUDE_UAPI_SOF_IPC_H__ +#define __INCLUDE_UAPI_SOF_IPC_H__ + +#include + +/* + * IPC messages have a prefixed 32 bit identifier made up as follows :- + * + * 0xGCCCNNNN where + * G is global cmd type (4 bits) + * C is command type (12 bits) + * I is the ID number (16 bits) - monotonic and overflows + * + * This is sent at the start of the IPM message in the mailbox. Messages should + * not be sent in the doorbell (special exceptions for firmware . + */ + +/* Global Message - Generic */ +#define SOF_GLB_TYPE_SHIFT 28 +#define SOF_GLB_TYPE_MASK (0xf << SOF_GLB_TYPE_SHIFT) +#define SOF_GLB_TYPE(x) ((x) << SOF_GLB_TYPE_SHIFT) + +/* Command Message - Generic */ +#define SOF_CMD_TYPE_SHIFT 16 +#define SOF_CMD_TYPE_MASK (0xfff << SOF_CMD_TYPE_SHIFT) +#define SOF_CMD_TYPE(x) ((x) << SOF_CMD_TYPE_SHIFT) + +/* Global Message Types */ +#define SOF_IPC_GLB_REPLY SOF_GLB_TYPE(0x1U) +#define SOF_IPC_GLB_COMPOUND SOF_GLB_TYPE(0x2U) +#define SOF_IPC_GLB_TPLG_MSG SOF_GLB_TYPE(0x3U) +#define SOF_IPC_GLB_PM_MSG SOF_GLB_TYPE(0x4U) +#define SOF_IPC_GLB_COMP_MSG SOF_GLB_TYPE(0x5U) +#define SOF_IPC_GLB_STREAM_MSG SOF_GLB_TYPE(0x6U) +#define SOF_IPC_FW_READY SOF_GLB_TYPE(0x7U) +#define SOF_IPC_GLB_DAI_MSG SOF_GLB_TYPE(0x8U) +#define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U) + +/* + * DSP Command Message Types + */ + +/* topology */ +#define SOF_IPC_TPLG_COMP_NEW SOF_CMD_TYPE(0x001) +#define SOF_IPC_TPLG_COMP_FREE SOF_CMD_TYPE(0x002) +#define SOF_IPC_TPLG_COMP_CONNECT SOF_CMD_TYPE(0x003) +#define SOF_IPC_TPLG_PIPE_NEW SOF_CMD_TYPE(0x010) +#define SOF_IPC_TPLG_PIPE_FREE SOF_CMD_TYPE(0x011) +#define SOF_IPC_TPLG_PIPE_CONNECT SOF_CMD_TYPE(0x012) +#define SOF_IPC_TPLG_PIPE_COMPLETE SOF_CMD_TYPE(0x013) +#define SOF_IPC_TPLG_BUFFER_NEW SOF_CMD_TYPE(0x020) +#define SOF_IPC_TPLG_BUFFER_FREE SOF_CMD_TYPE(0x021) + +/* PM */ +#define SOF_IPC_PM_CTX_SAVE SOF_CMD_TYPE(0x001) +#define SOF_IPC_PM_CTX_RESTORE SOF_CMD_TYPE(0x002) +#define SOF_IPC_PM_CTX_SIZE SOF_CMD_TYPE(0x003) +#define SOF_IPC_PM_CLK_SET SOF_CMD_TYPE(0x004) +#define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005) +#define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006) + +/* component runtime config - multiple different types */ +#define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001) +#define SOF_IPC_COMP_GET_VALUE SOF_CMD_TYPE(0x002) +#define SOF_IPC_COMP_SET_DATA SOF_CMD_TYPE(0x003) +#define SOF_IPC_COMP_GET_DATA SOF_CMD_TYPE(0x004) + +/* DAI messages */ +#define SOF_IPC_DAI_CONFIG SOF_CMD_TYPE(0x001) +#define SOF_IPC_DAI_LOOPBACK SOF_CMD_TYPE(0x002) + +/* stream */ +#define SOF_IPC_STREAM_PCM_PARAMS SOF_CMD_TYPE(0x001) +#define SOF_IPC_STREAM_PCM_PARAMS_REPLY SOF_CMD_TYPE(0x002) +#define SOF_IPC_STREAM_PCM_FREE SOF_CMD_TYPE(0x003) +#define SOF_IPC_STREAM_TRIG_START SOF_CMD_TYPE(0x004) +#define SOF_IPC_STREAM_TRIG_STOP SOF_CMD_TYPE(0x005) +#define SOF_IPC_STREAM_TRIG_PAUSE SOF_CMD_TYPE(0x006) +#define SOF_IPC_STREAM_TRIG_RELEASE SOF_CMD_TYPE(0x007) +#define SOF_IPC_STREAM_TRIG_DRAIN SOF_CMD_TYPE(0x008) +#define SOF_IPC_STREAM_TRIG_XRUN SOF_CMD_TYPE(0x009) +#define SOF_IPC_STREAM_POSITION SOF_CMD_TYPE(0x00a) +#define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010) +#define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011) + +/* trace and debug */ +#define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001) +#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) + +/* Get message component id */ +#define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff) + +/* maximum message size for mailbox Tx/Rx */ +#define SOF_IPC_MSG_MAX_SIZE 128 + +/* + * SOF panic codes + */ +#define SOF_IPC_PANIC_MAGIC 0x0dead000 +#define SOF_IPC_PANIC_MAGIC_MASK 0x0ffff000 +#define SOF_IPC_PANIC_CODE_MASK 0x00000fff +#define SOF_IPC_PANIC_MEM (SOF_IPC_PANIC_MAGIC | 0) +#define SOF_IPC_PANIC_WORK (SOF_IPC_PANIC_MAGIC | 1) +#define SOF_IPC_PANIC_IPC (SOF_IPC_PANIC_MAGIC | 2) +#define SOF_IPC_PANIC_ARCH (SOF_IPC_PANIC_MAGIC | 3) +#define SOF_IPC_PANIC_PLATFORM (SOF_IPC_PANIC_MAGIC | 4) +#define SOF_IPC_PANIC_TASK (SOF_IPC_PANIC_MAGIC | 5) +#define SOF_IPC_PANIC_EXCEPTION (SOF_IPC_PANIC_MAGIC | 6) +#define SOF_IPC_PANIC_DEADLOCK (SOF_IPC_PANIC_MAGIC | 7) +#define SOF_IPC_PANIC_STACK (SOF_IPC_PANIC_MAGIC | 8) +#define SOF_IPC_PANIC_IDLE (SOF_IPC_PANIC_MAGIC | 9) + +/* + * SOF memory capabilities, add new ones at the end + */ +#define SOF_MEM_CAPS_RAM (1 << 0) +#define SOF_MEM_CAPS_ROM (1 << 1) +#define SOF_MEM_CAPS_EXT (1 << 2) /* external */ +#define SOF_MEM_CAPS_LP (1 << 3) /* low power */ +#define SOF_MEM_CAPS_HP (1 << 4) /* high performance */ +#define SOF_MEM_CAPS_DMA (1 << 5) /* DMA'able */ +#define SOF_MEM_CAPS_CACHE (1 << 6) /* cacheable */ +#define SOF_MEM_CAPS_EXEC (1 << 7) /* executable */ + +/* + * Command Header - Header for all IPC. Identifies IPC message. + * The size can be greater than the structure size and that means there is + * extended bespoke data beyond the end of the structure including variable + * arrays. + */ + +struct sof_ipc_hdr { + uint32_t cmd; /* SOF_IPC_GLB_ + cmd */ + uint32_t size; /* size of structure */ +} __attribute__((packed)); + +/* + * Generic reply message. Some commands override this with their own reply + * types that must include this at start. + */ +struct sof_ipc_reply { + struct sof_ipc_hdr hdr; + int32_t error; /* negative error numbers */ +} __attribute__((packed)); + +/* + * Compound commands - SOF_IPC_GLB_COMPOUND. + * + * Compound commands are sent to the DSP as a single IPC operation. The + * commands are split into blocks and each block has a header. This header + * identifies the command type and the number of commands before the next + * header. + */ + +struct sof_ipc_compound_hdr { + struct sof_ipc_hdr hdr; + uint32_t count; /* count of 0 means end of compound sequence */ +} __attribute__((packed)); + +/* + * DAI Configuration. + * + * Each different DAI type will have it's own structure and IPC cmd. + */ + +#define SOF_DAI_FMT_I2S 1 /* I2S mode */ +#define SOF_DAI_FMT_RIGHT_J 2 /* Right Justified mode */ +#define SOF_DAI_FMT_LEFT_J 3 /* Left Justified mode */ +#define SOF_DAI_FMT_DSP_A 4 /* L data MSB after FRM LRC */ +#define SOF_DAI_FMT_DSP_B 5 /* L data MSB during FRM LRC */ +#define SOF_DAI_FMT_PDM 6 /* Pulse density modulation */ + +#define SOF_DAI_FMT_CONT (1 << 4) /* continuous clock */ +#define SOF_DAI_FMT_GATED (0 << 4) /* clock is gated */ + +#define SOF_DAI_FMT_NB_NF (0 << 8) /* normal bit clock + frame */ +#define SOF_DAI_FMT_NB_IF (2 << 8) /* normal BCLK + inv FRM */ +#define SOF_DAI_FMT_IB_NF (3 << 8) /* invert BCLK + nor FRM */ +#define SOF_DAI_FMT_IB_IF (4 << 8) /* invert BCLK + FRM */ + +#define SOF_DAI_FMT_CBM_CFM (0 << 12) /* codec clk & FRM master */ +#define SOF_DAI_FMT_CBS_CFM (2 << 12) /* codec clk slave & FRM master */ +#define SOF_DAI_FMT_CBM_CFS (3 << 12) /* codec clk master & frame slave */ +#define SOF_DAI_FMT_CBS_CFS (4 << 12) /* codec clk & FRM slave */ + +#define SOF_DAI_FMT_FORMAT_MASK 0x000f +#define SOF_DAI_FMT_CLOCK_MASK 0x00f0 +#define SOF_DAI_FMT_INV_MASK 0x0f00 +#define SOF_DAI_FMT_MASTER_MASK 0xf000 + + /* ssc1: TINTE */ +#define SOF_DAI_INTEL_SSP_QUIRK_TINTE (1 << 0) + /* ssc1: PINTE */ +#define SOF_DAI_INTEL_SSP_QUIRK_PINTE (1 << 1) + /* ssc2: SMTATF */ +#define SOF_DAI_INTEL_SSP_QUIRK_SMTATF (1 << 2) + /* ssc2: MMRATF */ +#define SOF_DAI_INTEL_SSP_QUIRK_MMRATF (1 << 3) + /* ssc2: PSPSTWFDFD */ +#define SOF_DAI_INTEL_SSP_QUIRK_PSPSTWFDFD (1 << 4) + /* ssc2: PSPSRWFDFD */ +#define SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD (1 << 5) + /* here is the possibility to define others aux macros */ + +#define SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX 38 +#define SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX 31 + +/** \brief Types of DAI */ +enum sof_ipc_dai_type { + SOF_DAI_INTEL_NONE = 0, /**< None */ + SOF_DAI_INTEL_SSP, /**< Intel SSP */ + SOF_DAI_INTEL_DMIC, /**< Intel DMIC */ + SOF_DAI_INTEL_HDA, /**< Intel HD/A */ +}; + +/* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ +struct sof_ipc_dai_ssp_params { + uint16_t mode; // FIXME: do we need this? + uint16_t mclk_id; + + uint32_t mclk_rate; /* mclk frequency in Hz */ + uint32_t fsync_rate; /* fsync frequency in Hz */ + uint32_t bclk_rate; /* bclk frequency in Hz */ + + /* TDM */ + uint32_t tdm_slots; + uint32_t rx_slots; + uint32_t tx_slots; + + /* data */ + uint32_t sample_valid_bits; + uint16_t tdm_slot_width; + uint16_t reserved2; /* alignment */ + + /* MCLK */ + uint32_t mclk_direction; + uint32_t mclk_keep_active; + uint32_t bclk_keep_active; + uint32_t fs_keep_active; + + uint16_t frame_pulse_width; + uint32_t quirks; // FIXME: is 32 bits enough ? + + uint16_t tdm_per_slot_padding_flag; + /* private data, e.g. for quirks */ + //uint32_t pdata[10]; // FIXME: would really need ~16 u32 +} __attribute__((packed)); + +/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */ +struct sof_ipc_dai_hda_params { + struct sof_ipc_hdr hdr; + /* TODO */ +} __attribute__((packed)); + +/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ + +/* This struct is defined per 2ch PDM controller available in the platform. + * Normally it is sufficient to set the used microphone specific enables to 1 + * and keep other parameters as zero. The customizations are: + * + * 1. If a device mixes different microphones types with different polarity + * and/or the absolute polarity matters the PCM signal from a microphone + * can be inverted with the controls. + * + * 2. If the microphones in a stereo pair do not appear in captured stream + * in desired order due to board schematics choises they can be swapped with + * the clk_edge parameter. + * + * 3. If PDM bit errors are seen in capture (poor quality) the skew parameter + * that delays the sampling time of data by half cycles of DMIC source clock + * can be tried for improvement. However there is no guarantee for this to fix + * data integrity problems. + */ +struct sof_ipc_dai_dmic_pdm_ctrl { + uint16_t id; /* PDM controller ID */ + uint16_t enable_mic_a; /* Use A (left) channel mic (0 or 1)*/ + uint16_t enable_mic_b; /* Use B (right) channel mic (0 or 1)*/ + uint16_t polarity_mic_a; /* Optionally invert mic A signal (0 or 1) */ + uint16_t polarity_mic_b; /* Optionally invert mic B signal (0 or 1) */ + uint16_t clk_edge; /* Optionally swap data clock edge (0 or 1) */ + uint16_t skew; /* Adjust PDM data sampling vs. clock (0..15) */ + uint16_t pad; /* Make sure the total size is 4 bytes aligned */ +} __attribute__((packed)); + +/* This struct contains the global settings for all 2ch PDM controllers. The + * version number used in configuration data is checked vs. version used by + * device driver src/drivers/dmic.c need to match. It is incremented from + * initial value 1 if updates done for the to driver would alter the operation + * of the microhone. + * + * Note: The microphone clock (pdmclk_min, pdmclk_max, duty_min, duty_max) + * parameters need to be set as defined in microphone data sheet. E.g. clock + * range 1.0 - 3.2 MHz is usually supported microphones. Some microphones are + * multi-mode capable and there may be denied mic clock frequencies between + * the modes. In such case set the clock range limits of the desired mode to + * avoid the driver to set clock to an illegal rate. + * + * The duty cycle could be set to 48-52% if not known. Generally these + * parameters can be altered within data sheet specified limits to match + * required audio application performance power. + * + * The microphone clock needs to be usually about 50-80 times the used audio + * sample rate. With highest sample rates above 48 kHz this can relaxed + * somewhat. + */ +struct sof_ipc_dai_dmic_params { + uint32_t driver_ipc_version; /* Version (1..N) */ + uint32_t pdmclk_min; /* Minimum microphone clock in Hz (100000..N) */ + uint32_t pdmclk_max; /* Maximum microphone clock in Hz (min...N) */ + uint32_t fifo_fs_a; /* FIFO A sample rate in Hz (8000..96000) */ + uint32_t fifo_fs_b; /* FIFO B sample rate in Hz (8000..96000) */ + uint16_t fifo_bits_a; /* FIFO A word length (16 or 32) */ + uint16_t fifo_bits_b; /* FIFO B word length (16 or 32) */ + uint16_t duty_min; /* Min. mic clock duty cycle in % (20..80) */ + uint16_t duty_max; /* Max. mic clock duty cycle in % (min..80) */ + uint32_t num_pdm_active; /* Number of active pdm controllers */ + /* variable number of pdm controller config */ + struct sof_ipc_dai_dmic_pdm_ctrl pdm[0]; +} __attribute__((packed)); + +/* general purpose DAI configuration */ +struct sof_ipc_dai_config { + struct sof_ipc_hdr hdr; + enum sof_ipc_dai_type type; + uint32_t dai_index; /* index of this type dai */ + + /* physical protocol and clocking */ + uint16_t format; /* SOF_DAI_FMT_ */ + uint16_t reserved; /* alignment */ + + /* HW specific data */ + union { + struct sof_ipc_dai_ssp_params ssp; + struct sof_ipc_dai_hda_params hda; + struct sof_ipc_dai_dmic_params dmic; + }; +}; + +/* + * Stream configuration. + */ + +#define SOF_IPC_MAX_CHANNELS 8 + +/* channel positions - uses same values as ALSA */ +enum sof_ipc_chmap { + SOF_CHMAP_UNKNOWN = 0, + SOF_CHMAP_NA, /* N/A, silent */ + SOF_CHMAP_MONO, /* mono stream */ + SOF_CHMAP_FL, /* front left */ + SOF_CHMAP_FR, /* front right */ + SOF_CHMAP_RL, /* rear left */ + SOF_CHMAP_RR, /* rear right */ + SOF_CHMAP_FC, /* front centre */ + SOF_CHMAP_LFE, /* LFE */ + SOF_CHMAP_SL, /* side left */ + SOF_CHMAP_SR, /* side right */ + SOF_CHMAP_RC, /* rear centre */ + SOF_CHMAP_FLC, /* front left centre */ + SOF_CHMAP_FRC, /* front right centre */ + SOF_CHMAP_RLC, /* rear left centre */ + SOF_CHMAP_RRC, /* rear right centre */ + SOF_CHMAP_FLW, /* front left wide */ + SOF_CHMAP_FRW, /* front right wide */ + SOF_CHMAP_FLH, /* front left high */ + SOF_CHMAP_FCH, /* front centre high */ + SOF_CHMAP_FRH, /* front right high */ + SOF_CHMAP_TC, /* top centre */ + SOF_CHMAP_TFL, /* top front left */ + SOF_CHMAP_TFR, /* top front right */ + SOF_CHMAP_TFC, /* top front centre */ + SOF_CHMAP_TRL, /* top rear left */ + SOF_CHMAP_TRR, /* top rear right */ + SOF_CHMAP_TRC, /* top rear centre */ + SOF_CHMAP_TFLC, /* top front left centre */ + SOF_CHMAP_TFRC, /* top front right centre */ + SOF_CHMAP_TSL, /* top side left */ + SOF_CHMAP_TSR, /* top side right */ + SOF_CHMAP_LLFE, /* left LFE */ + SOF_CHMAP_RLFE, /* right LFE */ + SOF_CHMAP_BC, /* bottom centre */ + SOF_CHMAP_BLC, /* bottom left centre */ + SOF_CHMAP_BRC, /* bottom right centre */ + SOF_CHMAP_LAST = SOF_CHMAP_BRC, +}; + +/* common sample rates for use in masks */ +#define SOF_RATE_8000 (1 << 0) /* 8000Hz */ +#define SOF_RATE_11025 (1 << 1) /* 11025Hz */ +#define SOF_RATE_12000 (1 << 2) /* 12000Hz */ +#define SOF_RATE_16000 (1 << 3) /* 16000Hz */ +#define SOF_RATE_22050 (1 << 4) /* 22050Hz */ +#define SOF_RATE_24000 (1 << 5) /* 24000Hz */ +#define SOF_RATE_32000 (1 << 6) /* 32000Hz */ +#define SOF_RATE_44100 (1 << 7) /* 44100Hz */ +#define SOF_RATE_48000 (1 << 8) /* 48000Hz */ +#define SOF_RATE_64000 (1 << 9) /* 64000Hz */ +#define SOF_RATE_88200 (1 << 10) /* 88200Hz */ +#define SOF_RATE_96000 (1 << 11) /* 96000Hz */ +#define SOF_RATE_176400 (1 << 12) /* 176400Hz */ +#define SOF_RATE_192000 (1 << 13) /* 192000Hz */ + +/* continuous and non-standard rates for flexibility */ +#define SOF_RATE_CONTINUOUS (1 << 30) /* range */ +#define SOF_RATE_KNOT (1 << 31) /* non-continuous */ + +/* stream PCM frame format */ +enum sof_ipc_frame { + SOF_IPC_FRAME_S16_LE = 0, + SOF_IPC_FRAME_S24_4LE, + SOF_IPC_FRAME_S32_LE, + SOF_IPC_FRAME_FLOAT, + /* other formats here */ +}; + +/* stream buffer format */ +enum sof_ipc_buffer_format { + SOF_IPC_BUFFER_INTERLEAVED, + SOF_IPC_BUFFER_NONINTERLEAVED, + /* other formats here */ +}; + +/* stream direction */ +enum sof_ipc_stream_direction { + SOF_IPC_STREAM_PLAYBACK = 0, + SOF_IPC_STREAM_CAPTURE, +}; + +/* stream ring info */ +struct sof_ipc_host_buffer { + uint32_t phy_addr; + uint32_t pages; + uint32_t size; + uint32_t offset; +} __attribute__((packed)); + +struct sof_ipc_stream_params { + struct sof_ipc_host_buffer buffer; + enum sof_ipc_stream_direction direction; + enum sof_ipc_frame frame_fmt; + enum sof_ipc_buffer_format buffer_fmt; + uint32_t stream_tag; + uint32_t rate; + uint32_t channels; + uint32_t sample_valid_bytes; + uint32_t sample_container_bytes; + /* for notifying host period has completed - 0 means no period IRQ */ + uint32_t host_period_bytes; + enum sof_ipc_chmap chmap[SOF_IPC_MAX_CHANNELS]; /* channel map */ +} __attribute__((packed)); + +/* PCM params info - SOF_IPC_STREAM_PCM_PARAMS */ +struct sof_ipc_pcm_params { + struct sof_ipc_hdr hdr; + uint32_t comp_id; + struct sof_ipc_stream_params params; +} __attribute__((packed)); + +/* PCM params info reply - SOF_IPC_STREAM_PCM_PARAMS_REPLY */ +struct sof_ipc_pcm_params_reply { + struct sof_ipc_reply rhdr; + uint32_t comp_id; + uint32_t posn_offset; +} __attribute__((packed)); + +/* compressed vorbis params - SOF_IPC_STREAM_VORBIS_PARAMS */ +struct sof_ipc_vorbis_params { + struct sof_ipc_hdr hdr; + uint32_t comp_id; + struct sof_ipc_stream_params params; + /* TODO */ +} __attribute__((packed)); + +/* free stream - SOF_IPC_STREAM_PCM_PARAMS */ +struct sof_ipc_stream { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +/* flags indicating which time stamps are in sync with each other */ +#define SOF_TIME_HOST_SYNC (1 << 0) +#define SOF_TIME_DAI_SYNC (1 << 1) +#define SOF_TIME_WALL_SYNC (1 << 2) +#define SOF_TIME_STAMP_SYNC (1 << 3) + +/* flags indicating which time stamps are valid */ +#define SOF_TIME_HOST_VALID (1 << 8) +#define SOF_TIME_DAI_VALID (1 << 9) +#define SOF_TIME_WALL_VALID (1 << 10) +#define SOF_TIME_STAMP_VALID (1 << 11) + +/* flags indicating time stamps are 64bit else 3use low 32bit */ +#define SOF_TIME_HOST_64 (1 << 16) +#define SOF_TIME_DAI_64 (1 << 17) +#define SOF_TIME_WALL_64 (1 << 18) +#define SOF_TIME_STAMP_64 (1 << 19) + +struct sof_ipc_stream_posn { + struct sof_ipc_reply rhdr; + uint32_t comp_id; /* host component ID */ + uint32_t flags; /* SOF_TIME_ */ + uint32_t wallclock_hz; /* frequency of wallclock in Hz */ + uint32_t timestamp_ns; /* resolution of timestamp in ns */ + uint64_t host_posn; /* host DMA position in bytes */ + uint64_t dai_posn; /* DAI DMA position in bytes */ + uint64_t comp_posn; /* comp position in bytes */ + uint64_t wallclock; /* audio wall clock */ + uint64_t timestamp; /* system time stamp */ + uint32_t xrun_comp_id; /* comp ID of XRUN component */ + int32_t xrun_size; /* XRUN size in bytes */ +} __attribute__((packed)); + +/* + * Component Mixers and Controls + */ + +/* control data type and direction */ +enum sof_ipc_ctrl_type { + /* per channel data - uses struct sof_ipc_ctrl_value_chan */ + SOF_CTRL_TYPE_VALUE_CHAN_GET = 0, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + /* component data - uses struct sof_ipc_ctrl_value_comp */ + SOF_CTRL_TYPE_VALUE_COMP_GET, + SOF_CTRL_TYPE_VALUE_COMP_SET, + /* bespoke data - struct struct sof_abi_hdr */ + SOF_CTRL_TYPE_DATA_GET, + SOF_CTRL_TYPE_DATA_SET, +}; + +/* control command type */ +enum sof_ipc_ctrl_cmd { + SOF_CTRL_CMD_VOLUME = 0, /* maps to ALSA volume style controls */ + SOF_CTRL_CMD_ENUM, /* maps to ALSA enum style controls */ + SOF_CTRL_CMD_SWITCH, /* maps to ALSA switch style controls */ + SOF_CTRL_CMD_BINARY, /* maps to ALSA binary style controls */ +}; + +/* generic channel mapped value data */ +struct sof_ipc_ctrl_value_chan { + enum sof_ipc_chmap channel; + uint32_t value; +} __attribute__((packed)); + +/* generic component mapped value data */ +struct sof_ipc_ctrl_value_comp { + uint32_t index; /* component source/sink/control index in control */ + union { + uint32_t uvalue; + int32_t svalue; + }; +} __attribute__((packed)); + +/* generic control data */ +struct sof_ipc_ctrl_data { + struct sof_ipc_reply rhdr; + uint32_t comp_id; + + /* control access and data type */ + enum sof_ipc_ctrl_type type; + enum sof_ipc_ctrl_cmd cmd; + uint32_t index; /* control index for comps > 1 control */ + + /* control data - can either be appended or DMAed from host */ + struct sof_ipc_host_buffer buffer; + uint32_t num_elems; /* in array elems or bytes */ + + /* control data - add new types if needed */ + union { + /* channel values can be used by volume type controls */ + struct sof_ipc_ctrl_value_chan chanv[0]; + /* component values used by routing controls like mux, mixer */ + struct sof_ipc_ctrl_value_comp compv[0]; + /* data can be used by binary controls */ + struct sof_abi_hdr data[0]; + }; +} __attribute__((packed)); + +/* + * Component + */ + +/* types of component */ +enum sof_comp_type { + SOF_COMP_NONE = 0, + SOF_COMP_HOST, + SOF_COMP_DAI, + SOF_COMP_SG_HOST, /* scatter gather variant */ + SOF_COMP_SG_DAI, /* scatter gather variant */ + SOF_COMP_VOLUME, + SOF_COMP_MIXER, + SOF_COMP_MUX, + SOF_COMP_SRC, + SOF_COMP_SPLITTER, + SOF_COMP_TONE, + SOF_COMP_SWITCH, + SOF_COMP_BUFFER, + SOF_COMP_EQ_IIR, + SOF_COMP_EQ_FIR, + SOF_COMP_FILEREAD, /* host test based file IO */ + SOF_COMP_FILEWRITE, /* host test based file IO */ +}; + +/* XRUN action for component */ +#define SOF_XRUN_STOP 1 /* stop stream */ +#define SOF_XRUN_UNDER_ZERO 2 /* send 0s to sink */ +#define SOF_XRUN_OVER_NULL 4 /* send data to NULL */ + +/* create new generic component - SOF_IPC_TPLG_COMP_NEW */ +struct sof_ipc_comp { + struct sof_ipc_hdr hdr; + uint32_t id; + enum sof_comp_type type; + uint32_t pipeline_id; +} __attribute__((packed)); + +/* + * Component Buffers + */ + +/* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */ +struct sof_ipc_buffer { + struct sof_ipc_comp comp; + uint32_t size; /* buffer size in bytes */ + uint32_t caps; /* SOF_MEM_CAPS_ */ +} __attribute__((packed)); + +/* generic component config data - must always be after struct sof_ipc_comp */ +struct sof_ipc_comp_config { + uint32_t periods_sink; /* 0 means variable */ + uint32_t periods_source; /* 0 means variable */ + uint32_t preload_count; /* how many periods to preload */ + enum sof_ipc_frame frame_fmt; + uint32_t xrun_action; +} __attribute__((packed)); + +/* generic host component */ +struct sof_ipc_comp_host { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + enum sof_ipc_stream_direction direction; + uint32_t no_irq; /* don't send periodic IRQ to host/DSP */ + uint32_t dmac_config; /* DMA engine specific */ +} __attribute__((packed)); + +/* generic DAI component */ +struct sof_ipc_comp_dai { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + enum sof_ipc_stream_direction direction; + uint32_t dai_index; /* index of this type dai */ + enum sof_ipc_dai_type type; + uint32_t dmac_config; /* DMA engine specific */ +} __attribute__((packed)); + +/* generic mixer component */ +struct sof_ipc_comp_mixer { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* volume ramping types */ +enum sof_volume_ramp { + SOF_VOLUME_LINEAR = 0, + SOF_VOLUME_LOG, + SOF_VOLUME_LINEAR_ZC, + SOF_VOLUME_LOG_ZC, +}; + +/* generic volume component */ +struct sof_ipc_comp_volume { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + uint32_t channels; + uint32_t min_value; + uint32_t max_value; + enum sof_volume_ramp ramp; + uint32_t initial_ramp; /* ramp space in ms */ +} __attribute__((packed)); + +/* generic SRC component */ +struct sof_ipc_comp_src { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + /* either source or sink rate must be non zero */ + uint32_t source_rate; /* source rate or 0 for variable */ + uint32_t sink_rate; /* sink rate or 0 for variable */ + uint32_t rate_mask; /* SOF_RATE_ supported rates */ +} __attribute__((packed)); + +/* generic MUX component */ +struct sof_ipc_comp_mux { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* generic tone generator component */ +struct sof_ipc_comp_tone { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + int32_t sample_rate; + int32_t frequency; + int32_t amplitude; + int32_t freq_mult; + int32_t ampl_mult; + int32_t length; + int32_t period; + int32_t repeats; + int32_t ramp_step; +} __attribute__((packed)); + +/* FIR equalizer component */ +struct sof_ipc_comp_eq_fir { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* IIR equalizer component */ +struct sof_ipc_comp_eq_iir { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* frees components, buffers and pipelines + * SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE + */ +struct sof_ipc_free { + struct sof_ipc_hdr hdr; + uint32_t id; +} __attribute__((packed)); + +struct sof_ipc_comp_reply { + struct sof_ipc_reply rhdr; + uint32_t id; + uint32_t offset; +} __attribute__((packed)); + +/* + * Pipeline + */ + +/* new pipeline - SOF_IPC_TPLG_PIPE_NEW */ +struct sof_ipc_pipe_new { + struct sof_ipc_hdr hdr; + uint32_t comp_id; /* component id for pipeline */ + uint32_t pipeline_id; /* pipeline id */ + uint32_t sched_id; /* sheduling component id */ + uint32_t core; /* core we run on */ + uint32_t deadline; /* execution completion deadline in us*/ + uint32_t priority; /* priority level 0 (low) to 10 (max) */ + uint32_t mips; /* worst case instruction count per period */ + uint32_t frames_per_sched;/* output frames of pipeline, 0 is variable */ + uint32_t xrun_limit_usecs; /* report xruns greater than limit */ + uint32_t timer;/* non zero if timer scheduled otherwise DAI scheduled */ +} __attribute__((packed)); + +/* pipeline construction complete - SOF_IPC_TPLG_PIPE_COMPLETE */ +struct sof_ipc_pipe_ready { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +struct sof_ipc_pipe_free { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +/* connect two components in pipeline - SOF_IPC_TPLG_COMP_CONNECT */ +struct sof_ipc_pipe_comp_connect { + struct sof_ipc_hdr hdr; + uint32_t source_id; + uint32_t sink_id; +} __attribute__((packed)); + +/* + * PM + */ + +/* PM context element */ +struct sof_ipc_pm_ctx_elem { + uint32_t type; + uint32_t size; + uint64_t addr; +} __attribute__((packed)); + +/* + * PM context - SOF_IPC_PM_CTX_SAVE, SOF_IPC_PM_CTX_RESTORE, + * SOF_IPC_PM_CTX_SIZE + */ +struct sof_ipc_pm_ctx { + struct sof_ipc_hdr hdr; + struct sof_ipc_host_buffer buffer; + uint32_t num_elems; + uint32_t size; + struct sof_ipc_pm_ctx_elem elems[]; +}; + +/* + * Firmware boot and version + */ + +#define SOF_IPC_MAX_ELEMS 16 + +/* extended data types that can be appended onto end of sof_ipc_fw_ready */ +enum sof_ipc_ext_data { + SOF_IPC_EXT_DMA_BUFFER = 0, + SOF_IPC_EXT_WINDOW, +}; + +/* FW version - SOF_IPC_GLB_VERSION */ +struct sof_ipc_fw_version { + uint16_t major; + uint16_t minor; + uint16_t build; + uint8_t date[12]; + uint8_t time[10]; + uint8_t tag[6]; + uint8_t pad[2]; /* Make sure the total size is 4 bytes aligned */ +} __attribute__((packed)); + +/* FW ready Message - sent by firmware when boot has completed */ +struct sof_ipc_fw_ready { + struct sof_ipc_hdr hdr; + uint32_t dspbox_offset; /* dsp initiated IPC mailbox */ + uint32_t hostbox_offset; /* host initiated IPC mailbox */ + uint32_t dspbox_size; + uint32_t hostbox_size; + struct sof_ipc_fw_version version; +} __attribute__((packed)); + +/* + * Extended Firmware data. All optional, depends on platform/arch. + */ + +enum sof_ipc_region { + SOF_IPC_REGION_DOWNBOX = 0, + SOF_IPC_REGION_UPBOX, + SOF_IPC_REGION_TRACE, + SOF_IPC_REGION_DEBUG, + SOF_IPC_REGION_STREAM, + SOF_IPC_REGION_REGS, + SOF_IPC_REGION_EXCEPTION, +}; + +struct sof_ipc_ext_data_hdr { + struct sof_ipc_hdr hdr; + enum sof_ipc_ext_data type; /* SOF_IPC_EXT_ */ +}; + +struct sof_ipc_dma_buffer_elem { + enum sof_ipc_region type; + uint32_t id; /* platform specific - used to map to host memory */ + struct sof_ipc_host_buffer buffer; +}; + +/* extended data DMA buffers for IPC, trace and debug */ +struct sof_ipc_dma_buffer_data { + struct sof_ipc_ext_data_hdr ext_hdr; + uint32_t num_buffers; + /* host files in buffer[n].buffer */ + struct sof_ipc_dma_buffer_elem buffer[]; +} __attribute__((packed)); + +struct sof_ipc_window_elem { + enum sof_ipc_region type; + uint32_t id; /* platform specific - used to map to host memory */ + uint32_t flags; /* R, W, RW, etc - to define */ + uint32_t size; /* size of region in bytes */ + /* offset in window region as windows can be partitioned */ + uint32_t offset; +}; + +/* extended data memory windows for IPC, trace and debug */ +struct sof_ipc_window { + struct sof_ipc_ext_data_hdr ext_hdr; + uint32_t num_windows; + struct sof_ipc_window_elem window[]; +} __attribute__((packed)); + +/* + * DMA for Trace + */ + +/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */ +struct sof_ipc_dma_trace_params { + struct sof_ipc_hdr hdr; + struct sof_ipc_host_buffer buffer; + uint32_t stream_tag; +} __attribute__((packed)); + +/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */ +struct sof_ipc_dma_trace_posn { + struct sof_ipc_reply rhdr; + uint32_t host_offset; /* Offset of DMA host buffer */ + uint32_t overflow; /* overflow bytes if any */ + uint32_t messages; /* total trace messages */ +} __attribute__((packed)); + +/* + * Architecture specific debug + */ + +/* Xtensa Firmware Oops data */ +struct sof_ipc_dsp_oops_xtensa { + uint32_t exccause; + uint32_t excvaddr; + uint32_t ps; + uint32_t epc1; + uint32_t epc2; + uint32_t epc3; + uint32_t epc4; + uint32_t epc5; + uint32_t epc6; + uint32_t epc7; + uint32_t eps2; + uint32_t eps3; + uint32_t eps4; + uint32_t eps5; + uint32_t eps6; + uint32_t eps7; + uint32_t depc; + uint32_t intenable; + uint32_t interrupt; + uint32_t sar; + uint32_t stack; +} __attribute__((packed)); + +#endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c new file mode 100644 index 00000000000000..02ae60455a9a66 --- /dev/null +++ b/sound/soc/sof/ipc.c @@ -0,0 +1,788 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * + * Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided + * by platform driver code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* + * IPC message default size and timeout (msecs). + * TODO: allow platforms to set size and timeout. + */ +#define IPC_TIMEOUT_MSECS 300 +#define IPC_EMPTY_LIST_SIZE 8 + +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id); +static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); + +/* + * IPC message Tx/Rx message handling. + */ + +/* SOF generic IPC data */ +struct snd_sof_ipc { + struct snd_sof_dev *sdev; + + /* TX message work and status */ + wait_queue_head_t wait_txq; + struct work_struct tx_kwork; + bool msg_pending; + + /* Rx Message work and status */ + struct work_struct rx_kwork; + + /* lists */ + struct list_head tx_list; + struct list_head reply_list; + struct list_head empty_list; +}; + +/* locks held by caller */ +static struct snd_sof_ipc_msg *msg_get_empty(struct snd_sof_ipc *ipc) +{ + struct snd_sof_ipc_msg *msg = NULL; + + /* get first empty message in the list */ + if (!list_empty(&ipc->empty_list)) { + msg = list_first_entry(&ipc->empty_list, struct snd_sof_ipc_msg, + list); + list_del(&msg->list); + } + + return msg; +} + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC) +static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) +{ + u8 *str; + u32 glb; + u32 type; + + glb = cmd & SOF_GLB_TYPE_MASK; + type = cmd & SOF_CMD_TYPE_MASK; + + switch (glb) { + case SOF_IPC_GLB_REPLY: + str = "GLB_REPLY"; break; + case SOF_IPC_GLB_COMPOUND: + str = "GLB_COMPOUND"; break; + case SOF_IPC_GLB_TPLG_MSG: + switch (type) { + case SOF_IPC_TPLG_COMP_NEW: + str = "GLB_TPLG_MSG: COMP_NEW"; break; + case SOF_IPC_TPLG_COMP_FREE: + str = "GLB_TPLG_MSG: COMP_FREE"; break; + case SOF_IPC_TPLG_COMP_CONNECT: + str = "GLB_TPLG_MSG: COMP_CONNECT"; break; + case SOF_IPC_TPLG_PIPE_NEW: + str = "GLB_TPLG_MSG: PIPE_NEW"; break; + case SOF_IPC_TPLG_PIPE_FREE: + str = "GLB_TPLG_MSG: PIPE_FREE"; break; + case SOF_IPC_TPLG_PIPE_CONNECT: + str = "GLB_TPLG_MSG: PIPE_CONNECT"; break; + case SOF_IPC_TPLG_PIPE_COMPLETE: + str = "GLB_TPLG_MSG: PIPE_COMPLETE"; break; + case SOF_IPC_TPLG_BUFFER_NEW: + str = "GLB_TPLG_MSG: BUFFER_NEW"; break; + case SOF_IPC_TPLG_BUFFER_FREE: + str = "GLB_TPLG_MSG: BUFFER_FREE"; break; + default: + str = "GLB_TPLG_MSG: unknown type"; break; + } + break; + case SOF_IPC_GLB_PM_MSG: + switch (type) { + case SOF_IPC_PM_CTX_SAVE: + str = "GLB_PM_MSG: CTX_SAVE"; break; + case SOF_IPC_PM_CTX_RESTORE: + str = "GLB_PM_MSG: CTX_RESTORE"; break; + case SOF_IPC_PM_CTX_SIZE: + str = "GLB_PM_MSG: CTX_SIZE"; break; + case SOF_IPC_PM_CLK_SET: + str = "GLB_PM_MSG: CLK_SET"; break; + case SOF_IPC_PM_CLK_GET: + str = "GLB_PM_MSG: CLK_GET"; break; + case SOF_IPC_PM_CLK_REQ: + str = "GLB_PM_MSG: CLK_REQ"; break; + default: + str = "GLB_PM_MSG: unknown type"; break; + } + break; + case SOF_IPC_GLB_COMP_MSG: + switch (type) { + case SOF_IPC_COMP_SET_VALUE: + str = "GLB_COMP_MSG: SET_VALUE"; break; + case SOF_IPC_COMP_GET_VALUE: + str = "GLB_COMP_MSG: GET_VALUE"; break; + case SOF_IPC_COMP_SET_DATA: + str = "GLB_COMP_MSG: SET_DATA"; break; + case SOF_IPC_COMP_GET_DATA: + str = "GLB_COMP_MSG: GET_DATA"; break; + default: + str = "GLB_COMP_MSG: unknown type"; break; + } + break; + case SOF_IPC_GLB_STREAM_MSG: + switch (type) { + case SOF_IPC_STREAM_PCM_PARAMS: + str = "GLB_STREAM_MSG: PCM_PARAMS"; break; + case SOF_IPC_STREAM_PCM_PARAMS_REPLY: + str = "GLB_STREAM_MSG: PCM_REPLY"; break; + case SOF_IPC_STREAM_PCM_FREE: + str = "GLB_STREAM_MSG: PCM_FREE"; break; + case SOF_IPC_STREAM_TRIG_START: + str = "GLB_STREAM_MSG: TRIG_START"; break; + case SOF_IPC_STREAM_TRIG_STOP: + str = "GLB_STREAM_MSG: TRIG_STOP"; break; + case SOF_IPC_STREAM_TRIG_PAUSE: + str = "GLB_STREAM_MSG: TRIG_PAUSE"; break; + case SOF_IPC_STREAM_TRIG_RELEASE: + str = "GLB_STREAM_MSG: TRIG_RELEASE"; break; + case SOF_IPC_STREAM_TRIG_DRAIN: + str = "GLB_STREAM_MSG: TRIG_DRAIN"; break; + case SOF_IPC_STREAM_TRIG_XRUN: + str = "GLB_STREAM_MSG: TRIG_XRUN"; break; + case SOF_IPC_STREAM_POSITION: + str = "GLB_STREAM_MSG: POSITION"; break; + case SOF_IPC_STREAM_VORBIS_PARAMS: + str = "GLB_STREAM_MSG: VORBIS_PARAMS"; break; + case SOF_IPC_STREAM_VORBIS_FREE: + str = "GLB_STREAM_MSG: VORBIS_FREE"; break; + default: + str = "GLB_STREAM_MSG: unknown type"; break; + } + break; + case SOF_IPC_FW_READY: + str = "FW_READY"; break; + case SOF_IPC_GLB_DAI_MSG: + switch (type) { + case SOF_IPC_DAI_CONFIG: + str = "GLB_DAI_MSG: CONFIG"; break; + case SOF_IPC_DAI_LOOPBACK: + str = "GLB_DAI_MSG: LOOPBACK"; break; + default: + str = "GLB_DAI_MSG: unknown type"; break; + } + break; + case SOF_IPC_GLB_TRACE_MSG: + str = "GLB_TRACE_MSG"; break; + default: + str = "unknown GLB command"; break; + } + + dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str); +} +#else +static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd) +{ + dev_dbg(dev, "%s: 0x%x\n", text, cmd); +} +#endif + +/* wait for IPC message reply */ +static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, + void *reply_data) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr *hdr = (struct sof_ipc_hdr *)msg->msg_data; + unsigned long flags; + int ret; + + /* wait for DSP IPC completion */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_TIMEOUT_MSECS)); + + spin_lock_irqsave(&sdev->ipc_lock, flags); + + if (ret == 0) { + dev_err(sdev->dev, "error: ipc timed out for 0x%x size 0x%x\n", + hdr->cmd, hdr->size); + snd_sof_dsp_dbg_dump(ipc->sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_trace_notify_for_error(ipc->sdev); + ret = -ETIMEDOUT; + } else { + /* copy the data returned from DSP */ + ret = snd_sof_dsp_get_reply(sdev, msg); + if (msg->reply_size) + memcpy(reply_data, msg->reply_data, msg->reply_size); + if (ret < 0) + dev_err(sdev->dev, "error: ipc error for 0x%x size 0x%zx\n", + hdr->cmd, msg->reply_size); + else + ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); + } + + /* return message body to empty list */ + list_move(&msg->list, &ipc->empty_list); + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + + snd_sof_dsp_cmd_done(sdev, SOF_IPC_DSP_REPLY); + + /* continue to schedule any remaining messages... */ + snd_sof_ipc_msgs_tx(sdev); + + return ret; +} + +/* send IPC message from host to DSP */ +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, + void *msg_data, size_t msg_bytes, void *reply_data, + size_t reply_bytes) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + unsigned long flags; + + spin_lock_irqsave(&sdev->ipc_lock, flags); + + /* get an empty message */ + msg = msg_get_empty(ipc); + if (!msg) { + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + return -EBUSY; + } + + msg->header = header; + msg->msg_size = msg_bytes; + msg->reply_size = reply_bytes; + msg->complete = false; + + /* attach any data */ + if (msg_bytes) + memcpy(msg->msg_data, msg_data, msg_bytes); + + /* add message to transmit list */ + list_add_tail(&msg->list, &ipc->tx_list); + + /* schedule the message if not busy */ + if (snd_sof_dsp_is_ready(sdev)) + schedule_work(&ipc->tx_kwork); + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + + /* now wait for completion */ + return tx_wait_done(ipc, msg, reply_data); +} +EXPORT_SYMBOL(sof_ipc_tx_message); + +/* send next IPC message in list */ +static void ipc_tx_next_msg(struct work_struct *work) +{ + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, tx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + unsigned long flags; + + spin_lock_irqsave(&sdev->ipc_lock, flags); + + /* send message if HW read and message in TX list */ + if (list_empty(&ipc->tx_list) || !snd_sof_dsp_is_ready(sdev)) + goto out; + + /* send first message in TX list */ + msg = list_first_entry(&ipc->tx_list, struct snd_sof_ipc_msg, list); + list_move(&msg->list, &ipc->reply_list); + snd_sof_dsp_send_msg(sdev, msg); + + ipc_log_header(sdev->dev, "ipc tx", msg->header); +out: + spin_unlock_irqrestore(&sdev->ipc_lock, flags); +} + +/* find original TX message from DSP reply */ +static struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, + u32 header) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + + header = SOF_IPC_MESSAGE_ID(header); + + if (list_empty(&ipc->reply_list)) + goto err; + + list_for_each_entry(msg, &ipc->reply_list, list) { + if (SOF_IPC_MESSAGE_ID(msg->header) == header) + return msg; + } + +err: + dev_err(sdev->dev, "error: rx list empty but received 0x%x\n", + header); + return NULL; +} + +/* mark IPC message as complete - locks held by caller */ +static void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, + struct snd_sof_ipc_msg *msg) +{ + msg->complete = true; + wake_up(&msg->waitq); +} + +/* drop all IPC messages in preparation for DSP stall/reset */ +void sof_ipc_drop_all(struct snd_sof_ipc *ipc) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg, *tmp; + unsigned long flags; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&sdev->ipc_lock, flags); + + list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { + list_move(&msg->list, &ipc->empty_list); + dev_err(sdev->dev, "error: dropped msg %d\n", msg->header); + } + + list_for_each_entry_safe(msg, tmp, &ipc->reply_list, list) { + list_move(&msg->list, &ipc->empty_list); + dev_err(sdev->dev, "error: dropped reply %d\n", msg->header); + } + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); +} +EXPORT_SYMBOL(sof_ipc_drop_all); + +/* handle reply message from DSP */ +int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct snd_sof_ipc_msg *msg; + + msg = sof_ipc_reply_find_msg(sdev->ipc, msg_id); + if (!msg) { + dev_err(sdev->dev, "error: can't find message header 0x%x", + msg_id); + return -EINVAL; + } + + /* wake up and return the error if we have waiters on this message ? */ + sof_ipc_tx_msg_reply_complete(sdev->ipc, msg); + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_reply); + +/* DSP firmware has sent host a message */ +static void ipc_msgs_rx(struct work_struct *work) +{ + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, rx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr hdr; + u32 cmd, type; + int err = -EINVAL; + + /* read back header */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &hdr, sizeof(hdr)); + ipc_log_header(sdev->dev, "ipc rx", hdr.cmd); + + cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + type = hdr.cmd & SOF_CMD_TYPE_MASK; + + /* check message type */ + switch (cmd) { + case SOF_IPC_GLB_REPLY: + dev_err(sdev->dev, "error: ipc reply unknown\n"); + break; + case SOF_IPC_FW_READY: + /* check for FW boot completion */ + if (!sdev->boot_complete) { + if (sdev->ops->fw_ready) + err = sdev->ops->fw_ready(sdev, cmd); + if (err < 0) { + dev_err(sdev->dev, "DSP firmware boot timeout %d\n", + err); + } else { + /* firmware boot completed OK */ + sdev->boot_complete = true; + dev_dbg(sdev->dev, "booting DSP firmware completed\n"); + wake_up(&sdev->boot_wait); + } + } + break; + case SOF_IPC_GLB_COMPOUND: + case SOF_IPC_GLB_TPLG_MSG: + case SOF_IPC_GLB_PM_MSG: + case SOF_IPC_GLB_COMP_MSG: + break; + case SOF_IPC_GLB_STREAM_MSG: + /* need to pass msg id into the function */ + ipc_stream_message(sdev, hdr.cmd); + break; + case SOF_IPC_GLB_TRACE_MSG: + ipc_trace_message(sdev, type); + break; + default: + dev_err(sdev->dev, "unknown DSP message 0x%x\n", cmd); + break; + } + + ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd); + + /* tell DSP we are done */ + snd_sof_dsp_cmd_done(sdev, SOF_IPC_HOST_REPLY); +} + +/* schedule work to transmit any IPC in queue */ +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->tx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_tx); + +/* schedule work to handle IPC from DSP */ +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); + +/* + * IPC trace mechanism. + */ + +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_dma_trace_posn posn; + + switch (msg_id) { + case SOF_IPC_TRACE_DMA_POSITION: + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + snd_sof_trace_update_pos(sdev, &posn); + break; + default: + dev_err(sdev->dev, "error: unhandled trace message %x\n", + msg_id); + break; + } +} + +/* + * IPC stream position. + */ + +static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + u32 posn_offset; + int direction; + + /* check if we have stream box */ + if (sdev->stream_box.size == 0) { + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + + spcm = snd_sof_find_spcm_comp(sdev, posn.comp_id, &direction); + } else { + spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + } + + if (!spcm) { + dev_err(sdev->dev, + "period elapsed for unknown stream, msg_id %d\n", + msg_id); + return; + } + + /* have stream box read from stream box */ + if (sdev->stream_box.size != 0) { + posn_offset = spcm->posn_offset[direction]; + snd_sof_dsp_mailbox_read(sdev, posn_offset, &posn, + sizeof(posn)); + + dev_dbg(sdev->dev, "posn mailbox: posn offset is 0x%x", + posn_offset); + } + + dev_dbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", + posn.host_posn, posn.dai_posn, posn.wallclock); + + memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); + snd_pcm_period_elapsed(spcm->stream[direction].substream); +} + +/* DSP notifies host of an XRUN within FW */ +static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + u32 posn_offset; + int direction; + + /* check if we have stream MMIO on this platform */ + if (sdev->stream_box.size == 0) { + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + + spcm = snd_sof_find_spcm_comp(sdev, posn.comp_id, &direction); + } else { + spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + } + + if (!spcm) { + dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", + msg_id); + return; + } + + /* have stream box read from stream box */ + if (sdev->stream_box.size != 0) { + posn_offset = spcm->posn_offset[direction]; + snd_sof_dsp_mailbox_read(sdev, posn_offset, &posn, + sizeof(posn)); + + dev_dbg(sdev->dev, "posn mailbox: posn offset is 0x%x", + posn_offset); + } + + dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", + posn.host_posn, posn.xrun_comp_id, posn.xrun_size); + +#if defined(CONFIG_SOC_SOF_DEBUG_XRUN_STOP) + /* stop PCM on XRUN - used for pipeline debug */ + memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); + snd_pcm_stop_xrun(spcm->stream[direction].substream); +#endif +} + +/* stream notifications from DSP FW */ +static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) +{ + /* get msg cmd type and msd id */ + u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd); + + switch (msg_type) { + case SOF_IPC_STREAM_POSITION: + ipc_period_elapsed(sdev, msg_id); + break; + case SOF_IPC_STREAM_TRIG_XRUN: + ipc_xrun(sdev, msg_id); + break; + default: + dev_err(sdev->dev, "error: unhandled stream message %x\n", + msg_id); + break; + } +} + +/* get stream position IPC - use faster MMIO method if available on platform */ +int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, int direction, + struct sof_ipc_stream_posn *posn) +{ + struct sof_ipc_stream stream; + int err; + + /* read position via slower IPC */ + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION; + stream.comp_id = spcm->stream[direction].comp_id; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + stream.hdr.cmd, &stream, sizeof(stream), &posn, + sizeof(*posn)); + if (err < 0) { + dev_err(sdev->dev, "error: failed to get stream %d position\n", + stream.comp_id); + return err; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_stream_posn); + +/* + * IPC get()/set() for kcontrols. + */ + +int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int err; + + /* read firmware volume */ + if (scontrol->readback_offset != 0) { + /* we can read value header via mmaped region */ + snd_sof_dsp_block_write(sdev, scontrol->readback_offset, + cdata->chanv, + sizeof(struct sof_ipc_ctrl_value_chan) * + cdata->num_elems); + + } else { + /* write value via slower IPC */ + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; + cdata->cmd = ctrl_cmd; + cdata->type = ctrl_type; + cdata->rhdr.hdr.size = scontrol->size; + cdata->comp_id = scontrol->comp_id; + cdata->num_elems = scontrol->num_channels; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + cdata->rhdr.hdr.cmd, cdata, + cdata->rhdr.hdr.size, + cdata, cdata->rhdr.hdr.size); + if (err < 0) { + dev_err(sdev->dev, "error: failed to set control %d values\n", + cdata->comp_id); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_set_comp_data); + +int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int err; + + /* read firmware byte counters */ + if (scontrol->readback_offset != 0) { + /* we can read values via mmaped region */ + snd_sof_dsp_block_read(sdev, scontrol->readback_offset, + cdata->chanv, + sizeof(struct sof_ipc_ctrl_value_chan) * + cdata->num_elems); + + } else { + /* read position via slower IPC */ + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; + cdata->cmd = ctrl_cmd; + cdata->type = ctrl_type; + cdata->rhdr.hdr.size = scontrol->size; + cdata->comp_id = scontrol->comp_id; + cdata->num_elems = scontrol->num_channels; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + cdata->rhdr.hdr.cmd, cdata, + cdata->rhdr.hdr.size, + cdata, cdata->rhdr.hdr.size); + if (err < 0) { + dev_err(sdev->dev, "error: failed to get control %d values\n", + cdata->comp_id); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_get_comp_data); + +/* + * IPC layer enumeration. + */ + +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size) +{ + sdev->dsp_box.offset = dspbox; + sdev->dsp_box.size = dspbox_size; + sdev->host_box.offset = hostbox; + sdev->host_box.size = hostbox_size; + return 0; +} +EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); + +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc *ipc; + struct snd_sof_ipc_msg *msg; + int i; + + ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return NULL; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->reply_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + INIT_WORK(&ipc->tx_kwork, ipc_tx_next_msg); + INIT_WORK(&ipc->rx_kwork, ipc_msgs_rx); + ipc->sdev = sdev; + + /* pre-allocate messages */ + dev_dbg(sdev->dev, "pre-allocate %d IPC messages\n", + IPC_EMPTY_LIST_SIZE); + msg = devm_kzalloc(sdev->dev, sizeof(struct snd_sof_ipc_msg) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (!msg) + return NULL; + + /* pre-allocate message data */ + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + msg->msg_data = devm_kzalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); + if (!msg->msg_data) + return NULL; + + msg->reply_data = devm_kzalloc(sdev->dev, PAGE_SIZE, + GFP_KERNEL); + if (!msg->reply_data) + return NULL; + + init_waitqueue_head(&msg->waitq); + list_add(&msg->list, &ipc->empty_list); + msg++; + } + + return ipc; +} +EXPORT_SYMBOL(snd_sof_ipc_init); + +void snd_sof_ipc_free(struct snd_sof_dev *sdev) +{ + cancel_work_sync(&sdev->ipc->tx_kwork); + cancel_work_sync(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_free); From 83f150e5d64d2a386aa21da8a8bab7681fe5728c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:32:38 +0000 Subject: [PATCH 022/298] ASoC: SOF: Add PCM operations support Add support for exposing PCMs to userspace. PCMs are defined by topology and the operations in this patch map to SOF IPC calls. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 719 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 719 insertions(+) create mode 100644 sound/soc/sof/pcm.c diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c new file mode 100644 index 00000000000000..4f9be5f8f4cf95 --- /dev/null +++ b/sound/soc/sof/pcm.c @@ -0,0 +1,719 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * + * PCM Layer, interface between ALSA and IPC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +#define DRV_NAME "sof-audio" + +/* Create DMA buffer page table for DSP */ +static int create_page_table(struct snd_pcm_substream *substream, + unsigned char *dma_area, size_t size) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); + int stream = substream->stream; + + return snd_sof_create_page_table(sdev, dmab, + spcm->stream[stream].page_table.area, size); +} + +/* this may get called several times by oss emulation */ +static int sof_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_pcm_params pcm; + struct sof_ipc_pcm_params_reply ipc_params_reply; + int posn_offset; + int ret; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: hw params stream %d dir %d\n", + spcm->pcm.pcm_id, substream->stream); + + memset(&pcm, 0, sizeof(pcm)); + + /* allocate audio buffer pages */ + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) { + dev_err(sdev->dev, "error: could not allocate %d bytes for PCM %d\n", + params_buffer_bytes(params), ret); + return ret; + } + + /* craete compressed page table for audio firmware */ + ret = create_page_table(substream, runtime->dma_area, + runtime->dma_bytes); + if (ret < 0) + return ret; + + /* number of pages should be rounded up */ + if (runtime->dma_bytes % PAGE_SIZE) + pcm.params.buffer.pages = (runtime->dma_bytes / PAGE_SIZE) + 1; + else + pcm.params.buffer.pages = runtime->dma_bytes / PAGE_SIZE; + + /* set IPC PCM parameters */ + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = spcm->stream[substream->stream].comp_id; + pcm.params.buffer.phy_addr = + spcm->stream[substream->stream].page_table.addr; + pcm.params.buffer.size = runtime->dma_bytes; + pcm.params.buffer.offset = 0; + pcm.params.direction = substream->stream; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* container size */ + switch (params_width(params)) { + case 16: + pcm.params.sample_container_bytes = 2; + break; + case 24: + pcm.params.sample_container_bytes = 4; + break; + case 32: + pcm.params.sample_container_bytes = 4; + break; + default: + return -EINVAL; + } + + /* format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + case SNDRV_PCM_FORMAT_FLOAT: + pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT; + break; + default: + return -EINVAL; + } + + /* firmware already configured host stream */ + pcm.params.stream_tag = snd_sof_pcm_platform_hw_params(sdev, + substream, + params); + dev_dbg(sdev->dev, "stream_tag %d", pcm.params.stream_tag); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + + /* validate offset */ + posn_offset = ipc_params_reply.posn_offset; + + /* check if offset is overflow or it is not aligned */ + if (posn_offset > sdev->stream_box.size || + posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) { + dev_err(sdev->dev, "error: got wrong posn offset 0x%x for PCM %d\n", + posn_offset, ret); + return ret; + } + spcm->posn_offset[substream->stream] = + sdev->stream_box.offset + posn_offset; + + /* save pcm hw_params */ + memcpy(&spcm->params[substream->stream], params, sizeof(*params)); + + return ret; +} + +static int sof_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + + snd_pcm_lib_free_pages(substream); + return ret; +} + +static int sof_restore_hw_params(struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, + struct snd_sof_dev *sdev) +{ + snd_pcm_uframes_t host = 0; + u64 host_posn; + int ret = 0; + + /* resume stream */ + host_posn = spcm->stream[substream->stream].posn.host_posn; + host = bytes_to_frames(substream->runtime, host_posn); + dev_dbg(sdev->dev, + "PCM: resume stream %d dir %d DMA position %lu\n", + spcm->pcm.pcm_id, substream->stream, host); + + /* set hw_params */ + ret = sof_pcm_hw_params(substream, + &spcm->params[substream->stream]); + if (ret < 0) { + dev_err(sdev->dev, + "error: set pcm hw_params after resume\n"); + return ret; + } + + return 0; +} + +static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret = 0; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: trigger stream %d dir %d cmd %d\n", + spcm->pcm.pcm_id, substream->stream, cmd); + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + + /* + * Check if stream was marked for restore before suspend + */ + if (spcm->restore_stream[substream->stream]) { + + /* unset restore_stream */ + spcm->restore_stream[substream->stream] = 0; + + /* do not send ipc as the stream hasn't been set up */ + return 0; + } + + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + + /* check if the stream hw_params needs to be restored */ + if (spcm->restore_stream[substream->stream]) { + + /* restore hw_params */ + ret = sof_restore_hw_params(substream, spcm, sdev); + if (ret < 0) + return ret; + + /* unset restore_stream */ + spcm->restore_stream[substream->stream] = 0; + + /* trigger start */ + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + } else { + + /* trigger pause release */ + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + } + break; + case SNDRV_PCM_TRIGGER_START: + /* fall through */ + case SNDRV_PCM_TRIGGER_RESUME: + + /* check if the stream hw_params needs to be restored */ + if (spcm->restore_stream[substream->stream]) { + + /* restore hw_params */ + ret = sof_restore_hw_params(substream, spcm, sdev); + if (ret < 0) + return ret; + + /* unset restore_stream */ + spcm->restore_stream[substream->stream] = 0; + } + + /* trigger stream */ + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + break; + default: + dev_err(sdev->dev, "error: unhandled trigger cmd %d\n", cmd); + return -EINVAL; + } + + snd_sof_pcm_platform_trigger(sdev, substream, cmd); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + + return ret; +} + +static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + snd_pcm_uframes_t host = 0, dai = 0; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + /* read position from DSP */ + host = bytes_to_frames(substream->runtime, + spcm->stream[substream->stream].posn.host_posn); + dai = bytes_to_frames(substream->runtime, + spcm->stream[substream->stream].posn.dai_posn); + + dev_dbg(sdev->dev, "PCM: stream %d dir %d DMA position %lu DAI position %lu\n", + spcm->pcm.pcm_id, substream->stream, host, dai); + + return host; +} + +static int sof_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + struct snd_soc_tplg_stream_caps *caps = + &spcm->pcm.caps[substream->stream]; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: open stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + mutex_lock(&spcm->mutex); + + pm_runtime_get_sync(sdev->dev); + + /* set any runtime constraints based on topology */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + le32_to_cpu(caps->period_size_min)); + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + le32_to_cpu(caps->period_size_min)); + + /* set runtime config */ + runtime->hw.info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; + runtime->hw.formats = le64_to_cpu(caps->formats); + runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min); + runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max); + runtime->hw.periods_min = le32_to_cpu(caps->periods_min); + runtime->hw.periods_max = le32_to_cpu(caps->periods_max); + runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max); + + dev_dbg(sdev->dev, "period min %zd max %zd bytes\n", + runtime->hw.period_bytes_min, + runtime->hw.period_bytes_max); + dev_dbg(sdev->dev, "period count %d max %d\n", + runtime->hw.periods_min, + runtime->hw.periods_max); + dev_dbg(sdev->dev, "buffer max %zd bytes\n", + runtime->hw.buffer_bytes_max); + + /* set wait time - TODO: come from topology */ + substream->wait_time = 500; + + spcm->stream[substream->stream].posn.host_posn = 0; + spcm->stream[substream->stream].posn.dai_posn = 0; + spcm->stream[substream->stream].substream = substream; + + snd_sof_pcm_platform_open(sdev, substream); + + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: close stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + snd_sof_pcm_platform_close(sdev, substream); + + mutex_lock(&spcm->mutex); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static struct snd_pcm_ops sof_pcm_ops = { + .open = sof_pcm_open, + .close = sof_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sof_pcm_hw_params, + .hw_free = sof_pcm_hw_free, + .trigger = sof_pcm_trigger, + .pointer = sof_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm; + struct snd_pcm *pcm = rtd->pcm; + struct snd_soc_tplg_stream_caps *caps; + int ret = 0, stream = SNDRV_PCM_STREAM_PLAYBACK; + + /* find SOF PCM for this RTD */ + spcm = snd_sof_find_spcm_dai(sdev, rtd); + if (!spcm) { + dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + rtd->dai_link->id); + return 0; + } + rtd->sof = spcm; + + dev_dbg(sdev->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); + + /* do we need to allocate playback PCM DMA pages */ + if (!spcm->pcm.playback) + goto capture; + + caps = &spcm->pcm.caps[stream]; + + /* pre-allocate playback audio buffer pages */ + dev_dbg(sdev->dev, "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + + ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); + if (ret) { + dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", + caps->buffer_size_min, caps->buffer_size_max, + caps->name, ret); + return ret; + } + + /* allocate playback page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &spcm->stream[stream].page_table); + if (ret < 0) { + dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + caps->name, ret); + return ret; + } + +capture: + stream = SNDRV_PCM_STREAM_CAPTURE; + + /* do we need to allocate capture PCM DMA pages */ + if (!spcm->pcm.capture) + return ret; + + caps = &spcm->pcm.caps[stream]; + + /* pre-allocate capture audio buffer pages */ + dev_dbg(sdev->dev, "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + + ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); + if (ret) { + dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", + caps->buffer_size_min, caps->buffer_size_max, + caps->name, ret); + snd_dma_free_pages(&spcm->stream[stream].page_table); + return ret; + } + + /* allocate capture page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &spcm->stream[stream].page_table); + if (ret < 0) { + dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + caps->name, ret); + snd_dma_free_pages(&spcm->stream[stream].page_table); + return ret; + } + + /* TODO: assign channel maps from topology */ + + return ret; +} + +static void sof_pcm_free(struct snd_pcm *pcm) +{ + struct snd_sof_pcm *spcm; + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + + spcm = snd_sof_find_spcm_dai(sdev, rtd); + if (!spcm) { + dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + rtd->dai_link->id); + return; + } + + if (spcm->pcm.playback) + snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].page_table); + + if (spcm->pcm.capture) + snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table); + + snd_sof_free_topology(sdev); +} + +/* fixup the BE DAI link to match any values from topology */ +static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_dai *dai = + snd_sof_find_dai(sdev, (char *)rtd->dai_link->name); + + /* no topology exists for this BE, try a common configuration */ + if (!dai) { + dev_err(sdev->dev, "error: no topology found for BE DAI %s config\n", + rtd->dai_link->name); + + /* set 48k, stereo, 16bits by default */ + rate->min = 48000; + rate->max = 48000; + + channels->min = 2; + channels->max = 2; + + snd_mask_none(fmt); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S16_LE); + + return 0; + } + + /* read format from topology */ + snd_mask_none(fmt); + + switch (dai->comp_dai.config.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(sdev->dev, "No available DAI format!\n"); + return -EINVAL; + } + + /* read rate and channels from topology */ + switch (dai->dai_config.type) { + case SOF_DAI_INTEL_SSP: + rate->min = dai->dai_config.ssp.fsync_rate; + rate->max = dai->dai_config.ssp.fsync_rate; + channels->min = dai->dai_config.ssp.tdm_slots; + channels->max = dai->dai_config.ssp.tdm_slots; + + dev_dbg(sdev->dev, + "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(sdev->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + + break; + case SOF_DAI_INTEL_DMIC: + /* DMIC only supports 16 or 32 bit formats */ + if (dai->comp_dai.config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + dev_err(sdev->dev, + "error: invalid fmt %d for DAI type %d\n", + dai->comp_dai.config.frame_fmt, + dai->dai_config.type); + } + /* TODO: add any other DMIC specific fixups */ + break; + case SOF_DAI_INTEL_HDA: + /* fallthrough */ + default: + dev_err(sdev->dev, "error: invalid DAI type %d\n", + dai->dai_config.type); + break; + } + + return 0; +} + +static int sof_pcm_probe(struct snd_soc_component *component) +{ + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pdata *plat_data = dev_get_platdata(component->dev); + int ret; + + /* load the default topology */ + sdev->component = component; + ret = snd_sof_load_topology(sdev, + plat_data->machine->sof_tplg_filename); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load DSP topology %d\n", + ret); + goto err; + } + + /* enable runtime PM with auto suspend */ + pm_runtime_set_autosuspend_delay(component->dev, + SND_SOF_SUSPEND_DELAY); + pm_runtime_use_autosuspend(component->dev); + pm_runtime_enable(component->dev); + pm_runtime_idle(component->dev); + +err: + return ret; +} + +static void sof_pcm_remove(struct snd_soc_component *component) +{ + pm_runtime_disable(component->dev); +} + +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) +{ + struct snd_soc_component_driver *pd = &sdev->plat_drv; + struct snd_sof_pdata *plat_data = sdev->pdata; + + dev_dbg(sdev->dev, "using platform alias %s\n", + plat_data->machine->asoc_plat_name); + + pd->name = "sof-audio"; + pd->probe = sof_pcm_probe; + pd->remove = sof_pcm_remove; + pd->ops = &sof_pcm_ops; + pd->compr_ops = &sof_compressed_ops; + pd->pcm_new = sof_pcm_new; + pd->pcm_free = sof_pcm_free; + pd->ignore_machine = plat_data->machine->drv_name; + pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; + pd->be_pcm_base = SOF_BE_PCM_BASE; + pd->use_dai_pcm_id = true; + pd->topology_name_prefix = "sof"; +} + From 824f11b7b4b4574c65cd08130c817eb8c3a243e7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:05 +0000 Subject: [PATCH 023/298] ASoC: SOF: Add support for loading topologies SOF uses topology to define the DAPM graphs and widgets, DAIs, PCMs and set parameters for init and run time usage. This patch loads topology and maps it to IPC commands that are build the topology on the DSP. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-topology.h | 95 ++ sound/soc/sof/sof-priv.h | 1 + sound/soc/sof/topology.c | 2191 +++++++++++++++++++++++++++++ 3 files changed, 2287 insertions(+) create mode 100644 include/uapi/sound/sof-topology.h create mode 100644 sound/soc/sof/topology.c diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h new file mode 100644 index 00000000000000..ba966cd4fb512c --- /dev/null +++ b/include/uapi/sound/sof-topology.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ + +/* + * Topology IDs and tokens. + * + * ** MUST BE ALIGNED WITH TOPOLOGY CONFIGURATION TOKEN VALUES ** + */ + +#ifndef __INCLUDE_UAPI_SOF_TOPOLOGY_H__ +#define __INCLUDE_UAPI_SOF_TOPOLOGY_H__ + +/* + * Kcontrol IDs + */ +#define SOF_TPLG_KCTL_VOL_ID 256 +#define SOF_TPLG_KCTL_ENUM_ID 257 +#define SOF_TPLG_KCTL_BYTES_ID 258 + +/* + * Tokens - must match values in topology configurations + */ + +/* buffers */ +#define SOF_TKN_BUF_SIZE 100 +#define SOF_TKN_BUF_CAPS 101 + +/* DAI */ +#define SOF_TKN_DAI_DMAC_CONFIG 153 +#define SOF_TKN_DAI_TYPE 154 +#define SOF_TKN_DAI_INDEX 155 + +/* scheduling */ +#define SOF_TKN_SCHED_DEADLINE 200 +#define SOF_TKN_SCHED_PRIORITY 201 +#define SOF_TKN_SCHED_MIPS 202 +#define SOF_TKN_SCHED_CORE 203 +#define SOF_TKN_SCHED_FRAMES 204 +#define SOF_TKN_SCHED_TIMER 205 + +/* volume */ +#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 +#define SOF_TKN_VOLUME_RAMP_STEP_MS 251 + +/* SRC */ +#define SOF_TKN_SRC_RATE_IN 300 +#define SOF_TKN_SRC_RATE_OUT 301 + +/* PCM */ +#define SOF_TKN_PCM_DMAC_CONFIG 353 + +/* Generic components */ +#define SOF_TKN_COMP_PERIOD_SINK_COUNT 400 +#define SOF_TKN_COMP_PERIOD_SOURCE_COUNT 401 +#define SOF_TKN_COMP_FORMAT 402 +#define SOF_TKN_COMP_PRELOAD_COUNT 403 + +/* SSP */ +#define SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE 500 +#define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 +#define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 +#define SOF_TKN_INTEL_SSP_MCLK_ID 503 +#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 504 +#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 505 +#define SOF_TKN_INTEL_SSP_QUIRKS 506 +#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 507 + +/* DMIC */ +#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 +#define SOF_TKN_INTEL_DMIC_CLK_MIN 601 +#define SOF_TKN_INTEL_DMIC_CLK_MAX 602 +#define SOF_TKN_INTEL_DMIC_DUTY_MIN 603 +#define SOF_TKN_INTEL_DMIC_DUTY_MAX 604 +#define SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE 605 +#define SOF_TKN_INTEL_DMIC_SAMPLE_RATE 608 +#define SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH 609 + +/* DMIC PDM */ +#define SOF_TKN_INTEL_DMIC_PDM_CTRL_ID 700 +#define SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable 701 +#define SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable 702 +#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_A 703 +#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_B 704 +#define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705 +#define SOF_TKN_INTEL_DMIC_PDM_SKEW 706 + +#endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 42020c0aeeb3da..dc9809ac84f815 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -242,6 +242,7 @@ struct snd_sof_control { struct sof_ipc_ctrl_data *control_data; u32 size; /* cdata size */ enum sof_ipc_ctrl_cmd cmd; + u32 *volume_table; /* volume table computed from tlv data*/ struct mutex mutex; /* access mutex */ struct list_head list; /* list in sdev control list */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c new file mode 100644 index 00000000000000..e5e6399b3987f0 --- /dev/null +++ b/sound/soc/sof/topology.c @@ -0,0 +1,2191 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +#define COMP_ID_UNASSIGNED 0xffffffff +/* Constants used in the computation of linear volume gain from dB gain */ +/* 20th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_TWENTIETH_ROOT_OF_TEN 73533 +/* 40th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_FORTIETH_ROOT_OF_TEN 69419 +/* Volume fractional word length */ +#define VOLUME_FWL 16 +/* 0.5 dB step value in topology TLV */ +#define VOL_HALF_DB_STEP 50 + +/* TLV data items */ +#define TLV_ITEMS 3 +#define TLV_MIN 0 +#define TLV_STEP 1 +#define TLV_MUTE 2 + +static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) +{ + /* we only support dB scale TLV type at the moment */ + if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) + return -EINVAL; + + /* min value in topology tlv data is multiplied by 100 */ + tlv[TLV_MIN] = (int)p[SNDRV_CTL_TLVO_DB_SCALE_MIN] / 100; + + /* volume steps */ + tlv[TLV_STEP] = (int)(p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MASK); + + /* mute ON/OFF */ + if ((p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MUTE) == 0) + tlv[TLV_MUTE] = 0; + else + tlv[TLV_MUTE] = 1; + + return 0; +} + +/* Function to truncate an unsigned 64-bit number + * by x bits and return 32-bit unsigned number + * This function also takes care of rounding while truncating + */ +static inline u32 vol_shift_64(u64 i, u32 x) +{ + /* do not truncate more than 32 bits */ + if (x > 32) + x = 32; + + if (x == 0) + return (u32)i; + + return (u32)(((i >> (x - 1)) + 1) >> 1); +} + +/* Function to compute a ^ exp where, + * a is a fractional number represented by a fixed-point integer + * with a fractional world length of "fwl" + * exp is an integer + * fwl is the fractional word length + * Return value is a fractional number represented by a fixed-point + * integer with a fractional word length of "fwl" + */ +static u32 vol_pow32(u32 a, int exp, u32 fwl) +{ + int i, iter; + u32 power = 1 << fwl; + u64 numerator; + + /* if exponent is 0, return 1 */ + if (exp == 0) + return power; + + /* determine the number of iterations based on the exponent */ + if (exp < 0) + iter = exp * -1; + else + iter = exp; + + /* mutiply a "iter" times to compute power */ + for (i = 0; i < iter; i++) { + /* Product of 2 Qx.fwl fixed-point numbers yields a Q2*x.2*fwl + * Truncate product back to fwl fractional bits with rounding + */ + power = vol_shift_64((u64)power * a, fwl); + } + + if (exp > 0) { + /* if exp is positive, return the result */ + return power; + } + + /* if exp is negative, return the multiplicative inverse */ + numerator = (u64)1 << (fwl << 1); + do_div(numerator, power); + + return (u32)numerator; +} + +/* Function to calculate volume gain from TLV data + * This function can only handle gain steps that are multiples of 0.5 dB + */ +static u32 vol_compute_gain(u32 value, int *tlv) +{ + int dB_gain; + u32 linear_gain; + int f_step; + + /* mute volume */ + if (value == 0 && tlv[TLV_MUTE]) + return 0; + + /* compute dB gain from tlv + * tlv_step in topology is multiplied by 100 + */ + dB_gain = tlv[TLV_MIN] + (value * tlv[TLV_STEP]) / 100; + + /* compute linear gain + * represented by fixed-point int with VOLUME_FWL fractional bits + */ + linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN, dB_gain, VOLUME_FWL); + + /* extract the fractional part of volume step */ + f_step = tlv[TLV_STEP] - (tlv[TLV_STEP] / 100); + + /* if volume step is an odd multiple of 0.5 dB */ + if (f_step == VOL_HALF_DB_STEP && (value & 1)) + linear_gain = vol_shift_64((u64)linear_gain * + VOL_FORTIETH_ROOT_OF_TEN, + VOLUME_FWL); + + return linear_gain; +} + +/* Set up volume table for kcontrols from tlv data + * "size" specifies the number of entries in the table + */ +static int set_up_volume_table(struct snd_sof_control *scontrol, + int tlv[TLV_ITEMS], int size) +{ + int j; + + /* init the volume table */ + scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); + if (!scontrol->volume_table) + return -ENOMEM; + + /* populate the volume table */ + for (j = 0; j < size ; j++) + scontrol->volume_table[j] = vol_compute_gain(j, tlv); + + return 0; +} + +struct sof_dai_types { + const char *name; + enum sof_ipc_dai_type type; +}; + +static const struct sof_dai_types sof_dais[] = { + {"SSP", SOF_DAI_INTEL_SSP}, + {"HDA", SOF_DAI_INTEL_HDA}, + {"DMIC", SOF_DAI_INTEL_DMIC}, +}; + +static enum sof_ipc_dai_type find_dai(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_dais); i++) { + if (strcmp(name, sof_dais[i].name) == 0) + return sof_dais[i].type; + } + + return SOF_DAI_INTEL_NONE; +} + +/* + * Supported Frame format types and lookup, add new ones to end of list. + */ + +struct sof_frame_types { + const char *name; + enum sof_ipc_frame frame; +}; + +static const struct sof_frame_types sof_frames[] = { + {"s16le", SOF_IPC_FRAME_S16_LE}, + {"s24le", SOF_IPC_FRAME_S24_4LE}, + {"s32le", SOF_IPC_FRAME_S32_LE}, + {"float", SOF_IPC_FRAME_FLOAT}, +}; + +static enum sof_ipc_frame find_format(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_frames); i++) { + if (strcmp(name, sof_frames[i].name) == 0) + return sof_frames[i].frame; + } + + /* use s32le if nothing is specified */ + return SOF_IPC_FRAME_S32_LE; +} + +/* + * Standard Kcontrols. + */ + +static int sof_control_load_volume(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_mixer_control *mc = + (struct snd_soc_tplg_mixer_control *)hdr; + struct sof_ipc_ctrl_data *cdata; + + /* validate topology data */ + if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; + + /* init the volume get/put data */ + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_ipc_ctrl_value_chan) * + le32_to_cpu(mc->num_channels); + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + cdata = scontrol->control_data; + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->num_channels = le32_to_cpu(mc->num_channels); + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + return 0; +} + +/* + * Topology Token Parsing. + * New tokens should be added to headers and parsing tables below. + */ + +struct sof_topology_token { + u32 token; + u32 type; + int (*get_token)(void *elem, void *object, u32 offset, u32 size); + u32 offset; + u32 size; +}; + +static int get_token_u32(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + u32 *val = object + offset; + + *val = le32_to_cpu(velem->value); + return 0; +} + +static int get_token_u16(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + u16 *val = object + offset; + + *val = (u16)le32_to_cpu(velem->value); + return 0; +} + +static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_format(velem->string); + return 0; +} + +static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_dai(velem->string); + return 0; +} + +/* Buffers */ +static const struct sof_topology_token buffer_tokens[] = { + {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, size), 0}, + {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, caps), 0}, +}; + +/* DAI */ +static const struct sof_topology_token dai_tokens[] = { + {SOF_TKN_DAI_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dmac_config), 0}, + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_comp_dai, type), 0}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dai_index), 0}, +}; + +/* BE DAI link */ +static const struct sof_topology_token dai_link_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_dai_config, type), 0}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_config, dai_index), 0}, +}; + +/* scheduling */ +static const struct sof_topology_token sched_tokens[] = { + {SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, deadline), 0}, + {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, priority), 0}, + {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, mips), 0}, + {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, core), 0}, + {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0}, + {SOF_TKN_SCHED_TIMER, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, timer), 0}, +}; + +/* volume */ +static const struct sof_topology_token volume_tokens[] = { + {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp), 0}, + {SOF_TKN_VOLUME_RAMP_STEP_MS, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_volume, initial_ramp), 0}, +}; + +/* SRC */ +static const struct sof_topology_token src_tokens[] = { + {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, source_rate), 0}, + {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, sink_rate), 0}, +}; + +/* Tone */ +static const struct sof_topology_token tone_tokens[] = { +}; + +/* PCM */ +static const struct sof_topology_token pcm_tokens[] = { + {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_config), 0}, +}; + +/* Generic components */ +static const struct sof_topology_token comp_tokens[] = { + {SOF_TKN_COMP_PERIOD_SINK_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_sink), 0}, + {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_source), 0}, + {SOF_TKN_COMP_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_comp_config, frame_fmt), 0}, + {SOF_TKN_COMP_PRELOAD_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, preload_count), 0}, +}; + +/* SSP */ +static const struct sof_topology_token ssp_tokens[] = { + {SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, mclk_keep_active), 0}, + {SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, bclk_keep_active), 0}, + {SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, fs_keep_active), 0}, + {SOF_TKN_INTEL_SSP_MCLK_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, + {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, + {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, + get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width), 0}, + {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, quirks), 0}, + {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, + get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, + tdm_per_slot_padding_flag), 0}, + +}; + +/* DMIC */ +static const struct sof_topology_token dmic_tokens[] = { + {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version), + 0}, + {SOF_TKN_INTEL_DMIC_CLK_MIN, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min), 0}, + {SOF_TKN_INTEL_DMIC_CLK_MAX, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max), 0}, + {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, fifo_fs_a), 0}, + {SOF_TKN_INTEL_DMIC_DUTY_MIN, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_min), 0}, + {SOF_TKN_INTEL_DMIC_DUTY_MAX, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_max), 0}, + {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, + num_pdm_active), 0}, + {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, fifo_bits_a), 0}, +}; + +/* + * DMIC PDM Tokens + * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token + * as it increments the index while parsing the array of pdm tokens + * and determines the correct offset + */ +static const struct sof_topology_token dmic_pdm_tokens[] = { + {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_SKEW, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew), + 0}, +}; + +/* HDA */ +static const struct sof_topology_token hda_tokens[] = { +}; + +static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_uuid_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { + elem = &array->uuid[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID) + continue; + + /* match token id */ + if (tokens[j].token != le32_to_cpu(elem->token)) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static void sof_parse_string_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_string_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { + elem = &array->string[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING) + continue; + + /* match token id */ + if (tokens[j].token != le32_to_cpu(elem->token)) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static void sof_parse_word_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_vendor_value_elem *elem; + size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl); + int i, j; + u32 offset; + u32 *index = NULL; + + /* parse element by element */ + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { + elem = &array->value[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT)) + continue; + + /* match token id */ + if (tokens[j].token != le32_to_cpu(elem->token)) + continue; + + /* pdm config array index */ + if (sdev->private) + index = (u32 *)sdev->private; + + /* matched - determine offset */ + switch (tokens[j].token) { + case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: + + /* inc number of pdm array index */ + if (index) + (*index)++; + /* fallthrough */ + case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: + case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: + case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: + case SOF_TKN_INTEL_DMIC_PDM_SKEW: + + /* check if array index is valid */ + if (!index || *index == 0) { + dev_err(sdev->dev, + "error: invalid array offset\n"); + continue; + } else { + /* offset within the pdm config array */ + offset = size * (*index - 1); + } + break; + default: + offset = 0; + break; + } + + /* load token */ + tokens[j].get_token(elem, object, + offset + tokens[j].offset, + tokens[j].size); + } + } +} + +static int sof_parse_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array, + int priv_size) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + int asize; + + while (priv_size > 0) { + asize = le32_to_cpu(array->size); + + /* validate asize */ + if (asize < 0) { /* FIXME: A zero-size array makes no sense */ + dev_err(sdev->dev, "error: invalid array size 0x%x\n", + asize); + return -EINVAL; + } + + /* make sure there is enough data before parsing */ + priv_size -= asize; + if (priv_size < 0) { + dev_err(sdev->dev, "error: invalid array size 0x%x\n", + asize); + return -EINVAL; + } + + /* call correct parser depending on type */ + switch (le32_to_cpu(array->type)) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + sof_parse_uuid_tokens(scomp, object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + sof_parse_string_tokens(scomp, object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + sof_parse_word_tokens(scomp, object, tokens, count, + array); + break; + default: + dev_err(sdev->dev, "error: unknown token type %d\n", + array->type); + return -EINVAL; + } + + /* next array */ + array = (void *)array + asize; + } + return 0; +} + +static void sof_dbg_comp_config(struct snd_soc_component *scomp, + struct sof_ipc_comp_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + + dev_dbg(sdev->dev, " config: periods snk %d src %d fmt %d\n", + config->periods_sink, config->periods_source, + config->frame_fmt); +} + +/* external kcontrol init - used for any driver specific init */ +static int sof_control_load(struct snd_soc_component *scomp, int index, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct soc_mixer_control *sm; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_dobj *dobj = NULL; + struct snd_sof_control *scontrol; + int ret = -EINVAL; + + dev_dbg(sdev->dev, "tplg: load control type %d name : %s\n", + hdr->type, hdr->name); + + scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); + if (!scontrol) + return -ENOMEM; + + scontrol->sdev = sdev; + mutex_init(&scontrol->mutex); + + switch (le32_to_cpu(hdr->ops.info)) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + sm = (struct soc_mixer_control *)kc->private_value; + dobj = &sm->dobj; + ret = sof_control_load_volume(scomp, scontrol, kc, hdr); + break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_BYTES: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_CTL_STROBE: + case SND_SOC_TPLG_DAPM_CTL_VOLSW: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_PIN: + default: + dev_warn(sdev->dev, "control type not supported %d:%d:%d\n", + hdr->ops.get, hdr->ops.put, hdr->ops.info); + kfree(scontrol); + return 0; + } + + dobj->private = scontrol; + list_add(&scontrol->list, &sdev->kcontrol_list); + return ret; +} + +static int sof_control_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_free fcomp; + struct snd_sof_control *scontrol = dobj->private; + + dev_dbg(sdev->dev, "tplg: unload control name : %s\n", scomp->name); + + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; + fcomp.hdr.size = sizeof(fcomp); + fcomp.id = scontrol->comp_id; + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, + fcomp.hdr.cmd, &fcomp, sizeof(fcomp), + NULL, 0); +} + +/* + * DAI Topology + */ + +static int sof_connect_dai_widget(struct snd_soc_component *scomp, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw, + struct snd_sof_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_card *card = scomp->card; + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + dev_dbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", + w->name, w->sname, rtd->dai_link->stream_name); + + if (!w->sname || !rtd->dai_link->stream_name) + continue; + + /* does stream match DAI link ? */ + if (strcmp(w->sname, rtd->dai_link->stream_name)) + continue; + + switch (w->id) { + case snd_soc_dapm_dai_out: + rtd->cpu_dai->capture_widget = w; + if (dai) + dai->name = rtd->dai_link->name; + dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + break; + case snd_soc_dapm_dai_in: + rtd->cpu_dai->playback_widget = w; + if (dai) + dai->name = rtd->dai_link->name; + dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + break; + default: + break; + } + } + + /* check we have a connection */ + if (!dai->name) { + dev_err(sdev->dev, "error: can't connect DAI %s stream %s\n", + w->name, w->sname); + return -EINVAL; + } + + return 0; +} + +static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r, + struct snd_sof_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_dai comp_dai; + int ret; + + /* configure dai IPC message */ + memset(&comp_dai, 0, sizeof(comp_dai)); + comp_dai.comp.hdr.size = sizeof(comp_dai); + comp_dai.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + comp_dai.comp.id = swidget->comp_id; + comp_dai.comp.type = SOF_COMP_DAI; + comp_dai.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &comp_dai, dai_tokens, + ARRAY_SIZE(dai_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dai tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + ret = sof_parse_tokens(scomp, &comp_dai.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dai.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "dai %s: type %d index %d\n", + swidget->widget->name, comp_dai.type, comp_dai.dai_index); + sof_dbg_comp_config(scomp, &comp_dai.config); + + ret = sof_ipc_tx_message(sdev->ipc, comp_dai.comp.hdr.cmd, + &comp_dai, sizeof(comp_dai), r, sizeof(*r)); + + if (ret == 0 && dai) { + dai->sdev = sdev; + memcpy(&dai->comp_dai, &comp_dai, sizeof(comp_dai)); + } + + return ret; +} + +/* + * Buffer topology + */ + +static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_buffer *buffer; + int ret; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* configure dai IPC message */ + buffer->comp.hdr.size = sizeof(*buffer); + buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; + buffer->comp.id = swidget->comp_id; + buffer->comp.type = SOF_COMP_BUFFER; + buffer->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, buffer, buffer_tokens, + ARRAY_SIZE(buffer_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse buffer tokens failed %d\n", + private->size); + kfree(buffer); + return ret; + } + + dev_dbg(sdev->dev, "buffer %s: size %d caps 0x%x\n", + swidget->widget->name, buffer->size, buffer->caps); + + swidget->private = (void *)buffer; + + ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer, + sizeof(*buffer), r, sizeof(*r)); + if (ret < 0) { + dev_err(sdev->dev, "buffer %s load failed\n", + swidget->widget->name); + kfree(buffer); + } + + return ret; +} + +/* + * PCM Topology + */ + +static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + enum sof_ipc_stream_direction dir, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_host *host; + int ret; + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + /* configure mixer IPC message */ + host->comp.hdr.size = sizeof(*host); + host->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + host->comp.id = swidget->comp_id; + host->comp.type = SOF_COMP_HOST; + host->comp.pipeline_id = index; + host->direction = dir; + + ret = sof_parse_tokens(scomp, host, pcm_tokens, + ARRAY_SIZE(pcm_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse host tokens failed %d\n", + private->size); + goto err; + } + + ret = sof_parse_tokens(scomp, &host->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n", + le32_to_cpu(private->size)); + goto err; + } + + dev_dbg(sdev->dev, "loaded host %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &host->config); + + swidget->private = (void *)host; + + ret = sof_ipc_tx_message(sdev->ipc, host->comp.hdr.cmd, host, + sizeof(*host), r, sizeof(*r)); + if (ret >= 0) + return ret; +err: + kfree(host); + return ret; +} + +/* + * Pipeline Topology + */ + +static int sof_widget_load_pipeline(struct snd_soc_component *scomp, + int index, struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_pipe_new *pipeline; + struct snd_sof_widget *comp_swidget; + int ret; + + pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); + if (!pipeline) + return -ENOMEM; + + /* configure dai IPC message */ + pipeline->hdr.size = sizeof(*pipeline); + pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; + pipeline->pipeline_id = index; + pipeline->comp_id = swidget->comp_id; + + /* component at start of pipeline is our stream id */ + comp_swidget = snd_sof_find_swidget(sdev, tw->sname); + if (!comp_swidget) { + dev_err(sdev->dev, "error: widget %s refers to non existent widget %s\n", + tw->name, tw->sname); + ret = -EINVAL; + goto err; + } + + pipeline->sched_id = comp_swidget->comp_id; + + dev_dbg(sdev->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", + pipeline->pipeline_id, pipeline->comp_id, pipeline->sched_id); + + ret = sof_parse_tokens(scomp, pipeline, sched_tokens, + ARRAY_SIZE(sched_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n", + private->size); + goto err; + } + + dev_dbg(sdev->dev, "pipeline %s: deadline %d pri %d mips %d core %d frames %d\n", + swidget->widget->name, pipeline->deadline, pipeline->priority, + pipeline->mips, pipeline->core, pipeline->frames_per_sched); + + swidget->private = (void *)pipeline; + + ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), r, sizeof(*r)); + if (ret >= 0) + return ret; +err: + kfree(pipeline); + return ret; +} + +/* + * Mixer topology + */ + +static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_mixer *mixer; + int ret; + + mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); + if (!mixer) + return -ENOMEM; + + /* configure mixer IPC message */ + mixer->comp.hdr.size = sizeof(*mixer); + mixer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + mixer->comp.id = swidget->comp_id; + mixer->comp.type = SOF_COMP_MIXER; + mixer->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &mixer->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n", + private->size); + kfree(mixer); + return ret; + } + + sof_dbg_comp_config(scomp, &mixer->config); + + swidget->private = (void *)mixer; + + ret = sof_ipc_tx_message(sdev->ipc, mixer->comp.hdr.cmd, mixer, + sizeof(*mixer), r, sizeof(*r)); + if (ret < 0) + kfree(mixer); + + return ret; +} + +/* + * PGA Topology + */ + +static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_volume *volume; + struct snd_soc_dapm_widget *widget = swidget->widget; + const struct snd_kcontrol_new *kc = NULL; + struct soc_mixer_control *sm; + struct snd_sof_control *scontrol; + const unsigned int *p; + int ret, tlv[TLV_ITEMS]; + + volume = kzalloc(sizeof(*volume), GFP_KERNEL); + if (!volume) + return -ENOMEM; + + if (le32_to_cpu(tw->num_kcontrols) != 1) { + dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", + tw->num_kcontrols); + ret = -EINVAL; + goto err; + } + + /* set up volume gain tables for kcontrol */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + + /* get volume control */ + scontrol = sm->dobj.private; + + /* set cmd for pga kcontrol */ + scontrol->cmd = SOF_CTRL_CMD_VOLUME; + + /* get topology tlv data */ + p = kc->tlv.p; + + /* extract tlv data */ + if (get_tlv_data(p, tlv) < 0) { + dev_err(sdev->dev, "error: invalid TLV data\n"); + ret = -EINVAL; + goto err; + } + + /* set up volume table */ + ret = set_up_volume_table(scontrol, tlv, sm->max + 1); + if (ret < 0) { + dev_err(sdev->dev, "error: setting up volume table\n"); + goto err; + } + + /* configure dai IPC message */ + volume->comp.hdr.size = sizeof(*volume); + volume->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + volume->comp.id = swidget->comp_id; + volume->comp.type = SOF_COMP_VOLUME; + volume->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, volume, volume_tokens, + ARRAY_SIZE(volume_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse volume tokens failed %d\n", + private->size); + goto err; + } + ret = sof_parse_tokens(scomp, &volume->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n", + le32_to_cpu(private->size)); + goto err; + } + + sof_dbg_comp_config(scomp, &volume->config); + + swidget->private = (void *)volume; + + ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume, + sizeof(*volume), r, sizeof(*r)); + if (ret >= 0) + return ret; +err: + kfree(volume); + return ret; +} + +/* + * SRC Topology + */ + +static int sof_widget_load_src(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_src *src; + int ret; + + src = kzalloc(sizeof(*src), GFP_KERNEL); + if (!src) + return -ENOMEM; + + /* configure mixer IPC message */ + src->comp.hdr.size = sizeof(*src); + src->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + src->comp.id = swidget->comp_id; + src->comp.type = SOF_COMP_SRC; + src->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, src, src_tokens, + ARRAY_SIZE(src_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse src tokens failed %d\n", + private->size); + goto err; + } + + ret = sof_parse_tokens(scomp, &src->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n", + le32_to_cpu(private->size)); + goto err; + } + + dev_dbg(sdev->dev, "src %s: source rate %d sink rate %d\n", + swidget->widget->name, src->source_rate, src->sink_rate); + sof_dbg_comp_config(scomp, &src->config); + + swidget->private = (void *)src; + + ret = sof_ipc_tx_message(sdev->ipc, src->comp.hdr.cmd, src, + sizeof(*src), r, sizeof(*r)); + if (ret >= 0) + return ret; +err: + kfree(src); + return ret; +} + +/* + * Signal Generator Topology + */ + +static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_tone *tone; + int ret; + + tone = kzalloc(sizeof(*tone), GFP_KERNEL); + if (!tone) + return -ENOMEM; + + /* configure mixer IPC message */ + tone->comp.hdr.size = sizeof(*tone); + tone->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + tone->comp.id = swidget->comp_id; + tone->comp.type = SOF_COMP_TONE; + tone->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, tone, tone_tokens, + ARRAY_SIZE(tone_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse tone tokens failed %d\n", + le32_to_cpu(private->size)); + goto err; + } + + ret = sof_parse_tokens(scomp, &tone->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n", + le32_to_cpu(private->size)); + goto err; + } + + dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d\n", + swidget->widget->name, tone->frequency, tone->amplitude); + sof_dbg_comp_config(scomp, &tone->config); + + swidget->private = (void *)tone; + + ret = sof_ipc_tx_message(sdev->ipc, tone->comp.hdr.cmd, tone, + sizeof(*tone), r, sizeof(*r)); + if (ret >= 0) + return ret; +err: + kfree(tone); + return ret; +} + +/* + * Generic widget loader. + */ + +static int sof_widget_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) +{ + /* nothing todo atm */ + return 0; +} + +/* external widget init - used for any driver specific init */ +static int sof_widget_ready(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + struct snd_sof_dai *dai; + struct sof_ipc_comp_reply reply; + struct snd_sof_control *scontrol = NULL; + int ret = 0; + + swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); + if (!swidget) + return -ENOMEM; + + swidget->sdev = sdev; + swidget->widget = w; + swidget->comp_id = sdev->next_comp_id++; + swidget->complete = 0; + swidget->id = w->id; + swidget->pipeline_id = index; + swidget->private = NULL; + memset(&reply, 0, sizeof(reply)); + + dev_dbg(sdev->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", + swidget->comp_id, index, swidget->id, tw->name, + strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 + ? tw->sname : "none"); + + /* handle any special case widgets */ + switch (w->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + dai = kzalloc(sizeof(*dai), GFP_KERNEL); + if (!dai) { + kfree(swidget); + return -ENOMEM; + } + + ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, + dai); + if (ret == 0) { + sof_connect_dai_widget(scomp, w, tw, dai); + list_add(&dai->list, &sdev->dai_list); + swidget->private = dai; + } else { + kfree(dai); + } + break; + case snd_soc_dapm_mixer: + ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_pga: + ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply); + /* Find scontrol for this pga and set readback offset*/ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + if (scontrol->comp_id == swidget->comp_id) { + scontrol->readback_offset = reply.offset; + break; + } + } + break; + case snd_soc_dapm_buffer: + ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_scheduler: + ret = sof_widget_load_pipeline(scomp, index, swidget, tw, + &reply); + break; + case snd_soc_dapm_aif_out: + ret = sof_widget_load_pcm(scomp, index, swidget, + SOF_IPC_STREAM_CAPTURE, tw, &reply); + break; + case snd_soc_dapm_aif_in: + ret = sof_widget_load_pcm(scomp, index, swidget, + SOF_IPC_STREAM_PLAYBACK, tw, &reply); + break; + case snd_soc_dapm_src: + ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_siggen: + ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_mux: + case snd_soc_dapm_demux: + case snd_soc_dapm_switch: + case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: + case snd_soc_dapm_effect: + default: + dev_warn(sdev->dev, "warning: widget type %d name %s not handled\n", + swidget->id, tw->name); + break; + } + + /* check IPC reply */ + if (ret < 0 || reply.rhdr.error < 0) { + dev_err(sdev->dev, + "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n", + tw->shift, swidget->id, tw->name, + strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 + ? tw->sname : "none", reply.rhdr.error); + kfree(swidget); + return ret; + } + + w->dobj.private = swidget; + mutex_init(&swidget->mutex); + list_add(&swidget->list, &sdev->widget_list); + return ret; +} + +static int sof_widget_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + const struct snd_kcontrol_new *kc = NULL; + struct snd_soc_dapm_widget *widget; + struct snd_sof_control *scontrol; + struct snd_sof_widget *swidget; + struct soc_mixer_control *sm; + struct snd_sof_dai *dai; + + swidget = dobj->private; + if (!swidget) + return 0; + + widget = swidget->widget; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + dai = swidget->private; + + /* remove and free dai object */ + if (dai) { + list_del(&dai->list); + kfree(dai); + } + break; + case snd_soc_dapm_pga: + + /* get volume kcontrol */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + + /* free volume table */ + kfree(scontrol->volume_table); + + /* fallthrough */ + default: + /* free private value */ + kfree(swidget->private); + break; + } + + /* remove and free swidget object */ + list_del(&swidget->list); + kfree(swidget); + + return 0; +} + +/* + * DAI HW configuration. + */ + +/* FE DAI - used for any driver specific init */ +static int sof_dai_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pcm *spcm; + + /* don't need to do anything for BEs atm */ + if (!pcm) + return 0; + + spcm = kzalloc(sizeof(*spcm), GFP_KERNEL); + if (!spcm) + return -ENOMEM; + + spcm->sdev = sdev; + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED; + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED; + if (pcm) { + spcm->pcm = *pcm; + dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name); + } + dai_drv->dobj.private = spcm; + mutex_init(&spcm->mutex); + list_add(&spcm->list, &sdev->pcm_list); + + return 0; +} + +static int sof_dai_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_pcm *spcm = dobj->private; + + list_del(&spcm->list); + kfree(spcm); + + return 0; +} + +static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + /* clock directions wrt codec */ + if (hw_config->bclk_master == SND_SOC_TPLG_BCLK_CM) { + /* codec is bclk master */ + if (hw_config->fsync_master == SND_SOC_TPLG_FSYNC_CM) + config->format |= SOF_DAI_FMT_CBM_CFM; + else + config->format |= SOF_DAI_FMT_CBM_CFS; + } else { + /* codec is bclk slave */ + if (hw_config->fsync_master == SND_SOC_TPLG_FSYNC_CM) + config->format |= SOF_DAI_FMT_CBS_CFM; + else + config->format |= SOF_DAI_FMT_CBS_CFS; + } + + /* inverted clocks ? */ + if (hw_config->invert_bclk) { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_IB_IF; + else + config->format |= SOF_DAI_FMT_IB_NF; + } else { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_NB_IF; + else + config->format |= SOF_DAI_FMT_NB_NF; + } +} + +static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->ssp, 0, sizeof(struct sof_ipc_dai_ssp_params)); + config->hdr.size = size; + + ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens, + ARRAY_SIZE(ssp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse ssp tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + config->ssp.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->ssp.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->ssp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->ssp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->ssp.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); + config->ssp.mclk_direction = hw_config->mclk_direction; + config->ssp.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->ssp.tx_slots = le32_to_cpu(hw_config->tx_slots); + + dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d\n", + config->dai_index, config->format, + config->ssp.mclk_rate, config->ssp.bclk_rate, + config->ssp.fsync_rate, config->ssp.sample_valid_bits, + config->ssp.tdm_slot_width, config->ssp.tdm_slots, + config->ssp.mclk_id); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for SSP%d\n", + config->dai_index); + + return ret; +} + +static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_dai_config *ipc_config; + struct sof_ipc_reply reply; + u32 size; + int ret, j; + + memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params)); + + /* get DMIC tokens */ + ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens, + ARRAY_SIZE(dmic_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dmic tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + /* + * allocate memory for common dai params, dmic params + * and dmic pdm controller params + */ + ipc_config = kzalloc(sizeof(*config) + + sizeof(struct sof_ipc_dai_dmic_pdm_ctrl) * + config->dmic.num_pdm_active, + GFP_KERNEL); + if (!ipc_config) { + dev_err(sdev->dev, "error: allocating memory for config\n"); + return -ENOMEM; + } + + /* copy the common dai config and dmic params */ + memcpy(ipc_config, config, sizeof(*config)); + + /* + * alloc memory for private member + * Used to track the pdm config array index currently being parsed + */ + sdev->private = kzalloc(sizeof(u32), GFP_KERNEL); + + /* get DMIC PDM tokens */ + ret = sof_parse_tokens(scomp, &ipc_config->dmic.pdm[0], dmic_pdm_tokens, + ARRAY_SIZE(dmic_pdm_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dmic pdm tokens failed %d\n", + le32_to_cpu(private->size)); + kfree(ipc_config); + return ret; + } + + /* set IPC header size */ + size = sizeof(*ipc_config); + ipc_config->hdr.size = size; + + /* debug messages */ + dev_dbg(sdev->dev, "tplg: config DMIC%d driver version %d\n", + ipc_config->dai_index, ipc_config->dmic.driver_ipc_version); + dev_dbg(sdev->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n", + ipc_config->dmic.pdmclk_min, ipc_config->dmic.pdmclk_max, + ipc_config->dmic.duty_min); + dev_dbg(sdev->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n", + ipc_config->dmic.duty_max, ipc_config->dmic.fifo_fs_a, + ipc_config->dmic.num_pdm_active); + dev_dbg(sdev->dev, "fifo word length %hd\n", + ipc_config->dmic.fifo_bits_a); + + for (j = 0; j < ipc_config->dmic.num_pdm_active; j++) { + dev_dbg(sdev->dev, "pdm %hd mic a %hd mic b %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].enable_mic_a, + ipc_config->dmic.pdm[j].enable_mic_b); + dev_dbg(sdev->dev, "pdm %hd polarity a %hd polarity b %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].polarity_mic_a, + ipc_config->dmic.pdm[j].polarity_mic_b); + dev_dbg(sdev->dev, "pdm %hd clk_edge %hd skew %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].clk_edge, + ipc_config->dmic.pdm[j].skew); + } + + /* TODO: check if fifo_b word length is needed */ + ipc_config->dmic.fifo_bits_b = ipc_config->dmic.fifo_bits_a; + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + ipc_config->hdr.cmd, ipc_config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for DMIC%d\n", + config->dai_index); + + /* update config with pdm config */ + memcpy(config, ipc_config, sizeof(*ipc_config)); + + kfree(sdev->private); + kfree(ipc_config); + + return ret; +} + +static int sof_link_hda_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* init IPC */ + memset(&config->hda, 0, sizeof(struct sof_ipc_dai_hda_params)); + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, config, hda_tokens, + ARRAY_SIZE(hda_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse hda tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + dev_dbg(sdev->dev, "tplg: config HDA%d fmt 0x%x\n", + config->dai_index, config->format); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for HDA%d\n", + config->dai_index); + + return ret; +} + +/* DAI link - used for any driver specific init */ +static int sof_link_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_dai_config config; + struct snd_soc_tplg_hw_config *hw_config; + struct snd_sof_dai *dai; + int ret = 0; + + link->platform_name = "sof-audio"; + link->nonatomic = true; + + /* send BE configurations to DSP */ + if (!link->no_pcm) + return 0; + + /* only support 1 config atm */ + if (le32_to_cpu(cfg->num_hw_configs) != 1) { + dev_err(sdev->dev, "error: unexpected DAI config count %d\n", + le32_to_cpu(cfg->num_hw_configs)); + return -EINVAL; + } + + /* check we have some tokens - we need at least DAI type */ + if (le32_to_cpu(private->size) == 0) { + dev_err(sdev->dev, "error: expected tokens for DAI, none found\n"); + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + + /* get any common DAI tokens */ + ret = sof_parse_tokens(scomp, &config, dai_link_tokens, + ARRAY_SIZE(dai_link_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse link tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + /* configure dai IPC message */ + hw_config = &cfg->hw_config[0]; + + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.format = le32_to_cpu(hw_config->fmt); + + /* now load DAI specific data and send IPC - type comes from token */ + switch (config.type) { + case SOF_DAI_INTEL_SSP: + ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, + &config); + break; + case SOF_DAI_INTEL_DMIC: + ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config, + &config); + break; + case SOF_DAI_INTEL_HDA: + ret = sof_link_hda_load(scomp, index, link, cfg, hw_config, + &config); + break; + default: + dev_err(sdev->dev, "error: invalid DAI type %d\n", config.type); + ret = -EINVAL; + break; + } + if (ret < 0) + return ret; + + /* set config for all DAI's with name matching the link name */ + list_for_each_entry(dai, &sdev->dai_list, list) { + if (!dai->name) + continue; + + if (strcmp(link->name, dai->name) == 0) + memcpy(&dai->dai_config, &config, + sizeof(struct sof_ipc_dai_config)); + } + + return 0; +} + +static int sof_link_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + return 0; +} + +/* bind PCM ID to host component ID */ +static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, + const char *host) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *host_widget; + + host_widget = snd_sof_find_swidget(sdev, (char *)host); + if (!host_widget) { + dev_err(sdev->dev, "error: can't find host component %s\n", + host); + return -ENODEV; + } + + switch (host_widget->id) { + case snd_soc_dapm_aif_in: + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = + host_widget->comp_id; + break; + case snd_soc_dapm_aif_out: + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = + host_widget->comp_id; + break; + default: + dev_err(sdev->dev, "error: host is wrong type %d\n", + host_widget->id); + return -EINVAL; + } + + return 0; +} + +/* DAI link - used for any driver specific init */ +static int sof_route_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_route *route) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_pipe_comp_connect *connect; + struct snd_sof_widget *source_swidget, *sink_swidget; + struct snd_sof_pcm *spcm; + struct snd_sof_route *sroute; + struct sof_ipc_reply reply; + int ret = 0; + + /* allocate memory for sroute and connect */ + sroute = kzalloc(sizeof(*sroute), GFP_KERNEL); + if (!sroute) + return -ENOMEM; + + sroute->sdev = sdev; + + connect = kzalloc(sizeof(*connect), GFP_KERNEL); + if (!connect) { + kfree(sroute); + return -ENOMEM; + } + + connect->hdr.size = sizeof(*connect); + connect->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; + + dev_dbg(sdev->dev, "sink %s control %s source %s\n", + route->sink, route->control ? route->control : "none", + route->source); + + /* source component */ + source_swidget = snd_sof_find_swidget(sdev, (char *)route->source); + if (!source_swidget) { + /* don't send any routes to DSP that include a driver PCM */ + spcm = snd_sof_find_spcm_name(sdev, (char *)route->source); + if (spcm) { + ret = spcm_bind(scomp, spcm, route->sink); + if (ret < 0) + goto err; + return 0; + } + + dev_err(sdev->dev, "error: source %s not found\n", + route->source); + ret = -EINVAL; + goto err; + } + + connect->source_id = source_swidget->comp_id; + + /* sink component */ + sink_swidget = snd_sof_find_swidget(sdev, (char *)route->sink); + if (!sink_swidget) { + /* don't send any routes to DSP that include a driver PCM */ + spcm = snd_sof_find_spcm_name(sdev, (char *)route->sink); + if (spcm) { + ret = spcm_bind(scomp, spcm, route->source); + if (ret < 0) + goto err; + return 0; + } + + dev_err(sdev->dev, "error: sink %s not found\n", + route->sink); + ret = -EINVAL; + goto err; + } + + connect->sink_id = sink_swidget->comp_id; + + /* Some virtual routes and widgets may been added in topology for + * compatibility. For virtual routes, both sink and source are not + * buffer. Since only buffer linked to component is supported by + * FW, others are reported as error, add check in route function, + * do not send it to FW when both source and sink are not buffer + */ + if (source_swidget->id != snd_soc_dapm_buffer && + sink_swidget->id != snd_soc_dapm_buffer) { + dev_dbg(sdev->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", + route->source, route->sink); + ret = 0; + goto err; + } else { + ret = sof_ipc_tx_message(sdev->ipc, + connect->hdr.cmd, + connect, sizeof(*connect), + &reply, sizeof(reply)); + + /* check IPC return value */ + if (ret < 0) { + dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", + route->sink, + route->control ? route->control : "none", + route->source); + goto err; + } + + /* check IPC reply */ + if (reply.error < 0) { + dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", + route->sink, + route->control ? route->control : "none", + route->source, reply.error); + ret = reply.error; + goto err; + } + + sroute->route = route; + sroute->private = connect; + + /* add route to route list */ + list_add(&sroute->list, &sdev->route_list); + } + + return ret; + +err: + kfree(connect); + kfree(sroute); + return ret; +} + +static int sof_route_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + /* TODO: unload routes when topology is changed */ + return 0; +} + +int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget) +{ + struct sof_ipc_pipe_ready ready; + struct sof_ipc_reply reply; + int ret; + + dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + swidget->widget->name, swidget->comp_id); + + memset(&ready, 0, sizeof(ready)); + ready.hdr.size = sizeof(ready); + ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; + ready.comp_id = swidget->comp_id; + + ret = sof_ipc_tx_message(sdev->ipc, + ready.hdr.cmd, &ready, sizeof(ready), &reply, + sizeof(reply)); + if (ret < 0) + return ret; + return 1; +} + +/* completion - called at completion of firmware loading */ +static void sof_complete(struct snd_soc_component *scomp) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + + /* some widget types require completion notificattion */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->complete) + continue; + + switch (swidget->id) { + case snd_soc_dapm_scheduler: + swidget->complete = + snd_sof_complete_pipeline(sdev, swidget); + break; + default: + break; + } + } +} + +/* manifest - optional to inform component of manifest */ +static int sof_manifest(struct snd_soc_component *scomp, int index, + struct snd_soc_tplg_manifest *man) +{ + /* not currently parsed */ + return 0; +} + +/* vendor specific kcontrol handlers available for binding */ +static const struct snd_soc_tplg_kcontrol_ops sof_io_ops[] = { + {SOF_TPLG_KCTL_VOL_ID, snd_sof_volume_get, snd_sof_volume_put}, + {SOF_TPLG_KCTL_ENUM_ID, snd_sof_enum_get, snd_sof_enum_put}, + {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_get, snd_sof_bytes_put}, +}; + +/* vendor specific bytes ext handlers available for binding */ +static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { +{}, +}; + +static struct snd_soc_tplg_ops sof_tplg_ops = { + /* external kcontrol init - used for any driver specific init */ + .control_load = sof_control_load, + .control_unload = sof_control_unload, + + /* external kcontrol init - used for any driver specific init */ + .dapm_route_load = sof_route_load, + .dapm_route_unload = sof_route_unload, + + /* external widget init - used for any driver specific init */ + .widget_load = sof_widget_load, + .widget_ready = sof_widget_ready, + .widget_unload = sof_widget_unload, + + /* FE DAI - used for any driver specific init */ + .dai_load = sof_dai_load, + .dai_unload = sof_dai_unload, + + /* DAI link - used for any driver specific init */ + .link_load = sof_link_load, + .link_unload = sof_link_unload, + + /* completion - called at completion of firmware loading */ + .complete = sof_complete, + + /* manifest - optional to inform component of manifest */ + .manifest = sof_manifest, + + /* vendor specific kcontrol handlers available for binding */ + .io_ops = sof_io_ops, + .io_ops_count = ARRAY_SIZE(sof_io_ops), + + /* vendor specific bytes ext handlers available for binding */ + .bytes_ext_ops = sof_bytes_ext_ops, + .bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops), +}; + +int snd_sof_init_topology(struct snd_sof_dev *sdev, + struct snd_soc_tplg_ops *ops) +{ + /* TODO: support linked list of topologies */ + sdev->tplg_ops = ops; + return 0; +} +EXPORT_SYMBOL(snd_sof_init_topology); + +int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file) +{ + const struct firmware *fw; + struct snd_soc_tplg_hdr *hdr; + int ret; + + dev_dbg(sdev->dev, "loading topology:%s\n", file); + + ret = request_firmware(&fw, file, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: tplg %s load failed with %d\n", + file, ret); + return ret; + } + + hdr = (struct snd_soc_tplg_hdr *)fw->data; + ret = snd_soc_tplg_component_load(sdev->component, + &sof_tplg_ops, fw, + SND_SOC_TPLG_INDEX_ALL); + if (ret < 0) { + dev_err(sdev->dev, "error: tplg component load failed %d\n", + ret); + ret = -EINVAL; + } + + release_firmware(fw); + return ret; +} +EXPORT_SYMBOL(snd_sof_load_topology); + +void snd_sof_free_topology(struct snd_sof_dev *sdev) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(sdev->component); + struct snd_sof_route *sroute, *temp; + int ret; + + dev_dbg(sdev->dev, "free topology...\n"); + + /* remove routes */ + list_for_each_entry_safe(sroute, temp, &sdev->route_list, list) { + + /* delete dapm route */ + snd_soc_dapm_del_routes(dapm, sroute->route, 1); + + /* free sroute and its private data */ + kfree(sroute->private); + kfree(sroute); + } + + ret = snd_soc_tplg_component_remove(sdev->component, + SND_SOC_TPLG_INDEX_ALL); + if (ret < 0) + dev_err(sdev->dev, + "error: tplg component free failed %d\n", ret); +} +EXPORT_SYMBOL(snd_sof_free_topology); From 987590dfa3cc0785711efb3a9a39043d4d8164ac Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:15 +0000 Subject: [PATCH 024/298] ASoC: SOF: Add DSP firmware trace event support Add a trace event buffer that can be used by userspace to read DSP runtime trace events alongside bespoke trace data in realtime for firmware debug. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-priv.h | 1 + sound/soc/sof/trace.c | 293 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 sound/soc/sof/trace.c diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index dc9809ac84f815..d9d62461c06d22 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -357,6 +357,7 @@ struct snd_sof_dev { wait_queue_head_t trace_sleep; u32 host_offset; bool dtrace_is_enabled; + bool dtrace_error; void *private; /* core does not touch this */ }; diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c new file mode 100644 index 00000000000000..172ff2500d5d8b --- /dev/null +++ b/sound/soc/sof/trace.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Yan Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, + loff_t pos, size_t buffer_size) +{ + wait_queue_entry_t wait; + + /* + * If host offset is less than local pos, it means write pointer of + * host DMA buffer has been wrapped. We should output the trace data + * at the end of host DMA buffer at first. + */ + if (sdev->host_offset < pos) + return buffer_size - pos; + + /* If there is available trace data now, it is unnecessary to wait. */ + if (sdev->host_offset > pos) + return sdev->host_offset - pos; + + /* wait for available trace data from FW */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&sdev->trace_sleep, &wait); + + if (signal_pending(current)) { + remove_wait_queue(&sdev->trace_sleep, &wait); + goto out; + } + + /* set timeout to max value, no error code */ + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + remove_wait_queue(&sdev->trace_sleep, &wait); + +out: + /* return bytes available for copy */ + if (sdev->host_offset < pos) + return buffer_size - pos; + else + return sdev->host_offset - pos; +} + +static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry_buf *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + unsigned long rem; + loff_t lpos = *ppos; + size_t avail, buffer_size = dfse->size; + u64 lpos_64; + + /* make sure we know about any failures on the DSP side */ + sdev->dtrace_error = false; + + /* check pos and count */ + if (lpos < 0) + return -EINVAL; + if (!count) + return 0; + + /* check for buffer wrap and count overflow */ + lpos_64 = lpos; + lpos = do_div(lpos_64, buffer_size); + + if (count > buffer_size - lpos) + count = buffer_size - lpos; + + /* get available count based on current host offset */ + avail = sof_wait_trace_avail(sdev, lpos, buffer_size); + if (sdev->dtrace_error) { + dev_err(sdev->dev, "error: trace IO error\n"); + return -EIO; + } + + /* make sure count is <= avail */ + count = avail > count ? count : avail; + + /* copy available trace data to debugfs */ + rem = copy_to_user(buffer, dfse->buf + lpos, count); + if (rem == count) + return -EFAULT; + + *ppos += count; + + /* move debugfs reading position */ + return count; +} + +static const struct file_operations sof_dfs_trace_fops = { + .open = simple_open, + .read = sof_dfsentry_trace_read, + .llseek = default_llseek, +}; + +static int trace_debugfs_create(struct snd_sof_dev *sdev) +{ + struct snd_sof_dfsentry_buf *dfse; + + if (!sdev) + return -EINVAL; + + dfse = kzalloc(sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->buf = sdev->dmatb.area; + dfse->size = sdev->dmatb.bytes; + dfse->sdev = sdev; + + dfse->dfsentry = debugfs_create_file("trace", 0444, sdev->debugfs_root, + dfse, &sof_dfs_trace_fops); + if (!dfse->dfsentry) { + dev_err(sdev->dev, + "error: cannot create debugfs entry for trace\n"); + kfree(dfse); + return -ENODEV; + } + + return 0; +} + +int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) +{ + struct sof_ipc_dma_trace_params params; + struct sof_ipc_reply ipc_reply; + int ret; + + /* set IPC parameters */ + params.hdr.size = sizeof(params); + params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS; + params.buffer.phy_addr = sdev->dmatp.addr; + params.buffer.size = sdev->dmatb.bytes; + params.buffer.offset = 0; + params.buffer.pages = sdev->dma_trace_pages; + + init_waitqueue_head(&sdev->trace_sleep); + sdev->host_offset = 0; + + ret = snd_sof_dma_trace_init(sdev, ¶ms.stream_tag); + if (ret < 0) { + dev_err(sdev->dev, + "error: fail in snd_sof_dma_trace_init %d\n", ret); + return ret; + } + dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + params.hdr.cmd, ¶ms, sizeof(params), + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't set params for DMA for trace %d\n", ret); + return ret; + } + + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, + "error: snd_sof_dma_trace_trigger: start: %d\n", ret); + return ret; + } + + sdev->dtrace_is_enabled = true; + + return 0; +} + +int snd_sof_init_trace(struct snd_sof_dev *sdev) +{ + int ret; + + /* set false before start initialization */ + sdev->dtrace_is_enabled = false; + + /* allocate trace page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &sdev->dmatp); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't alloc page table for trace %d\n", ret); + return ret; + } + + /* allocate trace data buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + DMA_BUF_SIZE_FOR_TRACE, &sdev->dmatb); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't alloc buffer for trace %d\n", ret); + goto page_err; + } + + /* craete compressed page table for audio firmware */ + ret = snd_sof_create_page_table(sdev, &sdev->dmatb, sdev->dmatp.area, + sdev->dmatb.bytes); + if (ret < 0) + goto table_err; + + sdev->dma_trace_pages = ret; + dev_dbg(sdev->dev, "dma_trace_pages: %d\n", sdev->dma_trace_pages); + + ret = trace_debugfs_create(sdev); + if (ret < 0) + goto table_err; + + ret = snd_sof_init_trace_ipc(sdev); + if (ret < 0) + goto table_err; + + return 0; +table_err: + snd_dma_free_pages(&sdev->dmatb); +page_err: + snd_dma_free_pages(&sdev->dmatp); + return ret; +} +EXPORT_SYMBOL(snd_sof_init_trace); + +int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn) +{ + if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { + sdev->host_offset = posn->host_offset; + wake_up(&sdev->trace_sleep); + } + + if (posn->overflow != 0) + dev_err(sdev->dev, + "error: DSP trace buffer overflow %u bytes. Total messages %d\n", + posn->overflow, posn->messages); + + return 0; +} + +/* an error has occurred within the DSP that prevents further trace */ +void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) +{ + if (sdev->dtrace_is_enabled) { + dev_err(sdev->dev, "error: waking up any trace sleepers\n"); + sdev->dtrace_error = true; + wake_up(&sdev->trace_sleep); + } +} +EXPORT_SYMBOL(snd_sof_trace_notify_for_error); + +void snd_sof_release_trace(struct snd_sof_dev *sdev) +{ + int ret; + + if (!sdev->dtrace_is_enabled) + return; + + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) + dev_err(sdev->dev, + "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); + + ret = snd_sof_dma_trace_release(sdev); + if (ret < 0) + dev_err(sdev->dev, + "error: fail in snd_sof_dma_trace_release %d\n", ret); + + snd_dma_free_pages(&sdev->dmatb); + snd_dma_free_pages(&sdev->dmatp); +} +EXPORT_SYMBOL(snd_sof_release_trace); From 52850c70a54bf9d31c6cd849f79e7316bc889b6a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:43 +0000 Subject: [PATCH 025/298] ASoC: SOF: Add DSP HW abstraction operations Add operation pointers that can be called by core to control a wide variety of DSP targets. The DSP HW drivers will fill in these operations. Signed-off-by: Liam Girdwood --- sound/soc/sof/ops.c | 210 +++++++++++++++++++++++++++++++ sound/soc/sof/ops.h | 298 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 508 insertions(+) create mode 100644 sound/soc/sof/ops.c create mode 100644 sound/soc/sof/ops.h diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c new file mode 100644 index 00000000000000..d1ea6a0c5f6229 --- /dev/null +++ b/sound/soc/sof/ops.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ops.h" +#include "sof-priv.h" + +int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret = ~0; /* explicit init to remove uninitialized use warnings */ + + pci_read_config_dword(sdev->pci, offset, &ret); + dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", + pci_read_config_dword(sdev->pci, offset, &ret), offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) { + pci_write_config_dword(sdev->pci, offset, new); + dev_dbg(sdev->dev, "Debug PCIW: %8.8x at %8.8x\n", value, + offset); + } + + return change; +} +EXPORT_SYMBOL(snd_sof_pci_update_bits_unlocked); + +int snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_pci_update_bits); + +int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + ret = snd_sof_dsp_read(sdev, bar, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + snd_sof_dsp_write(sdev, bar, offset, new); + + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_unlocked); + +int snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value) +{ + bool change; + u64 old, new; + + old = snd_sof_dsp_read64(sdev, bar, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + snd_sof_dsp_write64(sdev, bar, offset, new); + + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + unsigned int old, new; + u32 ret; + + ret = snd_sof_dsp_read(sdev, bar, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + snd_sof_dsp_write(sdev, bar, offset, new); +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced_unlocked); + +int snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, + value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits); + +int snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u64 mask, u64 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, + value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits64); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sdev->hw_lock, flags); + snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); + +int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 target, u32 timeout) +{ + int time, ret; + bool done = false; + + /* + * we will poll for couple of ms using mdelay, if not successful + * then go to longer sleep using usleep_range + */ + + /* check if set state successful */ + for (time = 0; time < 5; time++) { + if ((snd_sof_dsp_read(sdev, bar, offset) & mask) == target) { + done = true; + break; + } + msleep(20); + } + + if (!done) { + /* sleeping in 10ms steps so adjust timeout value */ + timeout /= 10; + + for (time = 0; time < timeout; time++) { + if ((snd_sof_dsp_read(sdev, bar, offset) & mask) == + target) + break; + + usleep_range(5000, 10000); + } + } + + ret = time < timeout ? 0 : -ETIME; + + return ret; +} +EXPORT_SYMBOL(snd_sof_dsp_register_poll); + +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) +{ + dev_err(sdev->dev, "error : DSP panic!\n"); + + /* check if DSP is not ready and did not set the dsp_oops_offset. + * if the dsp_oops_offset is not set, set it from the panic message. + * Also add a check to memory window setting with panic message. + */ + if (!sdev->dsp_oops_offset) + sdev->dsp_oops_offset = offset; + else + dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", + sdev->dsp_oops_offset, offset); + + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_trace_notify_for_error(sdev); + snd_sof_dsp_cmd_done(sdev, SOF_IPC_HOST_REPLY); +} +EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h new file mode 100644 index 00000000000000..0d6fa6ef1a3e8b --- /dev/null +++ b/sound/soc/sof/ops.h @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOUND_SOC_SOF_IO_H +#define __SOUND_SOC_SOF_IO_H + +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* init */ +static inline int snd_sof_probe(struct snd_sof_dev *sdev) +{ + if (sdev->ops->probe) + return sdev->ops->probe(sdev); + else + return 0; +} + +static inline int snd_sof_remove(struct snd_sof_dev *sdev) +{ + if (sdev->ops->remove) + return sdev->ops->remove(sdev); + else + return 0; +} + +/* control */ +static inline int snd_sof_dsp_run(struct snd_sof_dev *sdev) +{ + if (sdev->ops->run) + return sdev->ops->run(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_stall(struct snd_sof_dev *sdev) +{ + if (sdev->ops->stall) + return sdev->ops->stall(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev) +{ + if (sdev->ops->reset) + return sdev->ops->reset(sdev); + else + return 0; +} + +/* power management */ +static inline int snd_sof_dsp_resume(struct snd_sof_dev *sdev) +{ + if (sdev->ops->resume) + return sdev->ops->resume(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_suspend(struct snd_sof_dev *sdev, int state) +{ + if (sdev->ops->suspend) + return sdev->ops->suspend(sdev, state); + else + return 0; +} + +static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) +{ + if (sdev->ops->set_clk) + return sdev->ops->set_clk(sdev, freq); + else + return 0; +} + +/* debug */ +static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) +{ + if (sdev->ops->dbg_dump) + return sdev->ops->dbg_dump(sdev, flags); +} + +/* register IO */ +static inline void snd_sof_dsp_write(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 value) +{ + if (sdev->ops->write) + sdev->ops->write(sdev, sdev->bar[bar] + offset, value); +} + +static inline void snd_sof_dsp_write64(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 value) +{ + if (sdev->ops->write64) + sdev->ops->write64(sdev, + sdev->bar[bar] + offset, value); +} + +static inline u32 snd_sof_dsp_read(struct snd_sof_dev *sdev, u32 bar, + u32 offset) +{ + if (sdev->ops->read) + return sdev->ops->read(sdev, sdev->bar[bar] + offset); + else + return 0; +} + +static inline u64 snd_sof_dsp_read64(struct snd_sof_dev *sdev, u32 bar, + u32 offset) +{ + if (sdev->ops->read64) + return sdev->ops->read64(sdev, sdev->bar[bar] + offset); + else + return 0; +} + +/* block IO */ +static inline void snd_sof_dsp_block_read(struct snd_sof_dev *sdev, + u32 offset, void *dest, size_t bytes) +{ + if (sdev->ops->block_read) + sdev->ops->block_read(sdev, offset, dest, bytes); +} + +static inline void snd_sof_dsp_block_write(struct snd_sof_dev *sdev, + u32 offset, void *src, size_t bytes) +{ + if (sdev->ops->block_write) + sdev->ops->block_write(sdev, offset, src, bytes); +} + +/* mailbox */ +static inline void snd_sof_dsp_mailbox_read(struct snd_sof_dev *sdev, + u32 offset, void *message, + size_t bytes) +{ + if (sdev->ops->mailbox_read) + sdev->ops->mailbox_read(sdev, offset, message, bytes); +} + +static inline void snd_sof_dsp_mailbox_write(struct snd_sof_dev *sdev, + u32 offset, void *message, + size_t bytes) +{ + if (sdev->ops->mailbox_write) + sdev->ops->mailbox_write(sdev, offset, message, bytes); +} + +/* ipc */ +static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + if (sdev->ops->send_msg) + return sdev->ops->send_msg(sdev, msg); + else + return 0; +} + +static inline int snd_sof_dsp_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + if (sdev->ops->get_reply) + return sdev->ops->get_reply(sdev, msg); + else + return 0; +} + +static inline int snd_sof_dsp_is_ready(struct snd_sof_dev *sdev) +{ + if (sdev->ops->is_ready) + return sdev->ops->is_ready(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_cmd_done(struct snd_sof_dev *sdev, + int dir) +{ + if (sdev->ops->cmd_done) + return sdev->ops->cmd_done(sdev, dir); + else + return 0; +} + +/* host DMA trace */ +static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, + u32 *stream_tag) +{ + if (sdev->ops->trace_init) + return sdev->ops->trace_init(sdev, stream_tag); + else + return 0; +} + +static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev) +{ + if (sdev->ops->trace_release) + return sdev->ops->trace_release(sdev); + else + return 0; +} + +static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) +{ + if (sdev->ops->trace_trigger) + return sdev->ops->trace_trigger(sdev, cmd); + else + return 0; +} + +/* host PCM ops */ +static inline int +snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sdev->ops && sdev->ops->pcm_open) + return sdev->ops->pcm_open(sdev, substream); + else + return 0; +} + +/* disconnect pcm substream to a host stream */ +static inline int +snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sdev->ops && sdev->ops->pcm_close) + return sdev->ops->pcm_close(sdev, substream); + else + return 0; +} + +/* host stream hw params */ +static inline int +snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + if (sdev->ops && sdev->ops->pcm_hw_params) + return sdev->ops->pcm_hw_params(sdev, substream, params); + else + return 0; +} + +/* host stream trigger */ +static inline int +snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + if (sdev->ops && sdev->ops->pcm_trigger) + return sdev->ops->pcm_trigger(sdev, substream, cmd); + else + return 0; +} + +int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value); + +int snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value); + +int snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value); + +int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 target, u32 timeout); + +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); +#endif From d7e8241e7eb22982daba83c874238ddb10a8b19b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:34:01 +0000 Subject: [PATCH 026/298] ASoC: SOF: Add firmware loader support The firmware loader exports APIs that can be called by core to load and process multiple different file formats. Signed-off-by: Liam Girdwood --- sound/soc/sof/loader.c | 287 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 sound/soc/sof/loader.c diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c new file mode 100644 index 00000000000000..210a8d30989f21 --- /dev/null +++ b/sound/soc/sof/loader.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * + * Generic firmware loader. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int get_ext_windows(struct snd_sof_dev *sdev, + struct sof_ipc_ext_data_hdr *ext_hdr) +{ + struct sof_ipc_window *w = (struct sof_ipc_window *)ext_hdr; + + int ret = 0; + size_t size; + + if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) + return -EINVAL; + + size = sizeof(*w) + sizeof(struct sof_ipc_window_elem) * w->num_windows; + + /* keep a local copy of the data */ + sdev->info_window = kmemdup(w, size, GFP_KERNEL); + if (!sdev->info_window) + return -ENOMEM; + + return ret; +} + +/* parse the extended FW boot data structures from FW boot message */ +int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) +{ + struct sof_ipc_ext_data_hdr *ext_hdr; + void *ext_data; + int ret = 0; + + ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!ext_data) + return -ENOMEM; + + /* get first header */ + snd_sof_dsp_block_read(sdev, offset, ext_data, sizeof(*ext_hdr)); + ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data; + + while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { + /* read in ext structure */ + offset += sizeof(*ext_hdr); + snd_sof_dsp_block_read(sdev, offset, + ext_data + sizeof(*ext_hdr), + ext_hdr->hdr.size - sizeof(*ext_hdr)); + + dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", + ext_hdr->type, ext_hdr->hdr.size); + + /* process structure data */ + switch (ext_hdr->type) { + case SOF_IPC_EXT_DMA_BUFFER: + break; + case SOF_IPC_EXT_WINDOW: + ret = get_ext_windows(sdev, ext_hdr); + break; + default: + break; + } + + if (ret < 0) { + dev_err(sdev->dev, "error: failed to parse ext data type %d\n", + ext_hdr->type); + } + + /* move to next header */ + offset += ext_hdr->hdr.size; + snd_sof_dsp_block_read(sdev, offset, ext_data, + sizeof(*ext_hdr)); + ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data; + } + + kfree(ext_data); + return ret; +} +EXPORT_SYMBOL(snd_sof_fw_parse_ext_data); + +/* generic module parser for mmaped DSPs */ +int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, + struct snd_sof_mod_hdr *module) +{ + struct snd_sof_blk_hdr *block; + int count; + u32 offset; + + dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", + module->size, module->num_blocks, module->type); + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->num_blocks; count++) { + if (block->size == 0) { + dev_warn(sdev->dev, + "warning: block %d size zero\n", count); + dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", + block->type, block->offset); + continue; + } + + switch (block->type) { + case SOF_BLK_IMAGE: + case SOF_BLK_CACHE: + case SOF_BLK_REGS: + case SOF_BLK_SIG: + case SOF_BLK_ROM: + continue; /* not handled atm */ + case SOF_BLK_TEXT: + case SOF_BLK_DATA: + offset = block->offset; + break; + default: + dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", + block->type, count); + return -EINVAL; + } + + dev_dbg(sdev->dev, + "block %d type 0x%x size 0x%x ==> offset 0x%x\n", + count, block->type, block->size, offset); + + snd_sof_dsp_block_write(sdev, offset, + (void *)block + sizeof(*block), + block->size); + + /* next block */ + block = (void *)block + sizeof(*block) + block->size; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_parse_module_memcpy); + +static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_fw_header *header; + + /* Read the header information from the data pointer */ + header = (struct snd_sof_fw_header *)fw->data; + + /* verify FW sig */ + if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { + dev_err(sdev->dev, "error: invalid firmware signature\n"); + return -EINVAL; + } + + /* check size is valid */ + if (fw->size != header->file_size + sizeof(*header)) { + dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", + fw->size, header->file_size + sizeof(*header)); + return -EINVAL; + } + + dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", + header->file_size, header->num_modules, + header->abi, sizeof(*header)); + + return 0; +} + +static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_fw_header *header; + struct snd_sof_mod_hdr *module; + int (*load_module)(struct snd_sof_dev *sof_dev, + struct snd_sof_mod_hdr *hdr); + int ret, count; + + header = (struct snd_sof_fw_header *)fw->data; + load_module = sdev->ops->load_module; + if (!load_module) + return -EINVAL; + + /* parse each module */ + module = (void *)fw->data + sizeof(*header); + for (count = 0; count < header->num_modules; count++) { + /* module */ + ret = load_module(sdev, module); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid module %d\n", count); + return ret; + } + module = (void *)module + sizeof(*module) + module->size; + } + + return 0; +} + +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, + const struct firmware *fw, bool first_boot) +{ + int ret; + + /* make sure the FW header and file is valid */ + ret = check_header(sdev, fw); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid FW header\n"); + return ret; + } + + /* prepare the DSP for FW loading */ + ret = snd_sof_dsp_reset(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to reset DSP\n"); + return ret; + } + + /* parse and load firmware modules to DSP */ + ret = load_modules(sdev, fw); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid FW modules\n"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); + +int snd_sof_load_firmware(struct snd_sof_dev *sdev, + const struct firmware *fw, bool first_boot) +{ + dev_dbg(sdev->dev, "loading firmware\n"); + + if (sdev->ops->load_firmware) + return sdev->ops->load_firmware(sdev, fw, first_boot); + return 0; +} +EXPORT_SYMBOL(snd_sof_load_firmware); + +int snd_sof_run_firmware(struct snd_sof_dev *sdev) +{ + int ret; + + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + dev_dbg(sdev->dev, "booting DSP firmware\n"); + + /* boot the firmware on the DSP */ + ret = snd_sof_dsp_run(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to reset DSP\n"); + return ret; + } + + /* now wait for the DSP to boot */ + ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete, + msecs_to_jiffies(sdev->boot_timeout)); + if (ret == 0) { + dev_err(sdev->dev, "error: firmware boot timeout\n"); + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | + SOF_DBG_TEXT | SOF_DBG_PCI); + return -EIO; + } + + dev_info(sdev->dev, "firmware boot complete\n"); + + return 0; +} +EXPORT_SYMBOL(snd_sof_run_firmware); + +void snd_sof_fw_unload(struct snd_sof_dev *sdev) +{ + /* TODO: support module unloading at runtime */ +} +EXPORT_SYMBOL(snd_sof_fw_unload); From cc20534d4cb2760a4cb7bbef565a6d13ec9c1191 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:39:03 +0000 Subject: [PATCH 027/298] ASoC: SOF: Add userspace ABI support Add userspace ABI for audio userspace application IO outside of regular ALSA PCM and kcontrols. This is intended to be used to format coefficients and data for custom processing components. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-abi.h | 29 ++++++++++ include/uapi/sound/sof-eq.h | 102 ++++++++++++++++++++++++++++++++++ include/uapi/sound/sof-fw.h | 69 +++++++++++++++++++++++ include/uapi/sound/sof-tone.h | 28 ++++++++++ 4 files changed, 228 insertions(+) create mode 100644 include/uapi/sound/sof-abi.h create mode 100644 include/uapi/sound/sof-eq.h create mode 100644 include/uapi/sound/sof-fw.h create mode 100644 include/uapi/sound/sof-tone.h diff --git a/include/uapi/sound/sof-abi.h b/include/uapi/sound/sof-abi.h new file mode 100644 index 00000000000000..4e78fa14465e35 --- /dev/null +++ b/include/uapi/sound/sof-abi.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + */ + +#ifndef __INCLUDE_UAPI_ABI_H__ +#define __INCLUDE_UAPI_ABI_H__ + +#define SOF_ABI_VERSION 1 +#define SOF_ABI_MAGIC 0x00464F53 /* "SOF\0" */ + +/* + * Header for all non IPC ABI data. Identifies data type, size and ABI. + * Used by any bespoke component data structures or binary blobs. + */ + +struct sof_abi_hdr { + uint32_t magic; /* 'S', 'O', 'F', '\0' */ + uint32_t type; /* component specific type */ + uint32_t size; /* size in bytes of data excluding this struct */ + uint32_t abi; /* SOF ABI version */ + uint32_t comp_abi; /* component specific ABI version */ + char data[0]; +} __attribute__((packed)); + +#endif diff --git a/include/uapi/sound/sof-eq.h b/include/uapi/sound/sof-eq.h new file mode 100644 index 00000000000000..96aefd1e1950b9 --- /dev/null +++ b/include/uapi/sound/sof-eq.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ + +#ifndef EQ_H +#define EQ_H + +/* FIR EQ type */ + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_EQ_FIR_ABI_VERSION 1 + +#define SOF_EQ_FIR_IDX_SWITCH 0 + +#define SOF_EQ_FIR_MAX_SIZE 4096 /* Max size allowed for coef data in bytes */ + +/* + * eq_fir_configuration data structure contains this information + * uint16_t channels_in_config + * This describes the number of channels in this EQ config data. It + * can be different from PLATFORM_MAX_CHANNELS. + * uint16_t number_of_responses + * 0=no responses, 1=one response defined, 2=two responses defined, etc. + * int16_t data[] + * assign_response[STREAM_MAX_CHANNELS] + * -1 = not defined, 0 = use first response, 1 = use 2nd response, etc. + * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the + * same first defined response and leave channels 4-7 unequalized. + * coef_data[] + * Repeated data { filter_length, input_shift, output_shift, h[] } + * for every EQ response defined where vector h has filter_length + * number of coefficients. Coefficients in h[] are in Q1.15 format. + * E.g. 16384 (Q1.15) = 0.5. The shifts are number of right shifts. + */ + +struct sof_eq_fir_config { + uint16_t channels_in_config; + uint16_t number_of_responses; + int16_t data[]; +}; + +/* IIR EQ type */ + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_EQ_FIR_ABI_VERSION 1 + +#define SOF_EQ_IIR_IDX_SWITCH 0 + +#define SOF_EQ_IIR_MAX_SIZE 1024 /* Max size allowed for coef data in bytes */ + +/* eq_iir_configuration + * uint32_t channels_in_config + * This describes the number of channels in this EQ config data. It + * can be different from PLATFORM_MAX_CHANNELS. + * uint32_t number_of_responses_defined + * 0=no responses, 1=one response defined, 2=two responses defined, etc. + * int32_t data[] + * Data consist of two parts. First is the response assign vector that + * has length of channels_in_config. The latter part is coefficient + * data. + * uint32_t assign_response[channels_in_config] + * -1 = not defined, 0 = use first response, 1 = use 2nd, etc. + * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the + * same first defined response and leave channels 4-7 unequalized. + * coefficient_data[] + * <1st EQ> + * uint32_t num_biquads + * uint32_t num_biquads_in_series + * <1st biquad> + * int32_t coef_a2 Q2.30 format + * int32_t coef_a1 Q2.30 format + * int32_t coef_b2 Q2.30 format + * int32_t coef_b1 Q2.30 format + * int32_t coef_b0 Q2.30 format + * int32_t output_shift number of shifts right, shift left is negative + * int32_t output_gain Q2.14 format + * <2nd biquad> + * ... + * <2nd EQ> + * + * Note: A flat response biquad can be made with a section set to + * b0 = 1.0, gain = 1.0, and other parameters set to 0 + * {0, 0, 0, 0, 1073741824, 0, 16484} + */ + +struct sof_eq_iir_config { + uint32_t channels_in_config; + uint32_t number_of_responses; + int32_t data[]; +}; + +#endif /* EQ_H */ diff --git a/include/uapi/sound/sof-fw.h b/include/uapi/sound/sof-fw.h new file mode 100644 index 00000000000000..2f9bd827d2f02d --- /dev/null +++ b/include/uapi/sound/sof-fw.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Firmware file format . + */ + +#ifndef __INCLUDE_UAPI_SOF_FW_H__ +#define __INCLUDE_UAPI_SOF_FW_H__ + +#define SND_SOF_FW_SIG_SIZE 4 +#define SND_SOF_FW_ABI 1 +#define SND_SOF_FW_SIG "Reef" + +/* + * Firmware module is made up of 1 . N blocks of different types. The + * Block header is used to determine where and how block is to be copied in the + * DSP/host memory space. + */ +enum snd_sof_fw_blk_type { + SOF_BLK_IMAGE = 0, /* whole image - parsed by ROMs */ + SOF_BLK_TEXT = 1, + SOF_BLK_DATA = 2, + SOF_BLK_CACHE = 3, + SOF_BLK_REGS = 4, + SOF_BLK_SIG = 5, + SOF_BLK_ROM = 6, + /* add new block types here */ +}; + +struct snd_sof_blk_hdr { + enum snd_sof_fw_blk_type type; + uint32_t size; /* bytes minus this header */ + uint32_t offset; /* offset from base */ +} __attribute__((packed)); + +/* + * Firmware file is made up of 1 .. N different modules types. The module + * type is used to determine how to load and parse the module. + */ +enum snd_sof_fw_mod_type { + SOF_FW_BASE = 0, /* base firmware image */ + SOF_FW_MODULE = 1, /* firmware module */ +}; + +struct snd_sof_mod_hdr { + enum snd_sof_fw_mod_type type; + uint32_t size; /* bytes minus this header */ + uint32_t num_blocks; /* number of blocks */ +} __attribute__((packed)); + +/* + * Firmware file header. + */ +struct snd_sof_fw_header { + unsigned char sig[SND_SOF_FW_SIG_SIZE]; /* "Reef" */ + uint32_t file_size; /* size of file minus this header */ + uint32_t num_modules; /* number of modules */ + uint32_t abi; /* version of header format */ +} __attribute__((packed)); + +#endif diff --git a/include/uapi/sound/sof-tone.h b/include/uapi/sound/sof-tone.h new file mode 100644 index 00000000000000..f9c9bf5c482d85 --- /dev/null +++ b/include/uapi/sound/sof-tone.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ + +#ifndef TONE_H +#define TONE_H + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_TONE_ABI_VERSION 1 + +#define SOF_TONE_IDX_FREQUENCY 0 +#define SOF_TONE_IDX_AMPLITUDE 1 +#define SOF_TONE_IDX_FREQ_MULT 2 +#define SOF_TONE_IDX_AMPL_MULT 3 +#define SOF_TONE_IDX_LENGTH 4 +#define SOF_TONE_IDX_PERIOD 5 +#define SOF_TONE_IDX_REPEATS 6 +#define SOF_TONE_IDX_LIN_RAMP_STEP 7 + +#endif /* TONE_ABI_H */ From 8898b5667401f7f5a04eda49f606058ef9dd6af7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:34:16 +0000 Subject: [PATCH 028/298] ASoC: SOF: Add compressed PCM support Add support for compressed audio playback/capture in SOF. TODO: to be completed. Signed-off-by: Liam Girdwood --- sound/soc/sof/compressed.c | 180 +++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 sound/soc/sof/compressed.c diff --git a/sound/soc/sof/compressed.c b/sound/soc/sof/compressed.c new file mode 100644 index 00000000000000..ca830c216db0a7 --- /dev/null +++ b/sound/soc/sof/compressed.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +#define DRV_NAME "sof-audio" + +static int sof_compressed_open(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + + mutex_lock(&spcm->mutex); + pm_runtime_get_sync(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_compressed_free(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + + mutex_lock(&spcm->mutex); + pm_runtime_put(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_vorbis_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + return 0; +} + +static int sof_mp3_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + return 0; +} + +static int sof_compressed_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + + switch (params->codec.id) { + case SND_AUDIOCODEC_VORBIS: + return sof_vorbis_set_params(cstream, params); + case SND_AUDIOCODEC_MP3: + return sof_mp3_set_params(cstream, params); + default: + dev_err(sdev->dev, "error: codec id %d not supported\n", + params->codec.id); + return -EINVAL; + } +} + +static int sof_compressed_trigger(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[cstream->direction].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_RESUME: + default: + break; + } + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); +} + +static int sof_compressed_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = + snd_soc_component_get_drvdata(component); + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm = rtd->sof; + + snd_sof_ipc_stream_posn(sdev, spcm, cstream->direction, &posn); + + dev_vdbg(sdev->dev, "CPCM: DMA position %llu DAI position %llu\n", + posn.host_posn, posn.dai_posn); + + return 0; +} + +static int sof_compressed_ack(struct snd_compr_stream *cstream, + size_t bytes) +{ + return 0; +} + +static int sof_compressed_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + return 0; +} + +static int sof_compressed_get_codec_caps(struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec) +{ + return 0; +} + +static int sof_compressed_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + return 0; +} + +struct snd_compr_ops sof_compressed_ops = { + .open = sof_compressed_open, + .free = sof_compressed_free, + .set_params = sof_compressed_set_params, + .set_metadata = sof_compressed_set_metadata, + .trigger = sof_compressed_trigger, + .pointer = sof_compressed_pointer, + .ack = sof_compressed_ack, + .get_caps = sof_compressed_get_caps, + .get_codec_caps = sof_compressed_get_codec_caps, +}; From ac492e96dc49d3584b0f9f649b1043bd5e9391f4 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 16 Jan 2018 15:36:57 +0000 Subject: [PATCH 029/298] ASoC: SOF: Add PM support Add support for saving and restoring DSP context in D3 to host DDR. The suspend callback includes: suspend all pcm's stream that are running, send CTX_SAVE ipc, drop all ipc's, release trace dma and then power off the DSP. And the resume callback performs the following steps: load FW, run FW, re-initialize trace, restore pipeline, restore the kcontrol values and finally send the ctx restore ipc to the dsp. The streams that are suspended are resumed by the ALSA resume trigger. If the streams are paused during system suspend, they are marked explicitly so they can be restored during PAUSE_RELEASE. Signed-off-by: Liam Girdwood --- sound/soc/sof/pm.c | 344 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 sound/soc/sof/pm.c diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c new file mode 100644 index 00000000000000..75c1a71feb7981 --- /dev/null +++ b/sound/soc/sof/pm.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ops.h" +#include "sof-priv.h" + +static int sof_restore_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_widget *swidget = NULL; + struct snd_sof_route *sroute = NULL; + struct snd_sof_dai *dai; + struct sof_ipc_comp_dai *comp_dai; + struct sof_ipc_hdr *hdr; + struct snd_sof_control *scontrol = NULL; + int ret = 0; + + /* restore pipeline components */ + list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { + struct sof_ipc_comp_reply r; + + /* skip if there is no private data */ + if (!swidget->private) + continue; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + /* fallthrough */ + case snd_soc_dapm_dai_out: + dai = (struct snd_sof_dai *)swidget->private; + comp_dai = &dai->comp_dai; + ret = sof_ipc_tx_message(sdev->ipc, + comp_dai->comp.hdr.cmd, + comp_dai, sizeof(*comp_dai), + &r, sizeof(r)); + break; + default: + hdr = (struct sof_ipc_hdr *)swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, + swidget->private, hdr->size, + &r, sizeof(r)); + break; + } + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to load widget type %d with ID: %d\n", + swidget->widget->id, swidget->comp_id); + + return ret; + } + } + + /* restore pipeline connections */ + list_for_each_entry_reverse(sroute, &sdev->route_list, list) { + struct sof_ipc_pipe_comp_connect *connect; + struct sof_ipc_reply reply; + + /* skip if there's no private data */ + if (!sroute->private) + continue; + + connect = sroute->private; + + /* send ipc */ + ret = sof_ipc_tx_message(sdev->ipc, + connect->hdr.cmd, + connect, sizeof(*connect), + &reply, sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to load route sink %s control %s source %s\n", + sroute->route->sink, + sroute->route->control ? sroute->route->control + : "none", + sroute->route->source); + + return ret; + } + } + + /* restore dai links */ + list_for_each_entry_reverse(dai, &sdev->dai_list, list) { + struct sof_ipc_reply reply; + struct sof_ipc_dai_config *config = &dai->dai_config; + + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, + sizeof(*config), + &reply, sizeof(reply)); + + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set dai config for %s\n", + dai->name); + + return ret; + } + } + + /* complete pipeline */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + switch (swidget->id) { + case snd_soc_dapm_scheduler: + swidget->complete = + snd_sof_complete_pipeline(sdev, swidget); + break; + default: + break; + } + } + + /* restore kcontrol values */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + + /* notify DSP of kcontrol values */ + ret = snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + scontrol->cmd); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed kcontrol value set for widget: %d\n", + scontrol->comp_id); + + return ret; + } + } + + return 0; +} + +static int sof_send_pm_ipc(struct snd_sof_dev *sdev, int cmd) +{ + struct sof_ipc_pm_ctx pm_ctx; + + memset(&pm_ctx, 0, sizeof(pm_ctx)); + + /* configure ctx save ipc message */ + pm_ctx.hdr.size = sizeof(pm_ctx); + pm_ctx.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd; + + /* send ctx save ipc to dsp */ + return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, + sizeof(pm_ctx), &pm_ctx, sizeof(pm_ctx)); +} + +static void sof_suspend_streams(struct snd_sof_dev *sdev) +{ + struct snd_sof_pcm *spcm; + struct snd_pcm_substream *substream; + int state, dir; + + /* suspend all running streams */ + list_for_each_entry(spcm, &sdev->pcm_list, list) { + mutex_lock(&spcm->mutex); + + /* suspend running playback stream */ + dir = SNDRV_PCM_STREAM_PLAYBACK; + substream = spcm->stream[dir].substream; + + if (substream && substream->runtime) { + state = substream->runtime->status->state; + snd_pcm_suspend(substream); + + /* + * set restore_stream so that hw_params can be + * restored during resume + */ + spcm->restore_stream[dir] = 1; + } + + /* suspend running capture stream */ + dir = SNDRV_PCM_STREAM_CAPTURE; + substream = spcm->stream[dir].substream; + + if (substream && substream->runtime) { + state = substream->runtime->status->state; + snd_pcm_suspend(substream); + + /* + * set restore_stream so that hw_params can be + * restored during resume + */ + spcm->restore_stream[dir] = 1; + } + + mutex_unlock(&spcm->mutex); + } +} + +static int sof_resume(struct device *dev) +{ + struct sof_platform_priv *priv = dev_get_drvdata(dev); + struct snd_sof_dev *sdev = dev_get_drvdata(&priv->pdev_pcm->dev); + int ret = 0; + + /* do nothing if dsp resume callback is not set */ + if (!sdev->ops->resume) + return 0; + + /* power up DSP */ + ret = snd_sof_dsp_resume(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to power up DSP after resume\n"); + return ret; + } + + /* load the firmware */ + ret = snd_sof_load_firmware(sdev, sdev->pdata->fw, false); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to load DSP firmware after resume %d\n", + ret); + return ret; + } + + /* boot the firmware */ + ret = snd_sof_run_firmware(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to boot DSP firmware after resume %d\n", + ret); + return ret; + } + + /* init DMA trace */ + ret = snd_sof_init_trace_ipc(sdev); + if (ret < 0) { + /* non fatal */ + dev_warn(sdev->dev, + "warning: failed to init trace after resume %d\n", + ret); + } + + /* restore pipelines */ + ret = sof_restore_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to restore pipeline after resume %d\n", + ret); + return ret; + } + + /* notify DSP of system resume */ + ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); + if (ret < 0) + dev_err(sdev->dev, + "error: ctx_restore ipc error during resume %d\n", + ret); + + return ret; +} + +static int sof_suspend(struct device *dev) +{ + struct sof_platform_priv *priv = dev_get_drvdata(dev); + struct snd_sof_dev *sdev = dev_get_drvdata(&priv->pdev_pcm->dev); + int ret = 0; + + /* do nothing if dsp suspend callback is not set */ + if (!sdev->ops->suspend) + return 0; + + /* + * Suspend running pcm streams. + * They will be restarted by ALSA resume trigger call. + */ + sof_suspend_streams(sdev); + + /* notify DSP of upcoming power down */ + ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_SAVE); + if (ret < 0) { + dev_err(sdev->dev, + "error: ctx_save ipc error during suspend %d\n", + ret); + return ret; + } + + /* drop all ipc */ + sof_ipc_drop_all(sdev->ipc); + + /* release trace */ + snd_sof_dma_trace_release(sdev); + + /* power down DSP */ + ret = snd_sof_dsp_suspend(sdev, 0); + if (ret < 0) + dev_err(sdev->dev, + "error: failed to power down DSP during suspend %d\n", + ret); + + return ret; +} + +int snd_sof_runtime_suspend(struct device *dev) +{ + return sof_suspend(dev); +} +EXPORT_SYMBOL(snd_sof_runtime_suspend); + +int snd_sof_runtime_resume(struct device *dev) +{ + return sof_resume(dev); +} +EXPORT_SYMBOL(snd_sof_runtime_resume); + +int snd_sof_resume(struct device *dev) +{ + return sof_resume(dev); +} +EXPORT_SYMBOL(snd_sof_resume); + +int snd_sof_suspend(struct device *dev) +{ + /* suspend if dev is runtime_active */ + if (pm_runtime_active(dev)) + return sof_suspend(dev); + + return 0; +} +EXPORT_SYMBOL(snd_sof_suspend); + +int snd_sof_suspend_late(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_suspend_late); From 9e229633b23432b763265f023ccc32de4c0b6789 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:08 +0000 Subject: [PATCH 030/298] ASoC: SOF: Add Nocodec machine driver support Add a simple "fallback" machine driver that can be used to enable SOF on boards with no codec device. This machine driver can also be forced for debug/development. Signed-off-by: Liam Girdwood --- include/sound/sof.h | 10 +++++ sound/soc/sof/nocodec.c | 91 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 sound/soc/sof/nocodec.c diff --git a/include/sound/sof.h b/include/sound/sof.h index 445d0eddc551fc..835d0ae4770cf0 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -79,4 +80,13 @@ struct sof_dev_desc { const char *nocodec_tplg_filename; }; +int sof_nocodec_setup(struct device *dev, + struct snd_sof_pdata *sof_pdata, + struct snd_soc_acpi_mach *mach, + const struct sof_dev_desc *desc, + struct snd_sof_dsp_ops *ops); + +int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, + struct snd_soc_dai_link *links, int link_num, + struct snd_soc_card *card); #endif diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c new file mode 100644 index 00000000000000..6fe1fc3a87a954 --- /dev/null +++ b/sound/soc/sof/nocodec.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +static struct snd_soc_card sof_nocodec_card = { + .name = "sof-nocodec", +}; + +int sof_nocodec_setup(struct device *dev, + struct snd_sof_pdata *sof_pdata, + struct snd_soc_acpi_mach *mach, + const struct sof_dev_desc *desc, + struct snd_sof_dsp_ops *ops) +{ + struct snd_soc_dai_link *links; + int ret; + + if (!mach) + return -EINVAL; + + sof_pdata->drv_name = "sof-nocodec"; + + mach->drv_name = "sof-nocodec"; + mach->sof_fw_filename = desc->nocodec_fw_filename; + mach->sof_tplg_filename = desc->nocodec_tplg_filename; + + /* create dummy BE dai_links */ + links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * + ops->num_drv, GFP_KERNEL); + if (!links) + return -ENOMEM; + + ret = sof_bes_setup(dev, ops, links, ops->num_drv, + &sof_nocodec_card); + if (ret) { + kfree(links); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sof_nocodec_setup); + +static int sof_nocodec_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &sof_nocodec_card; + + card->dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static int sof_nocodec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver sof_nocodec_audio = { + .probe = sof_nocodec_probe, + .remove = sof_nocodec_remove, + .driver = { + .name = "sof-nocodec", + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(sof_nocodec_audio) + +MODULE_DESCRIPTION("ASoC sof nocodec"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:sof-nocodec"); From 8b2b2c02e3fd48e69c97d3adda503b453ca675d8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Jul 2018 11:31:11 -0500 Subject: [PATCH 031/298] Add xtensa support Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/xtensa/Kconfig | 3 + sound/soc/sof/xtensa/Makefile | 5 ++ sound/soc/sof/xtensa/core.c | 159 ++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 sound/soc/sof/xtensa/Kconfig create mode 100644 sound/soc/sof/xtensa/Makefile create mode 100644 sound/soc/sof/xtensa/core.c diff --git a/sound/soc/sof/xtensa/Kconfig b/sound/soc/sof/xtensa/Kconfig new file mode 100644 index 00000000000000..f66f17bb03357f --- /dev/null +++ b/sound/soc/sof/xtensa/Kconfig @@ -0,0 +1,3 @@ +config SND_SOC_SOF_XTENSA + tristate + diff --git a/sound/soc/sof/xtensa/Makefile b/sound/soc/sof/xtensa/Makefile new file mode 100644 index 00000000000000..cc89c7472a3802 --- /dev/null +++ b/sound/soc/sof/xtensa/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +snd-sof-xtensa-dsp-objs := core.o + +obj-$(CONFIG_SND_SOC_SOF_XTENSA) += snd-sof-xtensa-dsp.o diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c new file mode 100644 index 00000000000000..5ca9185f5afdf5 --- /dev/null +++ b/sound/soc/sof/xtensa/core.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Pan Xiuli + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sof-priv.h" +#include "../ops.h" + +struct xtensa_exception_cause { + u32 id; + const char *msg; + const char *description; +}; + +/* From 4.4.1.5 table 4-64 Exception Causes of + * Xtensa Instruction Set Architecture (ISA) Reference Manual + */ +static const struct xtensa_exception_cause xtensa_exception_causes[] = { + {0, "IllegalInstructionCause", "Illegal instruction"}, + {1, "SyscallCause", "SYSCALL instruction"}, + {2, "InstructionFetchErrorCause", + "Processor internal physical address or data error during instruction fetch"}, + {3, "LoadStoreErrorCause", + "Processor internal physical address or data error during load or store"}, + {4, "Level1InterruptCause", + "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"}, + {5, "AllocaCause", + "MOVSP instruction, if caller’s registers are not in the register file"}, + {6, "IntegerDivideByZeroCause", + "QUOS, QUOU, REMS, or REMU divisor operand is zero"}, + {8, "PrivilegedCause", + "Attempt to execute a privileged operation when CRING ? 0"}, + {9, "LoadStoreAlignmentCause", "Load or store to an unaligned address"}, + {12, "InstrPIFDataErrorCause", + "PIF data error during instruction fetch"}, + {13, "LoadStorePIFDataErrorCause", + "Synchronous PIF data error during LoadStore access"}, + {14, "InstrPIFAddrErrorCause", + "PIF address error during instruction fetch"}, + {15, "LoadStorePIFAddrErrorCause", + "Synchronous PIF address error during LoadStore access"}, + {16, "InstTLBMissCause", "Error during Instruction TLB refill"}, + {17, "InstTLBMultiHitCause", + "Multiple instruction TLB entries matched"}, + {18, "InstFetchPrivilegeCause", + "An instruction fetch referenced a virtual address at a ring level less than CRING"}, + {20, "InstFetchProhibitedCause", + "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch"}, + {24, "LoadStoreTLBMissCause", + "Error during TLB refill for a load or store"}, + {25, "LoadStoreTLBMultiHitCause", + "Multiple TLB entries matched for a load or store"}, + {26, "LoadStorePrivilegeCause", + "A load or store referenced a virtual address at a ring level less than CRING"}, + {28, "LoadProhibitedCause", + "A load referenced a page mapped with an attribute that does not permit loads"}, + {32, "Coprocessor0Disabled", + "Coprocessor 0 instruction when cp0 disabled"}, + {33, "Coprocessor1Disabled", + "Coprocessor 1 instruction when cp1 disabled"}, + {34, "Coprocessor2Disabled", + "Coprocessor 2 instruction when cp2 disabled"}, + {35, "Coprocessor3Disabled", + "Coprocessor 3 instruction when cp3 disabled"}, + {36, "Coprocessor4Disabled", + "Coprocessor 4 instruction when cp4 disabled"}, + {37, "Coprocessor5Disabled", + "Coprocessor 5 instruction when cp5 disabled"}, + {38, "Coprocessor6Disabled", + "Coprocessor 6 instruction when cp6 disabled"}, + {39, "Coprocessor7Disabled", + "Coprocessor 7 instruction when cp7 disabled"}, +}; + +/* only need xtensa atm */ +static void xtensa_dsp_oops(struct snd_sof_dev *sdev, void *oops) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + int i; + + dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { + if (xtensa_exception_causes[i].id == xoops->exccause) { + dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); + } + } + dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); +} + +static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, + u32 stack_words) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + u32 stack_ptr = xoops->stack; + int i; + + dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + + for (i = 0; i <= stack_words - 4; i += 4) { + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], + stack[i + 3]); + } + + /* deal with any remaining words */ + switch (stack_words - i) { + case 0: + break; + case 1: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", + stack_ptr + stack_words - 1, stack[stack_words - 1]); + break; + case 2: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 2, stack[stack_words - 2], + stack[stack_words - 1]); + break; + case 3: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 3, stack[stack_words - 3], + stack[stack_words - 2], stack[stack_words - 1]); + break; + default: + break; + } +} + +const struct sof_arch_ops sof_xtensa_arch_ops = { + .dsp_oops = xtensa_dsp_oops, + .dsp_stack = xtensa_stack, +}; +EXPORT_SYMBOL(sof_xtensa_arch_ops); + +MODULE_DESCRIPTION("SOF Xtensa DSP support"); +MODULE_LICENSE("Dual BSD/GPL"); From 329994e38c6d7ae4f6cf23c9a30d199dad61b89d Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:40 +0000 Subject: [PATCH 032/298] ASoC: SOF: Intel: Add BYT, CHT and BSW DSP HW support. Add support for the audio DSP hardware found on Intel Baytrail, Cherrytrail and Braswell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/byt.c | 918 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/shim.h | 157 +++++++ 2 files changed, 1075 insertions(+) create mode 100644 sound/soc/sof/intel/byt.c create mode 100644 sound/soc/sof/intel/shim.h diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c new file mode 100644 index 00000000000000..7b6d549b0d439b --- /dev/null +++ b/sound/soc/sof/intel/byt.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardware interface for audio DSP on Baytrail, Braswell and Cherrytrail. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* DSP memories */ +#define IRAM_OFFSET 0x0C0000 +#define IRAM_SIZE (80 * 1024) +#define DRAM_OFFSET 0x100000 +#define DRAM_SIZE (160 * 1024) +#define SHIM_OFFSET 0x140000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x144000 +#define MBOX_SIZE 0x1000 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0x098000 +#define DMAC1_OFFSET 0x09c000 +#define DMAC2_OFFSET 0x094000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0x0a0000 +#define SSP1_OFFSET 0x0a1000 +#define SSP2_OFFSET 0x0a2000 +#define SSP3_OFFSET 0x0a4000 +#define SSP4_OFFSET 0x0a5000 +#define SSP5_OFFSET 0x0a6000 +#define SSP_SIZE 0x100 + +#define BYT_STACK_DUMP_SIZE 32 + +#define BYT_PCI_BAR_SIZE 0x200000 + +#define BYT_PANIC_OFFSET(x) (((x) & (0xFFFFll << 32)) >> 32) + +/* + * Debug + */ + +#define MBOX_DUMP_SIZE 0x30 + +/* BARs */ +#define BYT_DSP_BAR 0 +#define BYT_PCI_BAR 1 +#define BYT_IMR_BAR 2 + +static const struct snd_sof_debugfs_map byt_debugfs[] = { + {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE}, + {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE}, + {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE}, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +static const struct snd_sof_debugfs_map cht_debugfs[] = { + {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"dmac2", BYT_DSP_BAR, DMAC2_OFFSET, DMAC_SIZE}, + {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE}, + {"ssp3", BYT_DSP_BAR, SSP3_OFFSET, SSP_SIZE}, + {"ssp4", BYT_DSP_BAR, SSP4_OFFSET, SSP_SIZE}, + {"ssp5", BYT_DSP_BAR, SSP5_OFFSET, SSP_SIZE}, + {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE}, + {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE}, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +static int byt_cmd_done(struct snd_sof_dev *sdev, int dir); + +/* + * Register IO + */ + +static void byt_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 byt_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void byt_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 byt_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * Memory copy. + */ + +static void byt_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy(dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy(dest + m * 4, &tmp, 1); + } +} + +static void byt_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +/* + * IPC Firmware ready. + */ +static void byt_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int byt_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + byt_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + byt_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void byt_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void byt_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Debug + */ + +static void byt_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void byt_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[BYT_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCX); + byt_get_registers(sdev, &xoops, stack, BYT_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + BYT_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t byt_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u64 isr; + int ret = IRQ_NONE; + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t byt_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u64 ipcx, ipcd; + + ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_BYT_IPCX_DONE) { + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, ipcx)) + byt_cmd_done(sdev, SOF_IPC_DSP_REPLY); + } + + /* new message from DSP */ + ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD); + if (ipcd & SHIM_BYT_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, BYT_PANIC_OFFSET(ipcd) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +static int byt_is_ready(struct snd_sof_dev *sdev) +{ + u64 ipcx; + + ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX); + if ((ipcx & SHIM_BYT_IPCX_BUSY) || (ipcx & SHIM_BYT_IPCX_DONE)) + return 0; + + return 1; +} + +static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u64 cmd = msg->header; + + /* send the message */ + byt_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, + cmd | SHIM_BYT_IPCX_BUSY); + + return 0; +} + +static int byt_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + byt_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + byt_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +static int byt_cmd_done(struct snd_sof_dev *sdev, int dir) +{ + if (dir == SOF_IPC_HOST_REPLY) { + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCD, + SHIM_BYT_IPCD_BUSY | + SHIM_BYT_IPCD_DONE, + SHIM_BYT_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + } else { + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX, + SHIM_BYT_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + return 0; +} + +/* + * DSP control. + */ + +static int byt_run(struct snd_sof_dev *sdev) +{ + int tries = 10; + + /* release stall and wait to unstall */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_STALL, 0x0); + while (tries--) { + if (!(snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_CSR) & + SHIM_BYT_CSR_PWAITMODE)) + break; + msleep(100); + } + if (tries < 0) { + dev_err(sdev->dev, "error: unable to run DSP firmware\n"); + byt_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + return -ENODEV; + } + + return 0; +} + +static int byt_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset, set reset vector and stall */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL | + SHIM_BYT_CSR_STALL, + SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL | + SHIM_BYT_CSR_STALL); + + usleep_range(10, 15); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_RST, 0); + + return 0; +} + +/* + * Probe and remove. + */ + +static int byt_acpi_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_DSP_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = BYT_DSP_BAR; + sdev->mailbox_bar = BYT_DSP_BAR; + + /* IMR base - optional */ + if (desc->resindex_imr_base == -1) + goto irq; + + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_imr_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get IMR base at idx %d\n", + desc->resindex_imr_base); + ret = -ENODEV; + goto imr_err; + } + + /* some BIOSes don't map IMR */ + if (base == 0x55aa55aa || base == 0x0) { + dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n"); + goto irq; + } + + dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_IMR_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_IMR_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto imr_err; + } + dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]); + +irq: + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, byt_irq_handler, + byt_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable Interrupt from both sides */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0); + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0); + + /* set BARS */ + sdev->cl_bar = BYT_DSP_BAR; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; + + return ret; + +irq_err: + iounmap(sdev->bar[BYT_IMR_BAR]); +imr_err: + iounmap(sdev->bar[BYT_DSP_BAR]); + return ret; +} + +static int byt_pci_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct pci_dev *pci = sdev->pci; + u32 base, size; + int ret = 0; + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* LPE base */ + base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET; + size = BYT_PCI_BAR_SIZE; + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_DSP_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]); + + /* IMR base - optional */ + if (desc->resindex_imr_base == -1) + goto irq; + + base = pci_resource_start(pci, desc->resindex_imr_base); + size = pci_resource_len(pci, desc->resindex_imr_base); + + /* some BIOSes don't map IMR */ + if (base == 0x55aa55aa || base == 0x0) { + dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n"); + goto irq; + } + + dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_IMR_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_IMR_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto imr_err; + } + dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]); + +irq: + /* register our IRQ */ + sdev->ipc_irq = pci->irq; + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, byt_irq_handler, + byt_irq_thread, 0, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable Interrupt from both sides */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0); + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0); + + /* set BARS */ + sdev->cl_bar = BYT_DSP_BAR; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; + + return ret; + +irq_err: + iounmap(sdev->bar[BYT_IMR_BAR]); +imr_err: + iounmap(sdev->bar[BYT_DSP_BAR]); + return ret; +} + +static int byt_probe(struct snd_sof_dev *sdev) +{ + if (sdev->pci) + return byt_pci_probe(sdev); + else + return byt_acpi_probe(sdev); +} + +static int byt_acpi_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[BYT_DSP_BAR]); + iounmap(sdev->bar[BYT_PCI_BAR]); + iounmap(sdev->bar[BYT_IMR_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +static int byt_pci_remove(struct snd_sof_dev *sdev) +{ + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +static int byt_remove(struct snd_sof_dev *sdev) +{ + if (sdev->pci) + return byt_pci_remove(sdev); + else + return byt_acpi_remove(sdev); +} + +#define BYT_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Baytrail DAIs */ +static struct snd_soc_dai_driver byt_dai[] = { +{ + .name = "ssp0-port", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp1-port", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp2-port", + .playback = SOF_DAI_STREAM("ssp2 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp2 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp3-port", + .playback = SOF_DAI_STREAM("ssp3 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp3 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp4-port", + .playback = SOF_DAI_STREAM("ssp4 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp4 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp5-port", + .playback = SOF_DAI_STREAM("ssp5 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp5 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +}; + +/* baytrail ops */ +struct snd_sof_dsp_ops sof_byt_ops = { + /* device init */ + .probe = byt_probe, + .remove = byt_remove, + + /* DSP core boot / reset */ + .run = byt_run, + .reset = byt_reset, + + /* Register IO */ + .write = byt_write, + .read = byt_read, + .write64 = byt_write64, + .read64 = byt_read64, + + /* Block IO */ + .block_read = byt_block_read, + .block_write = byt_block_write, + + /* doorbell */ + .irq_handler = byt_irq_handler, + .irq_thread = byt_irq_thread, + + /* mailbox */ + .mailbox_read = byt_mailbox_read, + .mailbox_write = byt_mailbox_write, + + /* ipc */ + .send_msg = byt_send_msg, + .get_reply = byt_get_reply, + .fw_ready = byt_fw_ready, + .is_ready = byt_is_ready, + .cmd_done = byt_cmd_done, + + /* debug */ + .debug_map = byt_debugfs, + .debug_map_count = ARRAY_SIZE(byt_debugfs), + .dbg_dump = byt_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .drv = byt_dai, + .num_drv = 3, /* we have only 3 SSPs on byt*/ +}; +EXPORT_SYMBOL(sof_byt_ops); + +/* cherrytrail and braswell ops */ +struct snd_sof_dsp_ops sof_cht_ops = { + /* device init */ + .probe = byt_probe, + .remove = byt_remove, + + /* DSP core boot / reset */ + .run = byt_run, + .reset = byt_reset, + + /* Register IO */ + .write = byt_write, + .read = byt_read, + .write64 = byt_write64, + .read64 = byt_read64, + + /* Block IO */ + .block_read = byt_block_read, + .block_write = byt_block_write, + + /* doorbell */ + .irq_handler = byt_irq_handler, + .irq_thread = byt_irq_thread, + + /* mailbox */ + .mailbox_read = byt_mailbox_read, + .mailbox_write = byt_mailbox_write, + + /* ipc */ + .send_msg = byt_send_msg, + .get_reply = byt_get_reply, + .fw_ready = byt_fw_ready, + .is_ready = byt_is_ready, + .cmd_done = byt_cmd_done, + + /* debug */ + .debug_map = cht_debugfs, + .debug_map_count = ARRAY_SIZE(cht_debugfs), + .dbg_dump = byt_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .drv = byt_dai, + /* all 6 SSPs may be available for cherrytrail */ + .num_drv = ARRAY_SIZE(byt_dai), +}; +EXPORT_SYMBOL(sof_cht_ops); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h new file mode 100644 index 00000000000000..b6481fc65b2de7 --- /dev/null +++ b/sound/soc/sof/intel/shim.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOF_INTEL_SHIM_H +#define __SOF_INTEL_SHIM_H + +/* + * SHIM registers for BYT, BSW, CHT, HSW, BDW + */ + +#define SHIM_CSR (SHIM_OFFSET + 0x00) +#define SHIM_PISR (SHIM_OFFSET + 0x08) +#define SHIM_PIMR (SHIM_OFFSET + 0x10) +#define SHIM_ISRX (SHIM_OFFSET + 0x18) +#define SHIM_ISRD (SHIM_OFFSET + 0x20) +#define SHIM_IMRX (SHIM_OFFSET + 0x28) +#define SHIM_IMRD (SHIM_OFFSET + 0x30) +#define SHIM_IPCX (SHIM_OFFSET + 0x38) +#define SHIM_IPCD (SHIM_OFFSET + 0x40) +#define SHIM_ISRSC (SHIM_OFFSET + 0x48) +#define SHIM_ISRLPESC (SHIM_OFFSET + 0x50) +#define SHIM_IMRSC (SHIM_OFFSET + 0x58) +#define SHIM_IMRLPESC (SHIM_OFFSET + 0x60) +#define SHIM_IPCSC (SHIM_OFFSET + 0x68) +#define SHIM_IPCLPESC (SHIM_OFFSET + 0x70) +#define SHIM_CLKCTL (SHIM_OFFSET + 0x78) +#define SHIM_CSR2 (SHIM_OFFSET + 0x80) +#define SHIM_LTRC (SHIM_OFFSET + 0xE0) +#define SHIM_HMDC (SHIM_OFFSET + 0xE8) + +#define SHIM_PWMCTRL 0x1000 + +/* + * SST SHIM register bits for BYT, BSW, CHT HSW, BDW + * Register bit naming and functionaility can differ between devices. + */ + +/* CSR / CS */ +#define SHIM_CSR_RST (0x1 << 1) +#define SHIM_CSR_SBCS0 (0x1 << 2) +#define SHIM_CSR_SBCS1 (0x1 << 3) +#define SHIM_CSR_DCS(x) ((x) << 4) +#define SHIM_CSR_DCS_MASK (0x7 << 4) +#define SHIM_CSR_STALL (0x1 << 10) +#define SHIM_CSR_S0IOCS (0x1 << 21) +#define SHIM_CSR_S1IOCS (0x1 << 23) +#define SHIM_CSR_LPCS (0x1 << 31) +#define SHIM_CSR_24MHZ_LPCS \ + (SHIM_CSR_SBCS0 | SHIM_CSR_SBCS1 | SHIM_CSR_LPCS) +#define SHIM_CSR_24MHZ_NO_LPCS (SHIM_CSR_SBCS0 | SHIM_CSR_SBCS1) +#define SHIM_BYT_CSR_RST (0x1 << 0) +#define SHIM_BYT_CSR_VECTOR_SEL (0x1 << 1) +#define SHIM_BYT_CSR_STALL (0x1 << 2) +#define SHIM_BYT_CSR_PWAITMODE (0x1 << 3) + +/* ISRX / ISC */ +#define SHIM_ISRX_BUSY (0x1 << 1) +#define SHIM_ISRX_DONE (0x1 << 0) +#define SHIM_BYT_ISRX_REQUEST (0x1 << 1) + +/* ISRD / ISD */ +#define SHIM_ISRD_BUSY (0x1 << 1) +#define SHIM_ISRD_DONE (0x1 << 0) + +/* IMRX / IMC */ +#define SHIM_IMRX_BUSY (0x1 << 1) +#define SHIM_IMRX_DONE (0x1 << 0) +#define SHIM_BYT_IMRX_REQUEST (0x1 << 1) + +/* IMRD / IMD */ +#define SHIM_IMRD_DONE (0x1 << 0) +#define SHIM_IMRD_BUSY (0x1 << 1) +#define SHIM_IMRD_SSP0 (0x1 << 16) +#define SHIM_IMRD_DMAC0 (0x1 << 21) +#define SHIM_IMRD_DMAC1 (0x1 << 22) +#define SHIM_IMRD_DMAC (SHIM_IMRD_DMAC0 | SHIM_IMRD_DMAC1) + +/* IPCX / IPCC */ +#define SHIM_IPCX_DONE (0x1 << 30) +#define SHIM_IPCX_BUSY (0x1 << 31) +#define SHIM_BYT_IPCX_DONE ((u64)0x1 << 62) +#define SHIM_BYT_IPCX_BUSY ((u64)0x1 << 63) + +/* IPCD */ +#define SHIM_IPCD_DONE (0x1 << 30) +#define SHIM_IPCD_BUSY (0x1 << 31) +#define SHIM_BYT_IPCD_DONE ((u64)0x1 << 62) +#define SHIM_BYT_IPCD_BUSY ((u64)0x1 << 63) + +/* CLKCTL */ +#define SHIM_CLKCTL_SMOS(x) ((x) << 24) +#define SHIM_CLKCTL_MASK (3 << 24) +#define SHIM_CLKCTL_DCPLCG BIT(18) +#define SHIM_CLKCTL_SCOE1 BIT(17) +#define SHIM_CLKCTL_SCOE0 BIT(16) + +/* CSR2 / CS2 */ +#define SHIM_CSR2_SDFD_SSP0 BIT(1) +#define SHIM_CSR2_SDFD_SSP1 BIT(2) + +/* LTRC */ +#define SHIM_LTRC_VAL(x) ((x) << 0) + +/* HMDC */ +#define SHIM_HMDC_HDDA0(x) ((x) << 0) +#define SHIM_HMDC_HDDA1(x) ((x) << 7) +#define SHIM_HMDC_HDDA_E0_CH0 1 +#define SHIM_HMDC_HDDA_E0_CH1 2 +#define SHIM_HMDC_HDDA_E0_CH2 4 +#define SHIM_HMDC_HDDA_E0_CH3 8 +#define SHIM_HMDC_HDDA_E1_CH0 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH0) +#define SHIM_HMDC_HDDA_E1_CH1 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH1) +#define SHIM_HMDC_HDDA_E1_CH2 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH2) +#define SHIM_HMDC_HDDA_E1_CH3 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH3) +#define SHIM_HMDC_HDDA_E0_ALLCH \ + (SHIM_HMDC_HDDA_E0_CH0 | SHIM_HMDC_HDDA_E0_CH1 | \ + SHIM_HMDC_HDDA_E0_CH2 | SHIM_HMDC_HDDA_E0_CH3) +#define SHIM_HMDC_HDDA_E1_ALLCH \ + (SHIM_HMDC_HDDA_E1_CH0 | SHIM_HMDC_HDDA_E1_CH1 | \ + SHIM_HMDC_HDDA_E1_CH2 | SHIM_HMDC_HDDA_E1_CH3) + +/* Audio DSP PCI registers */ +#define PCI_VDRTCTL0 0xa0 +#define PCI_VDRTCTL1 0xa4 +#define PCI_VDRTCTL2 0xa8 +#define PCI_VDRTCTL3 0xaC + +/* VDRTCTL0 */ +#define PCI_VDRTCL0_D3PGD BIT(0) +#define PCI_VDRTCL0_D3SRAMPGD BIT(1) +#define PCI_VDRTCL0_DSRAMPGE_SHIFT 12 +#define PCI_VDRTCL0_DSRAMPGE_MASK (0xfffff << PCI_VDRTCL0_DSRAMPGE_SHIFT) +#define PCI_VDRTCL0_ISRAMPGE_SHIFT 2 +#define PCI_VDRTCL0_ISRAMPGE_MASK (0x3ff << PCI_VDRTCL0_ISRAMPGE_SHIFT) + +/* VDRTCTL2 */ +#define PCI_VDRTCL2_DCLCGE BIT(1) +#define PCI_VDRTCL2_DTCGE BIT(10) +#define PCI_VDRTCL2_APLLSE_MASK BIT(31) + +/* PMCS */ +#define PCI_PMCS 0x84 +#define PCI_PMCS_PS_MASK 0x3 + +extern struct snd_sof_dsp_ops sof_byt_ops; +extern struct snd_sof_dsp_ops sof_cht_ops; +extern struct snd_sof_dsp_ops sof_hsw_ops; +extern struct snd_sof_dsp_ops sof_bdw_ops; + +#endif From 9a1571b682cebd65d5ce074707f7ae5eace302be Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:51 +0000 Subject: [PATCH 033/298] ASoC: SOF: Intel: Add HSW HW DSP support Add DSP hardware support for Intel Haswell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hsw.c | 795 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 795 insertions(+) create mode 100644 sound/soc/sof/intel/hsw.c diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c new file mode 100644 index 00000000000000..c0139855cd6da6 --- /dev/null +++ b/sound/soc/sof/intel/hsw.c @@ -0,0 +1,795 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardwre interface for audio DSP on Haswell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* BARs */ +#define HSW_DSP_BAR 0 +#define HSW_PCI_BAR 1 + +/* + * Debug + */ + +/* DSP memories for HSW */ +#define IRAM_OFFSET 0x80000 +#define HSW_IRAM_SIZE (10 * 32 * 1024) +#define DRAM_OFFSET 0x00000 +#define HSW_DRAM_SIZE (16 * 32 * 1024) +#define SHIM_OFFSET 0xE7000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x7E000 +#define MBOX_SIZE 0x1000 +#define MBOX_DUMP_SIZE 0x30 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0xFE000 +#define DMAC1_OFFSET 0xFF000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0xFC000 +#define SSP1_OFFSET 0xFD000 +#define SSP_SIZE 0x100 + +#define HSW_STACK_DUMP_SIZE 32 + +#define HSW_PANIC_OFFSET(x) ((x) & 0xFFFF) + +static const struct snd_sof_debugfs_map hsw_debugfs[] = { + {"dmac0", HSW_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", HSW_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", HSW_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", HSW_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"iram", HSW_DSP_BAR, IRAM_OFFSET, HSW_IRAM_SIZE}, + {"dram", HSW_DSP_BAR, DRAM_OFFSET, HSW_DRAM_SIZE}, + {"shim", HSW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +static int hsw_cmd_done(struct snd_sof_dev *sdev, int dir); + +/* + * Memory copy. + */ + +/* write has to deal with copying non 32 bit sized data */ +static void hsw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy(dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy(dest + m * 4, &tmp, 1); + } +} + +static void hsw_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +static void hsw_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void hsw_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Register IO + */ + +static void hsw_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 hsw_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void hsw_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 hsw_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * DSP Control. + */ + +static int hsw_run(struct snd_sof_dev *sdev) +{ + /* set oportunistic mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, 0); + + /* set DSP to RUN */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_STALL, 0x0); + + return 0; //TODO: Fix return value +} + +static int hsw_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset and stall */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_RST | SHIM_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_STALL); + + return 0; //TODO: Fix return value +} + +static int hsw_set_dsp_D0(struct snd_sof_dev *sdev) +{ + int tries = 10; + u32 reg; + + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | PCI_VDRTCL2_DTCGE, + 0); + + /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_D3PGD, PCI_VDRTCL0_D3PGD); + + /* Set D0 state */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_PMCS, + PCI_PMCS_PS_MASK, 0); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = readl(sdev->bar[HSW_PCI_BAR] + PCI_PMCS) + & PCI_PMCS_PS_MASK; + if (reg == 0) + goto finish; + + msleep(20); + } + + return -ENODEV; + +finish: + /* + * select SSP1 19.2MHz base clock, SSP clock 0, + * turn off Low Power Clock + */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_S1IOCS | SHIM_CSR_SBCS1 | + SHIM_CSR_LPCS, 0x0); + + /* stall DSP core, set clk to 192/96Mhz */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, + SHIM_CSR, SHIM_CSR_STALL | + SHIM_CSR_DCS_MASK, + SHIM_CSR_STALL | SHIM_CSR_DCS(4)); + + /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CLKCTL, + SHIM_CLKCTL_MASK | SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0, + SHIM_CLKCTL_MASK | SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0); + + /* Stall and reset core, set CSR */ + hsw_reset(sdev); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE); + + usleep_range(50, 55); + + /* switch on audio PLL */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_APLLSE_MASK, 0); + + /* + * set default power gating control, enable power gating control for + * all blocks. that is, can't be accessed, please enable each block + * before accessing. + */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_DSRAMPGE_MASK | + PCI_VDRTCL0_ISRAMPGE_MASK, 0); + + /* disable DMA finish function for SSP0 & SSP1 */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR2, + SHIM_CSR2_SDFD_SSP1, + SHIM_CSR2_SDFD_SSP1); + + /* set on-demond mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH); + + /* Enable Interrupt from both sides */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_IMRX, + (SHIM_IMRX_BUSY | SHIM_IMRX_DONE), 0x0); + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_IMRD, + (SHIM_IMRD_DONE | SHIM_IMRD_BUSY | + SHIM_IMRD_SSP0 | SHIM_IMRD_DMAC), 0x0); + + /* clear IPC registers */ + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCX, 0x0); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCD, 0x0); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, 0x80, 0x6); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, 0xe0, 0x300a); + + return 0; +} + +static void hsw_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void hsw_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[HSW_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + hsw_get_registers(sdev, &xoops, stack, HSW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + HSW_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t hsw_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 isr; + int ret = IRQ_NONE; + + spin_lock(&sdev->hw_lock); + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + spin_unlock(&sdev->hw_lock); + return ret; +} + +static irqreturn_t hsw_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 ipcx, ipcd, hdr; + + ipcx = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + hsw_mailbox_read(sdev, sdev->host_box.offset, &hdr, + sizeof(hdr)); + + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, hdr)) + hsw_cmd_done(sdev, SOF_IPC_DSP_REPLY); + } + + ipcd = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); + + /* new message from DSP */ + if (ipcd & SHIM_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, HSW_PANIC_OFFSET(ipcx) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +/* + * IPC Firmware ready. + */ +static void hsw_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int hsw_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + hsw_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + hsw_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static int hsw_is_ready(struct snd_sof_dev *sdev) +{ + u32 val; + + val = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + if ((val & SHIM_IPCX_BUSY) || (val & SHIM_IPCX_DONE)) + return 0; + + return 1; +} + +static int hsw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + /* send the message */ + hsw_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCX, SHIM_IPCX_BUSY); + + return 0; +} + +static int hsw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + hsw_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + hsw_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + return ret; +} + +static int hsw_cmd_done(struct snd_sof_dev *sdev, int dir) +{ + if (dir == SOF_IPC_HOST_REPLY) { + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + } else { + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + return 0; +} + +/* + * Probe and remove. + */ +static int hsw_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[HSW_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[HSW_DSP_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[HSW_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = HSW_DSP_BAR; + sdev->mailbox_bar = HSW_DSP_BAR; + + /* PCI base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get PCI base at idx %d\n", + desc->resindex_pcicfg_base); + ret = -ENODEV; + goto pci_err; + } + + dev_dbg(sdev->dev, "PCI base at 0x%x size 0x%x", base, size); + sdev->bar[HSW_PCI_BAR] = ioremap(base, size); + if (!sdev->bar[HSW_PCI_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap PCI base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto pci_err; + } + dev_dbg(sdev->dev, "PCI VADDR %p\n", sdev->bar[HSW_PCI_BAR]); + + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, hsw_irq_handler, + hsw_irq_thread, 0, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable the DSP SHIM */ + ret = hsw_set_dsp_D0(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DSP D0\n"); + return ret; + } + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* set BARS */ + sdev->cl_bar = HSW_DSP_BAR; + + /* set default mailbox */ + snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0); + + return ret; + +irq_err: + iounmap(sdev->bar[HSW_DSP_BAR]); +pci_err: + iounmap(sdev->bar[HSW_PCI_BAR]); + return ret; +} + +static int hsw_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[HSW_DSP_BAR]); + iounmap(sdev->bar[HSW_PCI_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +#define HSW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Haswell DAIs */ +static struct snd_soc_dai_driver hsw_dai[] = { +{ + .name = "ssp0-port", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), +}, +{ + .name = "ssp1-port", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), +}, +}; + +/* haswell ops */ +struct snd_sof_dsp_ops sof_hsw_ops = { + /*Device init */ + .probe = hsw_probe, + .remove = hsw_remove, + + /* DSP Core Control */ + .run = hsw_run, + .reset = hsw_reset, + + /* Register IO */ + .read = hsw_read, + .write = hsw_write, + .read64 = hsw_read64, + .write64 = hsw_write64, + + /* Block IO */ + .block_read = hsw_block_read, + .block_write = hsw_block_write, + + /* mailbox */ + .mailbox_read = hsw_mailbox_read, + .mailbox_write = hsw_mailbox_write, + + /* ipc */ + .send_msg = hsw_send_msg, + .get_reply = hsw_get_reply, + .fw_ready = hsw_fw_ready, + .is_ready = hsw_is_ready, + .cmd_done = hsw_cmd_done, + + /* debug */ + .debug_map = hsw_debugfs, + .debug_map_count = ARRAY_SIZE(hsw_debugfs), + .dbg_dump = hsw_dump, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .drv = hsw_dai, + .num_drv = ARRAY_SIZE(hsw_dai) + +}; +EXPORT_SYMBOL(sof_hsw_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From 63e37a02984e3790a800c40d1eac2c691450ed3e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:38:14 +0000 Subject: [PATCH 034/298] ASoC: SOF: Intel: Add support for BDW HW DSP support Add SOF support for Intel Broadwell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/bdw.c | 794 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 794 insertions(+) create mode 100644 sound/soc/sof/intel/bdw.c diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c new file mode 100644 index 00000000000000..ef272108593f64 --- /dev/null +++ b/sound/soc/sof/intel/bdw.c @@ -0,0 +1,794 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardwre interface for audio DSP on Haswell and Broadwell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* BARs */ +#define BDW_DSP_BAR 0 +#define BDW_PCI_BAR 1 + +/* + * Debug + */ + +/* DSP memories for BDW */ +#define IRAM_OFFSET 0xA0000 +#define BDW_IRAM_SIZE (10 * 32 * 1024) +#define DRAM_OFFSET 0x00000 +#define BDW_DRAM_SIZE (20 * 32 * 1024) +#define SHIM_OFFSET 0xFB000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x9E000 +#define MBOX_SIZE 0x1000 +#define MBOX_DUMP_SIZE 0x30 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0xFE000 +#define DMAC1_OFFSET 0xFF000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0xFC000 +#define SSP1_OFFSET 0xFD000 +#define SSP_SIZE 0x100 + +#define BDW_STACK_DUMP_SIZE 32 + +#define BDW_PANIC_OFFSET(x) ((x) & 0xFFFF) + +static const struct snd_sof_debugfs_map bdw_debugfs[] = { + {"dmac0", BDW_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BDW_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", BDW_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BDW_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"iram", BDW_DSP_BAR, IRAM_OFFSET, BDW_IRAM_SIZE}, + {"dram", BDW_DSP_BAR, DRAM_OFFSET, BDW_DRAM_SIZE}, + {"shim", BDW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +static int bdw_cmd_done(struct snd_sof_dev *sdev, int dir); + +/* + * Memory copy. + */ + +/* write has to deal with copying non 32 bit sized data */ +static void bdw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy(dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy(dest + m * 4, &tmp, 1); + } +} + +static void bdw_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +static void bdw_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void bdw_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Register IO + */ + +static void bdw_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 bdw_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void bdw_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 bdw_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * DSP Control. + */ + +static int bdw_run(struct snd_sof_dev *sdev) +{ + /* set oportunistic mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, 0); + + /* set DSP to RUN */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_STALL, 0x0); + + return 0; +} + +static int bdw_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset and stall */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_RST | SHIM_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_STALL); + + return 0; +} + +static int bdw_set_dsp_D0(struct snd_sof_dev *sdev) +{ + int tries = 10; + u32 reg; + + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, 0); + + /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_D3PGD, PCI_VDRTCL0_D3PGD); + + /* Set D0 state */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_PMCS, + PCI_PMCS_PS_MASK, 0); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = readl(sdev->bar[BDW_PCI_BAR] + PCI_PMCS) + & PCI_PMCS_PS_MASK; + if (reg == 0) + goto finish; + + msleep(20); + } + + return -ENODEV; + +finish: + /* + * select SSP1 19.2MHz base clock, SSP clock 0, + * turn off Low Power Clock + */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_S1IOCS | SHIM_CSR_SBCS1 | + SHIM_CSR_LPCS, 0x0); + + /* stall DSP core, set clk to 192/96Mhz */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_CSR, SHIM_CSR_STALL | + SHIM_CSR_DCS_MASK, + SHIM_CSR_STALL | + SHIM_CSR_DCS(4)); + + /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CLKCTL, + SHIM_CLKCTL_MASK | + SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0, + SHIM_CLKCTL_MASK | + SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0); + + /* Stall and reset core, set CSR */ + bdw_reset(sdev); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE); + + usleep_range(50, 55); + + /* switch on audio PLL */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_APLLSE_MASK, 0); + + /* + * set default power gating control, enable power gating control for + * all blocks. that is, can't be accessed, please enable each block + * before accessing. + */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL0, + 0xfffffffC, 0x0); + + /* disable DMA finish function for SSP0 & SSP1 */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR2, + SHIM_CSR2_SDFD_SSP1, + SHIM_CSR2_SDFD_SSP1); + + /* set on-demond mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH); + + /* Enable Interrupt from both sides */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_IMRX, + (SHIM_IMRX_BUSY | SHIM_IMRX_DONE), 0x0); + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_IMRD, + (SHIM_IMRD_DONE | SHIM_IMRD_BUSY | + SHIM_IMRD_SSP0 | SHIM_IMRD_DMAC), 0x0); + + /* clear IPC registers */ + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCX, 0x0); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCD, 0x0); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, 0x80, 0x6); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, 0xe0, 0x300a); + + return 0; +} + +static void bdw_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[BDW_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + bdw_get_registers(sdev, &xoops, stack, BDW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + BDW_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t bdw_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 isr; + int ret = IRQ_NONE; + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_IMRX, SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_IMRX, SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t bdw_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 ipcx, ipcd, hdr; + + ipcx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + bdw_mailbox_read(sdev, sdev->host_box.offset, &hdr, + sizeof(hdr)); + + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, hdr)) + bdw_cmd_done(sdev, SOF_IPC_DSP_REPLY); + } + + ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); + + /* new message from DSP */ + if (ipcd & SHIM_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +/* + * IPC Firmware ready. + */ +static void bdw_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int bdw_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + bdw_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + bdw_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static int bdw_is_ready(struct snd_sof_dev *sdev) +{ + u32 val; + + val = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + if ((val & SHIM_IPCX_BUSY) || (val & SHIM_IPCX_DONE)) + return 0; + + return 1; +} + +static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + /* send the message */ + bdw_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCX, SHIM_IPCX_BUSY); + + return 0; +} + +static int bdw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + bdw_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + bdw_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +static int bdw_cmd_done(struct snd_sof_dev *sdev, int dir) +{ + if (dir == SOF_IPC_HOST_REPLY) { + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + } else { + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + return 0; +} + +/* + * Probe and remove. + */ +static int bdw_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BDW_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BDW_DSP_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BDW_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = BDW_DSP_BAR; + sdev->mailbox_bar = BDW_DSP_BAR; + + /* PCI base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get PCI base at idx %d\n", + desc->resindex_pcicfg_base); + ret = -ENODEV; + goto pci_err; + } + + dev_dbg(sdev->dev, "PCI base at 0x%x size 0x%x", base, size); + sdev->bar[BDW_PCI_BAR] = ioremap(base, size); + if (!sdev->bar[BDW_PCI_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap PCI base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto pci_err; + } + dev_dbg(sdev->dev, "PCI VADDR %p\n", sdev->bar[BDW_PCI_BAR]); + + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, bdw_irq_handler, + bdw_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable the DSP SHIM */ + ret = bdw_set_dsp_D0(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DSP D0\n"); + return ret; + } + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* set BARS */ + sdev->cl_bar = BDW_DSP_BAR; + + /* set default mailbox */ + snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0); + + return ret; + +irq_err: + iounmap(sdev->bar[BDW_DSP_BAR]); +pci_err: + iounmap(sdev->bar[BDW_PCI_BAR]); + return ret; +} + +static int bdw_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[BDW_DSP_BAR]); + iounmap(sdev->bar[BDW_PCI_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +#define BDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Broadwell DAIs */ +static struct snd_soc_dai_driver bdw_dai[] = { +{ + .name = "ssp0-port", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), +}, +{ + .name = "ssp1-port", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), +}, +}; + +/* broadwell ops */ +struct snd_sof_dsp_ops sof_bdw_ops = { + /*Device init */ + .probe = bdw_probe, + .remove = bdw_remove, + + /* DSP Core Control */ + .run = bdw_run, + .reset = bdw_reset, + + /* Register IO */ + .read = bdw_read, + .write = bdw_write, + .read64 = bdw_read64, + .write64 = bdw_write64, + + /* Block IO */ + .block_read = bdw_block_read, + .block_write = bdw_block_write, + + /* mailbox */ + .mailbox_read = bdw_mailbox_read, + .mailbox_write = bdw_mailbox_write, + + /* ipc */ + .send_msg = bdw_send_msg, + .get_reply = bdw_get_reply, + .fw_ready = bdw_fw_ready, + .is_ready = bdw_is_ready, + .cmd_done = bdw_cmd_done, + + /* debug */ + .debug_map = bdw_debugfs, + .debug_map_count = ARRAY_SIZE(bdw_debugfs), + .dbg_dump = bdw_dump, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .drv = bdw_dai, + .num_drv = ARRAY_SIZE(bdw_dai) +}; +EXPORT_SYMBOL(sof_bdw_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From 330f5ef043276040c84f7e9ad0981ce250427692 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:38:27 +0000 Subject: [PATCH 035/298] ASoC: SOF: Intel: Add APL/CNL HW DSP support Add SOF hardware DSP support for Intel Apollolake and Cannonlake based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda.c | 576 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 513 +++++++++++++++++++++++++++++++++ 2 files changed, 1089 insertions(+) create mode 100644 sound/soc/sof/intel/hda.c create mode 100644 sound/soc/sof/intel/hda.h diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c new file mode 100644 index 00000000000000..a3a814a74742b1 --- /dev/null +++ b/sound/soc/sof/intel/hda.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * Register IO + */ + +void hda_dsp_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value) +{ + writel(value, addr); +} + +u32 hda_dsp_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +void hda_dsp_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +u64 hda_dsp_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * Memory copy. + */ + +void hda_dsp_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + u8 *dst_byte; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy(dest, src, m); + + if (n) { + /* first read the 32bit data of dest, then change affected + * bytes, and write back to dest. For unaffected bytes, it + * should not be changed + */ + __ioread32_copy(&tmp, dest + m * 4, 1); + + dst_byte = (u8 *)&tmp; + for (i = 0; i < n; i++) + dst_byte[i] = src_byte[m * 4 + i]; + + __iowrite32_copy(dest + m * 4, &tmp, 1); + } +} + +void hda_dsp_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +/* + * Debug + */ + +struct hda_dsp_msg_code { + u32 code; + const char *msg; +}; + +static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { + {HDA_DSP_ROM_FW_MANIFEST_LOADED, "status: manifest loaded"}, + {HDA_DSP_ROM_FW_FW_LOADED, "status: fw loaded"}, + {HDA_DSP_ROM_FW_ENTERED, "status: fw entered"}, + {HDA_DSP_ROM_CSE_ERROR, "error: cse error"}, + {HDA_DSP_ROM_CSE_WRONG_RESPONSE, "error: cse wrong response"}, + {HDA_DSP_ROM_IMR_TO_SMALL, "error: IMR too small"}, + {HDA_DSP_ROM_BASE_FW_NOT_FOUND, "error: base fw not found"}, + {HDA_DSP_ROM_CSE_VALIDATION_FAILED, "error: signature verification failed"}, + {HDA_DSP_ROM_IPC_FATAL_ERROR, "error: ipc fatal error"}, + {HDA_DSP_ROM_L2_CACHE_ERROR, "error: L2 cache error"}, + {HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL, "error: load offset too small"}, + {HDA_DSP_ROM_API_PTR_INVALID, "error: API ptr invalid"}, + {HDA_DSP_ROM_BASEFW_INCOMPAT, "error: base fw incompatble"}, + {HDA_DSP_ROM_UNHANDLED_INTERRUPT, "error: unhandled interrupt"}, + {HDA_DSP_ROM_MEMORY_HOLE_ECC, "error: ECC memory hole"}, + {HDA_DSP_ROM_KERNEL_EXCEPTION, "error: kernel exception"}, + {HDA_DSP_ROM_USER_EXCEPTION, "error: user exception"}, + {HDA_DSP_ROM_UNEXPECTED_RESET, "error: unexpected reset"}, + {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"}, +}; + +static void hda_dsp_get_status(struct snd_sof_dev *sdev) +{ + u32 status; + int i; + + status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS); + + for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) { + if (status == hda_dsp_rom_msg[i].code) { + dev_err(sdev->dev, "%s - code %8.8x\n", + hda_dsp_rom_msg[i].msg, status); + return; + } + } + + /* not for us, must be generic sof message */ + dev_dbg(sdev->dev, "unknown ROM status value %8.8x\n", status); +} + +static void hda_dsp_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read registers */ + hda_dsp_block_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* then get the stack */ + hda_dsp_block_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[HDA_DSP_STACK_DUMP_SIZE]; + u32 status, panic; + + /* try APL specific status message types first */ + hda_dsp_get_status(sdev); + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_FW_STATUS); + panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP); + + if (sdev->boot_complete) { + hda_dsp_get_registers(sdev, &xoops, stack, + HDA_DSP_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + HDA_DSP_STACK_DUMP_SIZE); + } else { + dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n", + status, panic); + hda_dsp_get_status(sdev); + } +} + +/* + * IPC Mailbox IO + */ + +void hda_dsp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +void hda_dsp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Supported devices. + */ + +static const struct sof_intel_dsp_desc chip_info[] = { +{ + /* Skylake */ + .id = 0x9d70, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_skl_ops, +}, +{ + /* Kabylake */ + .id = 0x9d71, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_skl_ops, +}, +{ + /* Apollolake - BXT-P */ + .id = 0x5a98, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* BXT-M */ + .id = 0x1a98, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* GeminiLake */ + .id = 0x3198, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* Cannonlake */ + .id = 0x9dc8, + .cores_num = 4, + .cores_mask = HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1) | + HDA_DSP_CORE_MASK(2) | + HDA_DSP_CORE_MASK(3), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .ops = &sof_cnl_ops, +}, +{ + /* Icelake */ + .id = 0x34c8, + .cores_num = 4, + .cores_mask = HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1) | + HDA_DSP_CORE_MASK(2) | + HDA_DSP_CORE_MASK(3), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .ops = &sof_cnl_ops, +}, +}; + +static const struct sof_intel_dsp_desc *get_chip_info(int pci_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(chip_info); i++) { + if (chip_info[i].id == pci_id) + return &chip_info[i]; + } + + return NULL; +} + +/* + * We don't need to do a full HDA codec probe as external HDA codec mode is + * considered legacy and will not be supported under SOF. HDMI/DP HDA will + * be supported in the DSP. + */ +int hda_dsp_probe(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = sdev->pci; + struct sof_intel_hda_dev *hdev; + struct sof_intel_hda_stream *stream; + const struct sof_intel_dsp_desc *chip; + int i; + int ret = 0; + + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + + chip = get_chip_info(pci->device); + if (!chip) { + dev_err(sdev->dev, "no such device supported, chip id:%x\n", + pci->device); + ret = -EIO; + goto err; + } + + hdev = devm_kzalloc(&pci->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + sdev->hda = hdev; + hdev->desc = chip; + + /* HDA base */ + sdev->bar[HDA_DSP_HDA_BAR] = pci_ioremap_bar(pci, HDA_DSP_HDA_BAR); + if (!sdev->bar[HDA_DSP_HDA_BAR]) { + dev_err(&pci->dev, "error: ioremap error\n"); + /* + * FIXME: why do we return directly, + * should we have a goto err here? + * or should all these gotos be replaced + * by a return? + */ + return -ENXIO; + } + + /* DSP base */ + sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR); + if (!sdev->bar[HDA_DSP_BAR]) { + dev_err(&pci->dev, "error: ioremap error\n"); + ret = -ENXIO; + goto err; + } + + sdev->mmio_bar = HDA_DSP_BAR; + sdev->mailbox_bar = HDA_DSP_BAR; + + pci_set_master(pci); + synchronize_irq(pci->irq); + + /* allow 64bit DMA address if supported by H/W */ + if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(64))) { + dev_dbg(&pci->dev, "DMA mask is 64 bit\n"); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(64)); + } else { + dev_dbg(&pci->dev, "DMA mask is 32 bit\n"); + dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); + } + + /* get controller capabilities */ + ret = hda_dsp_ctrl_get_caps(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to find DSP capability\n"); + goto err; + } + + /* init streams */ + ret = hda_dsp_stream_init(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to init streams\n"); + /* + * not all errors are due to memory issues, but trying + * to free everything does not harm + */ + goto stream_err; + } + + /* + * clear bits 0-2 of PCI register TCSEL (at offset 0x44) + * TCSEL == Traffic Class Select Register, which sets PCI express QOS + * Ensuring these bits are 0 clears playback static on some HD Audio + * codecs. PCI register TCSEL is defined in the Intel manuals. + */ + snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); + + /* + * while performing reset, controller may not come back properly causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do + * reset (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ + snd_sof_pci_update_bits(sdev, PCI_CGCTL, + PCI_CGCTL_MISCBDCGE_MASK, 0); + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + /* reset HDA controller */ + ret = hda_dsp_ctrl_link_reset(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to reset HDA controller\n"); + goto stream_err; + } + + /* clear stream status */ + for (i = 0 ; i < hdev->num_capture ; i++) { + stream = &hdev->cstream[i]; + if (stream) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + } + + for (i = 0 ; i < hdev->num_playback ; i++) { + stream = &hdev->pstream[i]; + if (stream) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + } + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + /* clear interrupt status register */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); + + /* enable CIE and GIE interrupts */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); + + /* + * register our IRQ + * let's try to enable msi firstly + * if it fails, use legacy interrupt mode + * TODO: support interrupt mode selection with kernel parameter + * support msi multiple vectors + */ + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI); + if (ret < 0) { + dev_info(sdev->dev, "use legacy interrupt mode\n"); + sdev->hda->irq = pci->irq; + sdev->ipc_irq = pci->irq; + } else { + dev_info(sdev->dev, "use msi interrupt mode\n"); + sdev->hda->irq = pci_irq_vector(pci, 0); + /* ipc irq number is the same of hda irq */ + sdev->ipc_irq = sdev->hda->irq; + } + + dev_dbg(sdev->dev, "using HDA IRQ %d\n", sdev->hda->irq); + ret = request_threaded_irq(sdev->hda->irq, hda_dsp_stream_interrupt, + hda_dsp_stream_threaded_handler, + IRQF_SHARED, "AudioHDA", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", + sdev->hda->irq); + goto stream_err; + } + + dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, + chip->ops->irq_thread, IRQF_SHARED, + "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* re-enable CGCTL.MISCBDCGE after reset */ + snd_sof_pci_update_bits(sdev, PCI_CGCTL, + PCI_CGCTL_MISCBDCGE_MASK, + PCI_CGCTL_MISCBDCGE_MASK); + + device_disable_async_suspend(&pci->dev); + + /* enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, SOF_HDA_PPCTL_GPROCEN); + + /* enable DSP IRQ */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_PIE, SOF_HDA_PPCTL_PIE); + + /* initialize waitq for code loading */ + init_waitqueue_head(&sdev->waitq); + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET; + + return 0; + +irq_err: + free_irq(sdev->hda->irq, sdev); +stream_err: + pci_free_irq_vectors(pci); + hda_dsp_stream_free(sdev); +err: + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + return ret; +} + +int hda_dsp_remove(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = sdev->pci; + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* disable DSP IRQ */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_PIE, 0); + + /* disable CIE and GIE interrupts */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, 0); + + /* disable cores */ + if (chip) + hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + + free_irq(sdev->ipc_irq, sdev); + free_irq(sdev->pci->irq, sdev); + pci_free_irq_vectors(pci); + + hda_dsp_stream_free(sdev); + return 0; +} + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h new file mode 100644 index 00000000000000..2f3bc45efb10d0 --- /dev/null +++ b/sound/soc/sof/intel/hda.h @@ -0,0 +1,513 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOF_INTEL_HDA_H +#define __SOF_INTEL_HDA_H + +/* PCI registers */ +#define PCI_TCSEL 0x44 +#define PCI_CGCTL 0x48 + +/* PCI_CGCTL bits */ +#define PCI_CGCTL_MISCBDCGE_MASK BIT(6) + +/* Legacy HDA registers and bits used - widths are variable */ +#define SOF_HDA_GCAP 0x0 +#define SOF_HDA_GCTL 0x8 +/* accept unsol. response enable */ +#define SOF_HDA_GCTL_UNSOL BIT(8) +#define SOF_HDA_LLCH 0x14 +#define SOF_HDA_INTCTL 0x20 +#define SOF_HDA_INTSTS 0x24 +#define SOF_HDA_WAKESTS 0x0E +#define SOF_HDA_WAKESTS_INT_MASK ((1 << 8) - 1) + +/* SOF_HDA_GCTL register bist */ +#define SOF_HDA_GCTL_RESET BIT(0) + +/* SOF_HDA_INCTL and SOF_HDA_INTSTS regs */ +#define SOF_HDA_INT_GLOBAL_EN BIT(31) +#define SOF_HDA_INT_CTRL_EN BIT(30) +#define SOF_HDA_INT_ALL_STREAM 0xff + +#define SOF_HDA_MAX_CAPS 10 +#define SOF_HDA_CAP_ID_OFF 16 +#define SOF_HDA_CAP_ID_MASK (0xFFF << SOF_HDA_CAP_ID_OFF) +#define SOF_HDA_CAP_NEXT_MASK 0xFFFF + +#define SOF_HDA_PP_CAP_ID 0x3 +#define SOF_HDA_REG_PP_PPCH 0x10 +#define SOF_HDA_REG_PP_PPCTL 0x04 +#define SOF_HDA_PPCTL_PIE BIT(31) +#define SOF_HDA_PPCTL_GPROCEN BIT(30) + +#define SOF_HDA_SPIB_CAP_ID 0x4 +#define SOF_HDA_DRSM_CAP_ID 0x5 + +#define SOF_HDA_SPIB_BASE 0x08 +#define SOF_HDA_SPIB_INTERVAL 0x08 +#define SOF_HDA_SPIB_SPIB 0x00 +#define SOF_HDA_SPIB_MAXFIFO 0x04 + +#define SOF_HDA_PPHC_BASE 0x10 +#define SOF_HDA_PPHC_INTERVAL 0x10 + +#define SOF_HDA_PPLC_BASE 0x10 +#define SOF_HDA_PPLC_MULTI 0x10 +#define SOF_HDA_PPLC_INTERVAL 0x10 + +#define SOF_HDA_DRSM_BASE 0x08 +#define SOF_HDA_DRSM_INTERVAL 0x08 + +/* Descriptor error interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_DESC_ERR 0x10 + +/* FIFO error interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_FIFO_ERR 0x08 + +/* Buffer completion interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_COMPLETE 0x04 + +#define SOF_HDA_CL_DMA_SD_INT_MASK \ + (SOF_HDA_CL_DMA_SD_INT_DESC_ERR | \ + SOF_HDA_CL_DMA_SD_INT_FIFO_ERR | \ + SOF_HDA_CL_DMA_SD_INT_COMPLETE) +#define SOF_HDA_SD_CTL_DMA_START 0x02 /* Stream DMA start bit */ + +/* Intel HD Audio Code Loader DMA Registers */ +#define SOF_HDA_ADSP_LOADER_BASE 0x80 +#define SOF_HDA_ADSP_DPLBASE 0x70 +#define SOF_HDA_ADSP_DPUBASE 0x74 +#define SOF_HDA_ADSP_DPLBASE_ENABLE 0x01 + +/* Stream Registers */ +#define SOF_HDA_ADSP_REG_CL_SD_CTL 0x00 +#define SOF_HDA_ADSP_REG_CL_SD_STS 0x03 +#define SOF_HDA_ADSP_REG_CL_SD_LPIB 0x04 +#define SOF_HDA_ADSP_REG_CL_SD_CBL 0x08 +#define SOF_HDA_ADSP_REG_CL_SD_LVI 0x0C +#define SOF_HDA_ADSP_REG_CL_SD_FIFOW 0x0E +#define SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE 0x10 +#define SOF_HDA_ADSP_REG_CL_SD_FORMAT 0x12 +#define SOF_HDA_ADSP_REG_CL_SD_FIFOL 0x14 +#define SOF_HDA_ADSP_REG_CL_SD_BDLPL 0x18 +#define SOF_HDA_ADSP_REG_CL_SD_BDLPU 0x1C + +/* CL: Software Position Based FIFO Capability Registers */ +#define SOF_DSP_REG_CL_SPBFIFO \ + (SOF_HDA_ADSP_LOADER_BASE + 0x20) +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCH 0x0 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL 0x4 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB 0x8 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_MAXFIFOS 0xc + +/* Stream Number */ +#define SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT 20 +#define SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK \ + (0xf << SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT) + +#define HDA_DSP_HDA_BAR 0 +#define HDA_DSP_PP_BAR 1 +#define HDA_DSP_SPIB_BAR 2 +#define HDA_DSP_DRSM_BAR 3 +#define HDA_DSP_BAR 4 + +#define SRAM_WINDOW_OFFSET(x) (0x80000 + (x) * 0x20000) + +#define HDA_DSP_MBOX_OFFSET SRAM_WINDOW_OFFSET(0) + +#define HDA_DSP_PANIC_OFFSET(x) \ + (((x) & 0xFFFFFF) + HDA_DSP_MBOX_OFFSET) + +/* SRAM window 0 FW "registers" */ +#define HDA_DSP_SRAM_REG_ROM_STATUS (HDA_DSP_MBOX_OFFSET + 0x0) +#define HDA_DSP_SRAM_REG_ROM_ERROR (HDA_DSP_MBOX_OFFSET + 0x4) +/* FW and ROM share offset 4 */ +#define HDA_DSP_SRAM_REG_FW_STATUS (HDA_DSP_MBOX_OFFSET + 0x4) +#define HDA_DSP_SRAM_REG_FW_TRACEP (HDA_DSP_MBOX_OFFSET + 0x8) +#define HDA_DSP_SRAM_REG_FW_END (HDA_DSP_MBOX_OFFSET + 0xc) + +#define HDA_DSP_MBOX_UPLINK_OFFSET 0x81000 + +#define HDA_DSP_STREAM_RESET_TIMEOUT 300 +#define HDA_DSP_CL_TRIGGER_TIMEOUT 300 + +#define HDA_DSP_SPIB_ENABLE 1 +#define HDA_DSP_SPIB_DISABLE 0 + +#define SOF_HDA_MAX_BUFFER_SIZE (32 * PAGE_SIZE) + +#define HDA_DSP_STACK_DUMP_SIZE 32 + +/* ROM status/error values */ +#define HDA_DSP_ROM_STS_MASK 0xf +#define HDA_DSP_ROM_INIT 0x1 +#define HDA_DSP_ROM_FW_MANIFEST_LOADED 0x3 +#define HDA_DSP_ROM_FW_FW_LOADED 0x4 +#define HDA_DSP_ROM_FW_ENTERED 0x5 +#define HDA_DSP_ROM_RFW_START 0xf +#define HDA_DSP_ROM_CSE_ERROR 40 +#define HDA_DSP_ROM_CSE_WRONG_RESPONSE 41 +#define HDA_DSP_ROM_IMR_TO_SMALL 42 +#define HDA_DSP_ROM_BASE_FW_NOT_FOUND 43 +#define HDA_DSP_ROM_CSE_VALIDATION_FAILED 44 +#define HDA_DSP_ROM_IPC_FATAL_ERROR 45 +#define HDA_DSP_ROM_L2_CACHE_ERROR 46 +#define HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL 47 +#define HDA_DSP_ROM_API_PTR_INVALID 50 +#define HDA_DSP_ROM_BASEFW_INCOMPAT 51 +#define HDA_DSP_ROM_UNHANDLED_INTERRUPT 0xBEE00000 +#define HDA_DSP_ROM_MEMORY_HOLE_ECC 0xECC00000 +#define HDA_DSP_ROM_KERNEL_EXCEPTION 0xCAFE0000 +#define HDA_DSP_ROM_USER_EXCEPTION 0xBEEF0000 +#define HDA_DSP_ROM_UNEXPECTED_RESET 0xDECAF000 +#define HDA_DSP_ROM_NULL_FW_ENTRY 0x4c4c4e55 +#define HDA_DSP_IPC_PURGE_FW 0x01004000 + +/* various timeout values */ +#define HDA_DSP_PU_TIMEOUT 50 +#define HDA_DSP_PD_TIMEOUT 50 +#define HDA_DSP_RESET_TIMEOUT 50 +#define HDA_DSP_BASEFW_TIMEOUT 3000 +#define HDA_DSP_INIT_TIMEOUT 500 +#define HDA_DSP_CTRL_RESET_TIMEOUT 100 +#define HDA_DSP_WAIT_TIMEOUT 500 /* 500 msec */ + +#define HDA_DSP_ADSPIC_IPC 1 +#define HDA_DSP_ADSPIS_IPC 1 + +/* Intel HD Audio General DSP Registers */ +#define HDA_DSP_GEN_BASE 0x0 +#define HDA_DSP_REG_ADSPCS (HDA_DSP_GEN_BASE + 0x04) +#define HDA_DSP_REG_ADSPIC (HDA_DSP_GEN_BASE + 0x08) +#define HDA_DSP_REG_ADSPIS (HDA_DSP_GEN_BASE + 0x0C) +#define HDA_DSP_REG_ADSPIC2 (HDA_DSP_GEN_BASE + 0x10) +#define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14) + +/* Intel HD Audio Inter-Processor Communication Registers */ +#define HDA_DSP_IPC_BASE 0x40 +#define HDA_DSP_REG_HIPCT (HDA_DSP_IPC_BASE + 0x00) +#define HDA_DSP_REG_HIPCTE (HDA_DSP_IPC_BASE + 0x04) +#define HDA_DSP_REG_HIPCI (HDA_DSP_IPC_BASE + 0x08) +#define HDA_DSP_REG_HIPCIE (HDA_DSP_IPC_BASE + 0x0C) +#define HDA_DSP_REG_HIPCCTL (HDA_DSP_IPC_BASE + 0x10) + +/* HIPCI */ +#define HDA_DSP_REG_HIPCI_BUSY BIT(31) +#define HDA_DSP_REG_HIPCI_MSG_MASK 0x7FFFFFFF + +/* HIPCIE */ +#define HDA_DSP_REG_HIPCIE_DONE BIT(30) +#define HDA_DSP_REG_HIPCIE_MSG_MASK 0x3FFFFFFF + +/* HIPCCTL */ +#define HDA_DSP_REG_HIPCCTL_DONE BIT(1) +#define HDA_DSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define HDA_DSP_REG_HIPCT_BUSY BIT(31) +#define HDA_DSP_REG_HIPCT_MSG_MASK 0x7FFFFFFF + +/* HIPCTE */ +#define HDA_DSP_REG_HIPCTE_MSG_MASK 0x3FFFFFFF + +#define HDA_DSP_ADSPIC_CL_DMA 0x2 +#define HDA_DSP_ADSPIS_CL_DMA 0x2 + +/* Delay before scheduling D0i3 entry */ +#define BXT_D0I3_DELAY 5000 + +#define FW_CL_STREAM_NUMBER 0x1 + +/* ADSPCS - Audio DSP Control & Status */ + +/* + * Core Reset - asserted high + * CRST Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CRST_SHIFT 0 +#define HDA_DSP_ADSPCS_CRST_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CRST_SHIFT) + +/* + * Core run/stall - when set to '1' core is stalled + * CSTALL Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CSTALL_SHIFT 8 +#define HDA_DSP_ADSPCS_CSTALL_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CSTALL_SHIFT) + +/* + * Set Power Active - when set to '1' turn cores on + * SPA Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_SPA_SHIFT 16 +#define HDA_DSP_ADSPCS_SPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_SPA_SHIFT) + +/* + * Current Power Active - power status of cores, set by hardware + * CPA Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CPA_SHIFT 24 +#define HDA_DSP_ADSPCS_CPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CPA_SHIFT) + +#define HDA_DSP_ADSPIC_CL_DMA 0x2 +#define HDA_DSP_ADSPIS_CL_DMA 0x2 + +/* Mask for a given core index, c = 0.. number of supported cores - 1 */ +#define HDA_DSP_CORE_MASK(c) BIT(c) + +/* + * Mask for a given number of cores + * nc = number of supported cores + */ +#define SOF_DSP_CORES_MASK(nc) GENMASK(((nc) - 1), 0) + +/* Intel HD Audio Inter-Processor Communication Registers for Cannonlake*/ +#define CNL_DSP_IPC_BASE 0xc0 +#define CNL_DSP_REG_HIPCTDR (CNL_DSP_IPC_BASE + 0x00) +#define CNL_DSP_REG_HIPCTDA (CNL_DSP_IPC_BASE + 0x04) +#define CNL_DSP_REG_HIPCTDD (CNL_DSP_IPC_BASE + 0x08) +#define CNL_DSP_REG_HIPCIDR (CNL_DSP_IPC_BASE + 0x10) +#define CNL_DSP_REG_HIPCIDA (CNL_DSP_IPC_BASE + 0x14) +#define CNL_DSP_REG_HIPCCTL (CNL_DSP_IPC_BASE + 0x28) + +/* HIPCI */ +#define CNL_DSP_REG_HIPCIDR_BUSY BIT(31) +#define CNL_DSP_REG_HIPCIDR_MSG_MASK 0x7FFFFFFF + +/* HIPCIE */ +#define CNL_DSP_REG_HIPCIDA_DONE BIT(31) +#define CNL_DSP_REG_HIPCIDA_MSG_MASK 0x7FFFFFFF + +/* HIPCCTL */ +#define CNL_DSP_REG_HIPCCTL_DONE BIT(1) +#define CNL_DSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define CNL_DSP_REG_HIPCTDR_BUSY BIT(31) +#define CNL_DSP_REG_HIPCTDR_MSG_MASK 0x7FFFFFFF + +/* HIPCTDA */ +#define CNL_DSP_REG_HIPCTDA_DONE BIT(31) +#define CNL_DSP_REG_HIPCTDA_MSG_MASK 0x7FFFFFFF + +/* HIPCTDD */ +#define CNL_DSP_REG_HIPCTDD_MSG_MASK 0x7FFFFFFF + +/* BDL */ +#define HDA_DSP_BDL_SIZE 4096 +#define HDA_DSP_MAX_BDL_ENTRIES \ + (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) + +/* Number of DAIs */ +#define SOF_SKL_NUM_DAIS 14 + +struct sof_intel_dsp_bdl { + u32 addr_l; + u32 addr_h; + u32 size; + u32 ioc; +} __attribute((packed)); + +/* DSP hardware descriptor */ +struct sof_intel_dsp_desc { + int id; + int cores_num; + int cores_mask; + int ipc_req; + int ipc_req_mask; + int ipc_ack; + int ipc_ack_mask; + int ipc_ctl; + struct snd_sof_dsp_ops *ops; +}; + +/* per stream data for HDA DSP Frontend */ +struct sof_intel_hda_stream { + + /* addresses for stream HDA functions */ + void __iomem *pphc_addr; + void __iomem *pplc_addr; + void __iomem *spib_addr; + void __iomem *fifo_addr; + void __iomem *drsm_addr; + + /* runtime state */ + u32 dpib; + u32 lpib; + int tag; + int direction; + bool open; + bool running; + u32 index; + + /* buffer & descriptors */ + struct snd_dma_buffer bdl; + void __iomem *sd_addr; /* stream descriptor pointer */ + int sd_offset; /* Stream descriptor offset */ + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int fifo_size; /* FIFO size */ + + __le32 *posbuf; /* position buffer pointer */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int config; /* format config value */ + + /* PCM */ + struct snd_pcm_substream *substream; +}; + +#define SOF_HDA_PLAYBACK_STREAMS 16 +#define SOF_HDA_CAPTURE_STREAMS 16 +#define SOF_HDA_PLAYBACK 0 +#define SOF_HDA_CAPTURE 1 + +/* represents DSP HDA controller frontend - i.e. host facing control */ +struct sof_intel_hda_dev { + + /* hw config */ + const struct sof_intel_dsp_desc *desc; + + /* streams */ + struct sof_intel_hda_stream pstream[SOF_HDA_PLAYBACK_STREAMS]; + struct sof_intel_hda_stream cstream[SOF_HDA_CAPTURE_STREAMS]; + int num_capture; + int num_playback; + + /* position buffers */ + struct snd_dma_buffer posbuffer; + + /*trace */ + struct sof_intel_hda_stream *dtrace_stream; + + int irq; +}; + +/* + * DSP Core services. + */ +int hda_dsp_probe(struct snd_sof_dev *sdev); +int hda_dsp_remove(struct snd_sof_dev *sdev); +int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); +bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_suspend(struct snd_sof_dev *sdev, int state); +int hda_dsp_resume(struct snd_sof_dev *sdev); +void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); + +/* + * DSP IO + */ +void hda_dsp_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value); +u32 hda_dsp_read(struct snd_sof_dev *sdev, void __iomem *addr); +void hda_dsp_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value); +u64 hda_dsp_read64(struct snd_sof_dev *sdev, void __iomem *addr); +void hda_dsp_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size); +void hda_dsp_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size); +void hda_dsp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes); +void hda_dsp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes); + +/* + * DSP PCM Operations. + */ +int hda_dsp_pcm_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); +int hda_dsp_pcm_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); +int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd); + +/* + * DSP Stream Operations. + */ + +int hda_dsp_stream_init(struct snd_sof_dev *sdev); +void hda_dsp_stream_free(struct snd_sof_dev *sdev); +int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params); +int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd); +irqreturn_t hda_dsp_stream_interrupt(int irq, void *context); +irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); +int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream, + struct sof_intel_dsp_bdl *bdl, int size, + struct snd_pcm_hw_params *params); +struct sof_intel_hda_stream * + hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev); +struct sof_intel_hda_stream * + hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev); +int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int stream_tag); +int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int stream_tag); +int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + int enable, u32 size); + +/* + * DSP IPC Operations. + */ +int hda_dsp_ipc_is_ready(struct snd_sof_dev *sdev); +int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id); +irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context); +irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context); +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); + +/* + * DSP Code loader. + */ +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw, + bool first_boot); +int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); + +/* + * HDA Controller Operations. + */ +int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev); +int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev); + +/* + * Trace Control. + */ +int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); +int hda_dsp_trace_release(struct snd_sof_dev *sdev); +int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); + +/* common dai driver */ +extern struct snd_soc_dai_driver skl_dai[]; + +/* + * Platform Specific HW abstraction Ops. + */ +extern struct snd_sof_dsp_ops sof_apl_ops; +extern struct snd_sof_dsp_ops sof_cnl_ops; +extern struct snd_sof_dsp_ops sof_skl_ops; + +#endif From 9b4fd60c0cd59614e12b2134e2ad8ef9d3336e06 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:11 +0100 Subject: [PATCH 036/298] ASoC: SOF: Intel: Add HDA controller for Intel DSP Support HDA controller operations for DSP and provide space for future DSP HDA FW integration. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ctrl.c | 133 +++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 sound/soc/sof/intel/hda-ctrl.c diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c new file mode 100644 index 00000000000000..57b877bde8a8f4 --- /dev/null +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * HDA Operations. + */ + +int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev) +{ + unsigned long timeout; + u32 gctl = 0; + + /* reset the HDA controller */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL, + SOF_HDA_GCTL_RESET, 0); + + /* wait for reset */ + timeout = jiffies + msecs_to_jiffies(HDA_DSP_CTRL_RESET_TIMEOUT); + while (time_before(jiffies, timeout)) { + usleep_range(500, 1000); + gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL); + if ((gctl & SOF_HDA_GCTL_RESET) == 0) + goto clear; + } + + /* reset failed */ + dev_err(sdev->dev, "error: failed to reset HDA controller gctl 0x%x\n", + gctl); + return -EIO; + +clear: + /* wait for codec */ + usleep_range(500, 1000); + + /* now take controller out of reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL, + SOF_HDA_GCTL_RESET, SOF_HDA_GCTL_RESET); + + /* wait for controller to be ready */ + timeout = jiffies + msecs_to_jiffies(HDA_DSP_CTRL_RESET_TIMEOUT); + while (time_before(jiffies, timeout)) { + gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL); + if ((gctl & SOF_HDA_GCTL_RESET) == 1) + return 0; + usleep_range(500, 1000); + } + + /* reset failed */ + dev_err(sdev->dev, "error: failed to ready HDA controller gctl 0x%x\n", + gctl); + return -EIO; +} + +int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) +{ + u32 cap, offset, feature; + int ret = -ENODEV, count = 0; + + offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); + + do { + cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); + + dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n", + offset & SOF_HDA_CAP_NEXT_MASK); + + feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF; + + switch (feature) { + case SOF_HDA_PP_CAP_ID: + dev_dbg(sdev->dev, "found DSP capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_PP_BAR] = sdev->bar[HDA_DSP_HDA_BAR] + + offset; + ret = 0; + break; + case SOF_HDA_SPIB_CAP_ID: + dev_dbg(sdev->dev, "found SPIB capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_SPIB_BAR] = + sdev->bar[HDA_DSP_HDA_BAR] + offset; + break; + case SOF_HDA_DRSM_CAP_ID: + dev_dbg(sdev->dev, "found DRSM capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_DRSM_BAR] = + sdev->bar[HDA_DSP_HDA_BAR] + offset; + break; + default: + dev_vdbg(sdev->dev, "found capability %d at 0x%x\n", + feature, offset); + break; + } + + offset = cap & SOF_HDA_CAP_NEXT_MASK; + } while (count++ <= SOF_HDA_MAX_CAPS && offset); + + return ret; +} + From 46f92e8cea7ff665cf4805b87c69290d81acf7dd Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:12 +0100 Subject: [PATCH 037/298] ASoC: SOF: Intel: Add Intel specific HDA DSP HW operations Add support for various PM and core reset/run state transitions. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-dsp.c | 256 ++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 sound/soc/sof/intel/hda-dsp.c diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c new file mode 100644 index 00000000000000..86d474c083a920 --- /dev/null +++ b/sound/soc/sof/intel/hda-dsp.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * DSP Core control. + */ + +int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* set reset bits for cores */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_ADSPCS_CRST_MASK(core_mask)); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_RESET_TIMEOUT); + + /* has core entered reset ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != + HDA_DSP_ADSPCS_CRST_MASK(core_mask)) { + dev_err(sdev->dev, + "error: reset enter failed: core_mask %x adspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* clear reset bits for cores */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + 0); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), 0, + HDA_DSP_RESET_TIMEOUT); + + /* has core left reset ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != 0) { + dev_err(sdev->dev, + "error: reset leave failed: core_mask %x adspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + /* stall core */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_HDA_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + + /* set reset state */ + return hda_dsp_core_reset_enter(sdev, core_mask); +} + +int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + int ret; + + /* leave reset state */ + ret = hda_dsp_core_reset_leave(sdev, core_mask); + if (ret < 0) + return ret; + + /* run core */ + dev_dbg(sdev->dev, "unstall/run core: core_mask = %x\n", core_mask); + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + 0); + + /* is core now running ? */ + if (!hda_dsp_core_is_enabled(sdev, core_mask)) { + hda_dsp_core_stall_reset(sdev, core_mask); + dev_err(sdev->dev, "error: DSP start core failed: core_mask %x\n", + core_mask); + ret = -EIO; + } + + return ret; +} + +/* + * Power Management. + */ + +int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* update bits */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_SPA_MASK(core_mask), + HDA_DSP_ADSPCS_SPA_MASK(core_mask)); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CPA_MASK(core_mask), + HDA_DSP_ADSPCS_CPA_MASK(core_mask), + HDA_DSP_PU_TIMEOUT); + if (ret < 0) + dev_err(sdev->dev, "error: timeout on core powerup\n"); + + /* did core power up ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) != + HDA_DSP_ADSPCS_CPA_MASK(core_mask)) { + dev_err(sdev->dev, + "error: power up core failed core_mask %xadspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + /* update bits */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0); + + /* poll with timeout to check if operation successful */ + return snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, HDA_DSP_ADSPCS_CPA_MASK(core_mask), 0, + HDA_DSP_PD_TIMEOUT); +} + +bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, + unsigned int core_mask) +{ + int val; + bool is_enable; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); + + is_enable = ((val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) && + (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask))); + + dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", + is_enable, core_mask); + + return is_enable; +} + +int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, + unsigned int core_mask) +{ + int ret; + + /* place core in reset prior to power down */ + ret = hda_dsp_core_stall_reset(sdev, core_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core reset failed: core_mask %x\n", + core_mask); + return ret; + } + + /* power down core */ + ret = hda_dsp_core_power_down(sdev, core_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core power down fail mask %x: %d\n", + core_mask, ret); + return ret; + } + + /* make sure we are in OFF state */ + if (hda_dsp_core_is_enabled(sdev, core_mask)) { + dev_err(sdev->dev, "error: dsp core disable fail mask %x: %d\n", + core_mask, ret); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* power down DSP */ + return hda_dsp_core_reset_power_down(sdev, chip->cores_mask); +} + +int hda_dsp_resume(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* power up the DSP */ + return hda_dsp_core_power_up(sdev, chip->cores_mask); +} From 2129f6cbdbc89c1d57582dc603e3c883da21572b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:13 +0100 Subject: [PATCH 038/298] ASoC: SOF: Intel: Add Intel specific HDA IPC mechanisms. Add HDA specific IPC mechanism for Intel DSP HW. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ipc.c | 389 ++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 sound/soc/sof/intel/hda-ipc.c diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c new file mode 100644 index 00000000000000..0fb3f48ef776a6 --- /dev/null +++ b/sound/soc/sof/intel/hda-ipc.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) +{ + if (dir == SOF_IPC_HOST_REPLY) { + /* + * tell DSP cmd is done - clear busy + * interrupt and send reply msg to dsp + */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCT, + HDA_DSP_REG_HIPCT_BUSY, + HDA_DSP_REG_HIPCT_BUSY); + } else { + /* + * set DONE bit - tell DSP we have received the reply msg + * from DSP, and processed it, don't send more reply to host + */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCIE, + HDA_DSP_REG_HIPCIE_DONE, + HDA_DSP_REG_HIPCIE_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + } + + return 0; +} + +int hda_dsp_ipc_is_ready(struct snd_sof_dev *sdev) +{ + u64 busy, done; + + /* is DSP ready for next IPC command */ + busy = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); + done = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); + if ((busy & HDA_DSP_REG_HIPCI_BUSY) || + (done & HDA_DSP_REG_HIPCIE_DONE)) + return 0; + + return 1; +} + +int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u32 cmd = msg->header; + + /* send IPC message to DSP */ + hda_dsp_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, + cmd | HDA_DSP_REG_HIPCI_BUSY); + + return 0; +} + +int hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get IPC reply from DSP in the mailbox */ + hda_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply, + sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + hda_dsp_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, size); + + return ret; +} + +/* IPC handler thread */ +irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 hipci, hipcie, hipct, hipcte, msg = 0, msg_ext = 0; + irqreturn_t ret = IRQ_NONE; + int reply = -EINVAL; + + /* here we handle IPC interrupts only */ + if (!(sdev->irq_status & HDA_DSP_ADSPIS_IPC)) + return ret; + + /* read IPC status */ + hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCIE); + hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); + + /* is this a reply message from the DSP */ + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { + + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCI); + msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; + msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* mask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, 0); + + /* handle immediate reply from DSP core - ignore ROM messages */ + if (msg != 0x1004000) + reply = snd_sof_ipc_reply(sdev, msg); + + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (reply) + hda_dsp_ipc_cmd_done(sdev, SOF_IPC_DSP_REPLY); + + ret = IRQ_HANDLED; + } + + /* is this a new message from DSP */ + if (hipct & HDA_DSP_REG_HIPCT_BUSY) { + + hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCTE); + msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; + msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* handle messages from DSP */ + if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + /* this is a PANIC message !! */ + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + } else { + /* normal message - process normally*/ + snd_sof_ipc_msgs_rx(sdev); + } + + /* clear busy interrupt */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCT, + HDA_DSP_REG_HIPCT_BUSY, + HDA_DSP_REG_HIPCT_BUSY); + ret = IRQ_HANDLED; + } + + if (ret == IRQ_HANDLED) { + /* reenable IPC interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + } + + /* wake up sleeper if we are loading code */ + if (sdev->code_loading) { + sdev->code_loading = 0; + wake_up(&sdev->waitq); + } + + return ret; +} + +/* is this IRQ for ADSP ? - we only care about IPC here */ +irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + int ret = IRQ_NONE; + + spin_lock(&sdev->hw_lock); + + /* store status */ + sdev->irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIS); + + /* invalid message ? */ + if (sdev->irq_status == 0xffffffff) + goto out; + + /* IPC message ? */ + if (sdev->irq_status & HDA_DSP_ADSPIS_IPC) { + /* disable IPC interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, 0); + ret = IRQ_WAKE_THREAD; + } + +out: + spin_unlock(&sdev->hw_lock); + return ret; +} + +/* + * IPC Firmware ready. + */ + +static void ipc_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + + SRAM_WINDOW_OFFSET(elem->id); + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = HDA_DSP_MBOX_UPLINK_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + hda_dsp_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + dev_info(sdev->dev, + " Firmware info: version %d.%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, HDA_DSP_MBOX_UPLINK_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + ipc_get_windows(sdev); + + return 0; +} From 0166e8ae3ab9bf0875da24df543fc5cb8ce2a19c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:14 +0100 Subject: [PATCH 039/298] ASoC: SOF: Intel: Add Intel specific HDA firmware loader Add support for loading DSP firmware on Intel HDA based platforms. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-loader.c | 368 +++++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 sound/soc/sof/intel/hda-loader.c diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c new file mode 100644 index 00000000000000..d5476bbdc70d6f --- /dev/null +++ b/sound/soc/sof/intel/hda-loader.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for HDA DSP code loader + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab, + int direction) +{ + struct sof_intel_hda_stream *stream = NULL; + struct pci_dev *pci = sdev->pci; + int ret; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + stream = hda_dsp_stream_get_pstream(sdev); + } else { + dev_err(sdev->dev, "error: code loading DMA is playback only\n"); + return -EINVAL; + } + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* allocate DMA buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); + if (ret < 0) { + dev_err(sdev->dev, "error: memory alloc failed: %x\n", ret); + goto error; + } + + stream->config = format; + stream->bufsize = size; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + goto error; + } + + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, size); + + return stream->tag; + +error: + hda_dsp_stream_put_pstream(sdev, stream->tag); + snd_dma_free_pages(dmab); + return ret; +} + +/* + * first boot sequence has some extra steps. core 0 waits for power + * status on core 1, so power up core 1 also momentarily, keep it in + * reset/stall and then turn it off + */ +static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, + u32 fwsize) +{ + int tag, ret, i; + u32 hipcie; + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* prepare DMA for code loader stream */ + tag = cl_stream_prepare(sdev, 0x40, fwsize, &sdev->dmab, + SNDRV_PCM_STREAM_PLAYBACK); + + if (tag <= 0) { + dev_err(sdev->dev, "error: dma prepare for fw loading err: %x\n", + tag); + return tag; + } + + memcpy(sdev->dmab.area, fwdata, fwsize); + + /* step 1: power up corex */ + ret = hda_dsp_core_power_up(sdev, chip->cores_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); + goto err; + } + + /* step 2: purge FW request */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, + chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW | + ((tag - 1) << 9))); + + /* step 3: unset core 0 reset state & unstall/run core 0 */ + ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core start failed %d\n", ret); + ret = -EIO; + goto err; + } + + /* step 4: wait for IPC DONE bit from ROM */ + for (i = HDA_DSP_INIT_TIMEOUT; i > 0; i--) { + hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + chip->ipc_ack); + + if (hipcie & chip->ipc_ack_mask) { + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + chip->ipc_ack, + chip->ipc_ack_mask, + chip->ipc_ack_mask); + goto step5; + } + mdelay(1); + } + + dev_err(sdev->dev, "error: waiting for HIPCIE done, reg: 0x%x\n", + hipcie); + goto err; + +step5: + /* step 5: power down corex */ + ret = hda_dsp_core_power_down(sdev, + chip->cores_mask & ~(HDA_DSP_CORE_MASK(0))); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core x power down failed\n"); + goto err; + } + + /* step 6: enable interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + + /* enable IPC DONE interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + + /* enable IPC BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_BUSY, + HDA_DSP_REG_HIPCCTL_BUSY); + + /* step 7: wait for ROM init */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS, + HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_INIT, + HDA_DSP_INIT_TIMEOUT); + if (ret >= 0) + goto out; + + ret = -EIO; + +err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + //sdev->dsp_ops.cleanup(sdev->dev, &sdev->dmab, tag); + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1)); + return ret; + +out: + return tag; +} + +static int cl_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd) +{ + /* code loader is special case that reuses stream ops */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + wait_event_timeout(sdev->waitq, !sdev->code_loading, + HDA_DSP_CL_TRIGGER_TIMEOUT); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = true; + return 0; + default: + return hda_dsp_stream_trigger(sdev, stream, cmd); + } +} + +static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream) +{ + int ret; + + ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + + /* TODO: spin lock ?*/ + stream->open = 0; + stream->running = 0; + stream->substream = NULL; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0); + snd_dma_free_pages(dmab); + dmab->area = NULL; + stream->bufsize = 0; + stream->config = 0; + + return ret; +} + +static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_stream *stream = NULL; + struct sof_intel_hda_dev *hdev = sdev->hda; + int ret, status, i; + + /* get stream with tag */ + for (i = 0; i < hdev->num_playback; i++) { + if (hdev->pstream[i].tag == tag) { + stream = &hdev->pstream[i]; + break; + } + } + if (!stream) { + dev_err(sdev->dev, + "error: could not get stream with stream tag%d\n", + tag); + return -ENODEV; + } + + ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "error: DMA trigger start failed\n"); + return ret; + } + + status = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS, + HDA_DSP_ROM_STS_MASK, + HDA_DSP_ROM_FW_ENTERED, + HDA_DSP_BASEFW_TIMEOUT); + + ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) { + dev_err(sdev->dev, "error: DMA trigger stop failed\n"); + return ret; + } + + ret = cl_cleanup(sdev, &sdev->dmab, stream); + if (ret < 0) { + dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); + return ret; + } + + return status; +} + +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw, + bool first_boot) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + int ret; + + /* set code loading condition to true */ + sdev->code_loading = 1; + + ret = request_firmware(&plat_data->fw, + plat_data->machine->sof_fw_filename, sdev->dev); + + if (ret < 0) { + dev_err(sdev->dev, "error: request firmware failed err: %d\n", + ret); + return -EINVAL; + } + + if (!plat_data->fw) + return -EINVAL; + + return ret; +} + +int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + struct firmware stripped_firmware; + int ret, tag; + + stripped_firmware.data = plat_data->fw->data; + stripped_firmware.size = plat_data->fw->size; + + tag = cl_dsp_init(sdev, stripped_firmware.data, + stripped_firmware.size); + + /* retry enabling core and ROM load. seemed to help */ + if (tag < 0) { + tag = cl_dsp_init(sdev, stripped_firmware.data, + stripped_firmware.size); + if (tag <= 0) { + dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); + dev_err(sdev->dev, "Core En/ROM load fail:%d\n", + tag); + ret = tag; + goto irq_err; + } + } + + /* init for booting wait */ + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + /* at this point DSP ROM has been initialized and should be ready for + * code loading and firmware boot + */ + ret = cl_copy_fw(sdev, tag); + if (ret < 0) { + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + goto irq_err; + } + + dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); + + return ret; + +irq_err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + return ret; +} From 5866022876d847fea939f56ee543889d3630d4e4 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:15 +0100 Subject: [PATCH 040/298] ASoC: SOF: Intel: Add Intel specific HDA PCM operations Add PCM operations for Intel HDA based DSPs. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-pcm.c | 177 ++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 sound/soc/sof/intel/hda-pcm.c diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c new file mode 100644 index 00000000000000..5511277985284d --- /dev/null +++ b/sound/soc/sof/intel/hda-pcm.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +#define SDnFMT_BASE(x) ((x) << 14) +#define SDnFMT_MULT(x) (((x) - 1) << 11) +#define SDnFMT_DIV(x) (((x) - 1) << 8) +#define SDnFMT_BITS(x) ((x) << 4) +#define SDnFMT_CHAN(x) ((x) << 0) + +static inline u32 get_mult_div(struct snd_sof_dev *sdev, int rate) +{ + switch (rate) { + case 8000: + return SDnFMT_DIV(6); + case 9600: + return SDnFMT_DIV(5); + case 11025: + return SDnFMT_BASE(1) | SDnFMT_DIV(4); + case 16000: + return SDnFMT_DIV(3); + case 22050: + return SDnFMT_BASE(1) | SDnFMT_DIV(2); + case 32000: + return SDnFMT_DIV(3) | SDnFMT_MULT(2); + case 44100: + return SDnFMT_BASE(1); + case 48000: + return 0; + case 88200: + return SDnFMT_BASE(1) | SDnFMT_MULT(2); + case 96000: + return SDnFMT_MULT(2); + case 176400: + return SDnFMT_BASE(1) | SDnFMT_MULT(4); + case 192000: + return SDnFMT_MULT(4); + default: + dev_warn(sdev->dev, "can't find div rate %d using 48kHz\n", + rate); + return 0; /* use 48KHz if not found */ + } +}; + +static inline u32 get_bits(struct snd_sof_dev *sdev, int sample_bits) +{ + switch (sample_bits) { + case 8: + return SDnFMT_BITS(0); + case 16: + return SDnFMT_BITS(1); + case 20: + return SDnFMT_BITS(2); + case 24: + return SDnFMT_BITS(3); + case 32: + return SDnFMT_BITS(4); + default: + dev_warn(sdev->dev, "can't find %d bits using 16bit\n", + sample_bits); + return SDnFMT_BITS(1); /* use 16bits format if not found */ + } +}; + +int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + struct snd_dma_buffer *dmab; + int ret; + u32 size, rate, bits; + + size = params_buffer_bytes(params); + rate = get_mult_div(sdev, params_rate(params)); + bits = get_bits(sdev, params_width(params)); + + stream->substream = substream; + + dmab = substream->runtime->dma_buffer_p; + + stream->config = rate | bits | (params_channels(params) - 1); + stream->bufsize = size; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + return ret; + } + + /* disable SPIB, to enable buffer wrap for stream */ + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + + return stream->tag; +} + +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + + return hda_dsp_stream_trigger(sdev, stream, cmd); +} + +int hda_dsp_pcm_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct sof_intel_hda_stream *stream; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stream = hda_dsp_stream_get_pstream(sdev); + else + stream = hda_dsp_stream_get_cstream(sdev); + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* binding pcm substream to hda stream */ + substream->runtime->private_data = stream; + return 0; +} + +int hda_dsp_pcm_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = hda_dsp_stream_put_pstream(sdev, stream->tag); + else + ret = hda_dsp_stream_put_cstream(sdev, stream->tag); + + if (ret) { + dev_dbg(sdev->dev, "stream %s not opened!\n", substream->name); + return -ENODEV; + } + + /* unbinding pcm substream to hda stream */ + substream->runtime->private_data = NULL; + return 0; +} + From d0d428b24de21a9dda925a5cfa11658869ea41c5 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:16 +0100 Subject: [PATCH 041/298] ASoC: SOF: Intel: Add Intel specific HDA stream operations Add support or HDA DSP stream operations for Intel HDA DSPs. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-stream.c | 656 +++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 sound/soc/sof/intel/hda-stream.c diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c new file mode 100644 index 00000000000000..a9e81a505aa6c6 --- /dev/null +++ b/sound/soc/sof/intel/hda-stream.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * set up Buffer Descriptor List (BDL) for host memory transfer + * BDL describes the location of the individual buffers and is little endian. + */ +int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream, + struct sof_intel_dsp_bdl *bdl, int size, + struct snd_pcm_hw_params *params) +{ + int offset = 0; + int chunk = PAGE_SIZE, entry_size; + dma_addr_t addr; + + if (stream->substream && params) { + chunk = params_period_bytes(params); + dev_dbg(sdev->dev, "period_bytes:0x%x\n", chunk); + } + + while (size > 0) { + if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { + dev_err(sdev->dev, "error: stream frags exceeded\n"); + return -EINVAL; + } + + addr = snd_sgbuf_get_addr(dmab, offset); + + /* program BDL addr */ + bdl->addr_l = lower_32_bits(addr); + bdl->addr_h = upper_32_bits(addr); + + entry_size = size > chunk ? chunk : size; + + /* program BDL size */ + bdl->size = snd_sgbuf_get_chunk_size(dmab, offset, entry_size); + + /* program the IOC to enable interrupt + * when the whole fragment is processed + */ + size -= entry_size; + if (size) + bdl->ioc = 0; + else + bdl->ioc = 1; + + stream->frags++; + offset += bdl->size; + + dev_vdbg(sdev->dev, "bdl, frags:%d, entry size:0x%x;\n", + stream->frags, entry_size); + + bdl++; + } + + return offset; +} + +int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + int enable, u32 size) +{ + u32 mask = 0; + + if (!sdev->bar[HDA_DSP_SPIB_BAR]) { + dev_err(sdev->dev, "error: address of spib capability is NULL\n"); + return -EINVAL; + } + + mask |= (1 << stream->index); + + /* enable/disable SPIB for the stream */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, + enable << stream->index); + + /* set the SPIB value */ + hda_dsp_write(sdev, stream->spib_addr, size); + + return 0; +} + +/* get next unused playback stream */ +struct sof_intel_hda_stream * +hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream = NULL; + int i; + + /* get an unused playback stream */ + for (i = 0; i < hdev->num_playback; i++) { + if (!hdev->pstream[i].open) { + hdev->pstream[i].open = true; + stream = &hdev->pstream[i]; + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free playback streams\n"); + + return stream; +} + +/* get next unused capture stream */ +struct sof_intel_hda_stream * +hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream = NULL; + int i; + + /* get an unused capture stream */ + for (i = 0; i < hdev->num_capture; i++) { + if (!hdev->cstream[i].open) { + hdev->cstream[i].open = true; + stream = &hdev->cstream[i]; + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free capture streams\n"); + + return stream; +} + +/* free playback stream */ +int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + int i; + + /* find used playback stream */ + for (i = 0; i < hdev->num_playback; i++) { + if (hdev->pstream[i].open && + hdev->pstream[i].tag == tag) { + hdev->pstream[i].open = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + +/* free capture stream */ +int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + int i; + + /* find used capture stream */ + for (i = 0; i < hdev->num_capture; i++) { + if (hdev->cstream[i].open && + hdev->cstream[i].tag == tag) { + hdev->cstream[i].open = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + +int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd) +{ + /* cmd must be for audio stream */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_START: + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, + 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = true; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = false; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, 0x0); + break; + default: + dev_err(sdev->dev, "error: unknown command: %d\n", cmd); + return -EINVAL; + } + + return 0; +} + +/* + * prepare for common hdac registers settings, for both code loader + * and normal stream. + */ +int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_dsp_bdl *bdl; + int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 val, mask; + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* decouple host and link DMA */ + mask = 0x1 << stream->index; + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + + if (!dmab) { + dev_err(sdev->dev, "error: no dma buffer allocated!\n"); + return -ENODEV; + } + + /* clear stream status */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK | + SOF_HDA_SD_CTL_DMA_START, 0); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + /* stream reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + 0x1); + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset); + if (val & 0x1) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "error: stream reset failed\n"); + return -ETIMEDOUT; + } + + timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + 0x0); + + /* wait for hardware to report that stream is out of reset */ + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset); + if ((val & 0x1) == 0) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); + return -ETIMEDOUT; + } + + if (stream->posbuf) + *stream->posbuf = 0; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + 0x0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + 0x0); + + /* clear stream status */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK | + SOF_HDA_SD_CTL_DMA_START, 0); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->frags = 0; + + bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; + ret = hda_dsp_stream_setup_bdl(sdev, dmab, stream, bdl, + stream->bufsize, params); + if (ret < 0) { + dev_err(sdev->dev, "error: set up of BDL failed\n"); + return ret; + } + + /* set up stream descriptor for DMA */ + /* program stream tag */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK, + stream->tag << + SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT); + + /* program cyclic buffer length */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, + stream->bufsize); + + /* program stream format */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_FORMAT, + 0xffff, stream->config); + + /* program last valid index */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, + 0xffff, (stream->frags - 1)); + + /* program BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + (u32)stream->bdl.addr); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + upper_32_bits(stream->bdl.addr)); + + /* enable position buffer */ + if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) + & SOF_HDA_ADSP_DPLBASE_ENABLE)) + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, + (u32)hdev->posbuffer.addr | + SOF_HDA_ADSP_DPLBASE_ENABLE); + + /* set interrupt enable bits */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + /* read FIFO size */ + if (stream->direction == SNDRV_PCM_STREAM_PLAYBACK) { + stream->fifo_size = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE); + stream->fifo_size &= 0xffff; + stream->fifo_size += 1; + } else { + stream->fifo_size = 0; + } + + return ret; +} + +irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 status; + + if (!pm_runtime_active(sdev->dev)) + return IRQ_NONE; + + status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + + if (status == 0 || status == 0xffffffff) + return IRQ_NONE; + + return status ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + struct sof_intel_hda_dev *hdev = sdev->hda; + u32 status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + u32 sd_status; + int i; + + /* check playback streams */ + for (i = 0; i < hdev->num_playback; i++) { + /* is IRQ for this stream ? */ + if (status & (1 << hdev->pstream[i].index)) { + sd_status = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + hdev->pstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS) & + 0xff; + + dev_dbg(sdev->dev, "pstream %d status 0x%x\n", + i, sd_status); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + hdev->pstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + if (!hdev->pstream[i].substream || + !hdev->pstream[i].running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) + continue; + } + } + + /* check capture streams */ + for (i = 0; i < hdev->num_capture; i++) { + /* is IRQ for this stream ? */ + if (status & (1 << hdev->cstream[i].index)) { + sd_status = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + hdev->cstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS) & + 0xff; + + dev_dbg(sdev->dev, "cstream %d status 0x%x\n", + i, sd_status); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + hdev->cstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + if (!hdev->cstream[i].substream || + !hdev->cstream[i].running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) + continue; + } + } + + return IRQ_HANDLED; +} + +int hda_dsp_stream_init(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream; + struct pci_dev *pci = sdev->pci; + int i, num_playback, num_capture, num_total, ret; + u32 gcap; + + gcap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCAP); + dev_dbg(sdev->dev, "hda global caps = 0x%x\n", gcap); + + /* get stream count from GCAP */ + num_capture = (gcap >> 8) & 0x0f; + num_playback = (gcap >> 12) & 0x0f; + num_total = num_playback + num_capture; + + hdev->num_capture = num_capture; + hdev->num_playback = num_playback; + + dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", + num_playback, num_capture); + + if (num_playback >= SOF_HDA_PLAYBACK_STREAMS) { + dev_err(sdev->dev, "error: too many playback streams %d\n", + num_playback); + return -EINVAL; + } + + if (num_capture >= SOF_HDA_CAPTURE_STREAMS) { + dev_err(sdev->dev, "error: too many capture streams %d\n", + num_playback); + return -EINVAL; + } + + /* mem alloc for the position buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 8, + &hdev->posbuffer); + if (ret < 0) { + dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); + return -ENOMEM; + } + + /* create capture streams */ + for (i = 0; i < num_capture; i++) { + stream = &hdev->cstream[i]; + + stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; + + stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + + SOF_HDA_PPLC_INTERVAL * i; + + /* do we support SPIB */ + if (sdev->bar[HDA_DSP_SPIB_BAR]) { + stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_SPIB; + + stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_MAXFIFO; + } + + /* do we support DRSM */ + if (sdev->bar[HDA_DSP_DRSM_BAR]) + stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; + + stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; + stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + + stream->sd_offset; + + stream->tag = i + 1; + stream->open = false; + stream->running = false; + stream->direction = SNDRV_PCM_STREAM_CAPTURE; + stream->index = i; + + /* memory alloc for stream BDL */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + HDA_DSP_BDL_SIZE, &stream->bdl); + if (ret < 0) { + dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + return -ENOMEM; + } + stream->posbuf = (__le32 *)(hdev->posbuffer.area + + (stream->index) * 8); + } + + /* create playback streams */ + for (i = num_capture; i < num_total; i++) { + stream = &hdev->pstream[i - num_capture]; + + /* we always have DSP support */ + stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; + + stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + + SOF_HDA_PPLC_INTERVAL * i; + + /* do we support SPIB */ + if (sdev->bar[HDA_DSP_SPIB_BAR]) { + stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_SPIB; + + stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_MAXFIFO; + } + + /* do we support DRSM */ + if (sdev->bar[HDA_DSP_DRSM_BAR]) + stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; + + stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; + stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + + stream->sd_offset; + stream->tag = i - num_capture + 1; + stream->open = false; + stream->running = false; + stream->direction = SNDRV_PCM_STREAM_PLAYBACK; + stream->index = i; + + /* mem alloc for stream BDL */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + HDA_DSP_BDL_SIZE, &stream->bdl); + if (ret < 0) { + dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + return -ENOMEM; + } + + stream->posbuf = (__le32 *)(hdev->posbuffer.area + + (stream->index) * 8); + } + + return 0; +} + +void hda_dsp_stream_free(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream; + int i; + + /* free position buffer */ + if (hdev->posbuffer.area) + snd_dma_free_pages(&hdev->posbuffer); + + /* free capture streams */ + for (i = 0; i < hdev->num_capture; i++) { + stream = &hdev->cstream[i]; + + /* free bdl buffer */ + if (stream->bdl.area) + snd_dma_free_pages(&stream->bdl); + } + + /* free playback streams */ + for (i = 0; i < hdev->num_playback; i++) { + stream = &hdev->pstream[i]; + + /* free bdl buffer */ + if (stream->bdl.area) + snd_dma_free_pages(&stream->bdl); + } +} + From 52111fb2ceb8cb3d9fbf6bc31051ba44a65d3b8f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:17 +0100 Subject: [PATCH 042/298] ASoC: SOF: Intel: Add Intel specific HDA trace operations Add trace operations for Intel based HDA DSPs Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-trace.c | 88 +++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 sound/soc/sof/intel/hda-trace.c diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c new file mode 100644 index 00000000000000..2b88768cb903bb --- /dev/null +++ b/sound/soc/sof/intel/hda-trace.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_stream *stream = sdev->hda->dtrace_stream; + struct snd_dma_buffer *dmab = &sdev->dmatb; + int ret; + + stream->bufsize = sdev->dmatb.bytes; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + if (ret < 0) + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + + return ret; +} + +int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *tag) +{ + sdev->hda->dtrace_stream = hda_dsp_stream_get_cstream(sdev); + + if (!sdev->hda->dtrace_stream) { + dev_err(sdev->dev, + "error: no available capture stream for DMA trace\n"); + return -ENODEV; + } + + *tag = sdev->hda->dtrace_stream->tag; + + /* + * initialize capture stream, set BDL address and return corresponding + * stream tag which will be sent to the firmware by IPC message. + */ + return hda_dsp_trace_prepare(sdev); +} + +int hda_dsp_trace_release(struct snd_sof_dev *sdev) +{ + if (sdev->hda->dtrace_stream) { + sdev->hda->dtrace_stream->open = false; + hda_dsp_stream_put_cstream(sdev, + sdev->hda->dtrace_stream->tag); + sdev->hda->dtrace_stream = NULL; + return 0; + } + + dev_dbg(sdev->dev, "DMA trace stream is not opened!\n"); + return -ENODEV; +} + +int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd) +{ + return hda_dsp_stream_trigger(sdev, sdev->hda->dtrace_stream, cmd); +} From 939d674a1b26044f5baeba725ae2940bb7e8e13e Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 3 Jul 2018 10:41:41 +0800 Subject: [PATCH 043/298] ASoC: SOF: intel: SKL, CNL, APL platform DAIs Signed-off-by: Liam Girdwood Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-dai.c | 112 ++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 sound/soc/sof/intel/hda-dai.c diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c new file mode 100644 index 00000000000000..d5067db0d9f5c6 --- /dev/null +++ b/sound/soc/sof/intel/hda-dai.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Authors: Keyon Jie + */ + +#include +#include "../sof-priv.h" +#include "hda.h" + +#define SKL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * common dai driver for skl+ platforms. + * some products who use this DAI array only physically have a subset of + * the DAIs, but no harm is done here by adding the whole set. + */ +struct snd_soc_dai_driver skl_dai[] = { +{ + .name = "SSP0 Pin", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP1 Pin", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP2 Pin", + .playback = SOF_DAI_STREAM("ssp2 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp2 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP3 Pin", + .playback = SOF_DAI_STREAM("ssp3 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp3 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP4 Pin", + .playback = SOF_DAI_STREAM("ssp4 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp4 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP5 Pin", + .playback = SOF_DAI_STREAM("ssp5 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp5 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "DMIC01 Pin", + .capture = SOF_DAI_STREAM("DMIC01 Rx", 1, 4, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "DMIC16k Pin", + .capture = SOF_DAI_STREAM("DMIC16k Rx", 1, 4, + SNDRV_PCM_RATE_16000, SKL_FORMATS), +}, +{ + .name = "iDisp1 Pin", + .playback = SOF_DAI_STREAM("iDisp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "iDisp2 Pin", + .playback = SOF_DAI_STREAM("iDisp2 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "iDisp3 Pin", + .playback = SOF_DAI_STREAM("iDisp3 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "Analog Codec DAI", + .playback = SOF_DAI_STREAM("Analog Codec Playback", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("Analog Codec Capture", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "Digital Codec DAI", + .playback = SOF_DAI_STREAM("Digital Codec Playback", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("Digital Codec Capture", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "Alt Analog Codec DAI", + .playback = SOF_DAI_STREAM("Alt Analog Codec Playback", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("Alt Analog Codec Capture", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +}; From 0cfec549334e99a4baa5816003283bee1cc4e7cc Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:18 +0100 Subject: [PATCH 044/298] ASoC: SOF: Intel: Add platform differentiation for SKL, APL and CNL Add platform differentiation operations for different Intel HDA DSP platforms. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/apl.c | 104 ++++++++++++++++ sound/soc/sof/intel/cnl.c | 248 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/skl.c | 100 +++++++++++++++ 3 files changed, 452 insertions(+) create mode 100644 sound/soc/sof/intel/apl.c create mode 100644 sound/soc/sof/intel/cnl.c create mode 100644 sound/soc/sof/intel/skl.c diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c new file mode 100644 index 00000000000000..d3816b99498584 --- /dev/null +++ b/sound/soc/sof/intel/apl.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Apollolake and GeminiLake + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +/* apollolake ops */ +struct snd_sof_dsp_ops sof_apl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = hda_dsp_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = hda_dsp_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = hda_dsp_ipc_is_ready, + .cmd_done = hda_dsp_ipc_cmd_done, + + /* debug */ + .debug_map = apl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, + + /* PM */ + .suspend = hda_dsp_suspend, + .resume = hda_dsp_resume, +}; +EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c new file mode 100644 index 00000000000000..f98e699f0d8631 --- /dev/null +++ b/sound/soc/sof/intel/cnl.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Cannonlake. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); + +static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 hipci, hipcida, hipctdr, hipctdd, msg = 0, msg_ext = 0; + irqreturn_t ret = IRQ_NONE; + + /* here we handle IPC interrupts only */ + if (!(sdev->irq_status & HDA_DSP_ADSPIS_IPC)) + return ret; + + hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + + /* reply message from DSP */ + if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) { + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDR); + msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK; + msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* mask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, 0); + + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, msg)) + cnl_ipc_cmd_done(sdev, SOF_IPC_DSP_REPLY); + + ret = IRQ_HANDLED; + } + + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); + + /* new message from DSP */ + if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { + hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDD); + msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; + msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* handle messages from DSP */ + if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == + SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + + /* + * clear busy interrupt to tell dsp controller this + * interrupt has been accepted, not trigger it again + */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDR, + CNL_DSP_REG_HIPCTDR_BUSY, + CNL_DSP_REG_HIPCTDR_BUSY); + + ret = IRQ_HANDLED; + } + + if (ret == IRQ_HANDLED) { + /* reenable IPC interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + } + + if (sdev->code_loading) { + sdev->code_loading = 0; + wake_up(&sdev->waitq); + } + + return ret; +} + +static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) +{ + if (dir == SOF_IPC_HOST_REPLY) { + /* + * set done bit to ack dsp the msg has been + * processed and send reply msg to dsp + */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDA, + CNL_DSP_REG_HIPCTDA_DONE, + CNL_DSP_REG_HIPCTDA_DONE); + } else { + /* + * set DONE bit - tell DSP we have received the reply msg + * from DSP, and processed it, don't send more reply to host + */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDA, + CNL_DSP_REG_HIPCIDA_DONE, + CNL_DSP_REG_HIPCIDA_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, + CNL_DSP_REG_HIPCCTL_DONE); + } + + return 0; +} + +static int cnl_ipc_is_ready(struct snd_sof_dev *sdev) +{ + u64 busy, done; + + busy = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR); + done = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + if ((busy & CNL_DSP_REG_HIPCIDR_BUSY) || + (done & CNL_DSP_REG_HIPCIDA_DONE)) + return 0; + + return 1; +} + +static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + u32 cmd = msg->header; + + /* send the message */ + hda_dsp_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, + cmd | CNL_DSP_REG_HIPCIDR_BUSY); + + return 0; +} + +/* cannonlake ops */ +struct snd_sof_dsp_ops sof_cnl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = cnl_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = cnl_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = cnl_ipc_is_ready, + .cmd_done = cnl_ipc_cmd_done, + + /* debug */ + .debug_map = cnl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, +}; +EXPORT_SYMBOL(sof_cnl_ops); diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c new file mode 100644 index 00000000000000..4c1ae35f371897 --- /dev/null +++ b/sound/soc/sof/intel/skl.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Skylake and Kabylake. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map skl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +/* skylake ops */ +struct snd_sof_dsp_ops sof_skl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = hda_dsp_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = hda_dsp_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = hda_dsp_ipc_is_ready, + .cmd_done = hda_dsp_ipc_cmd_done, + + /* debug */ + .debug_map = skl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(skl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, +}; +EXPORT_SYMBOL(sof_skl_ops); From db3e860fc55bf1bb6ccd0059b1e431312e188651 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:35:47 +0000 Subject: [PATCH 045/298] ASoC: SOF: Add VirtIO support Add support for running SOF on multiple VMs within the same HW. Signed-off-by: Liam Girdwood --- sound/soc/sof/virtio-be.c | 126 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/virtio-fe.c | 130 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 sound/soc/sof/virtio-be.c create mode 100644 sound/soc/sof/virtio-fe.c diff --git a/sound/soc/sof/virtio-be.c b/sound/soc/sof/virtio-be.c new file mode 100644 index 00000000000000..4c07caeba41b0c --- /dev/null +++ b/sound/soc/sof/virtio-be.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Luo Xionghu + * Liam Girdwood + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* BE driver + * + * This driver will create IO Queues for communition from FE drivers. + * The FE driver will send real IPC structures over the queue and then + * the BE driver will send the structures directlt to the DSP. The BE will + * get the IPC reply from the DSP and send it back to the FE over the queue. + * + * The virt IO message Q handlers in this file will :- + * + * 1) Check that the message is valid and not for any componenets that don't + * belong to the guest. + * + * 2) Call snd_sof_dsp_tx_msg(struct snd_sof_dev *sdev, + * struct snd_sof_ipc_msg *msg) to send the message to the DSP. + * + * Replies will be sent back using a similar method. + */ + +static int sof_virtio_validate(struct virtio_device *dev) +{ + /* do we need this func ?? */ + return 0; +} + +static int sof_virtio_probe(struct virtio_device *dev) +{ + /* register fe device with sof core */ + //snd_sof_virtio_register_fe(dev); + + /* create our virtqueues */s + + /* send topology data to fe via virtq */ + + return 0; +} + +static void sof_virtio_remove(struct virtio_device *dev) +{ + /* remove topology from fe via virtqueue */ + + /* destroy virtqueue */ +} + +#ifdef CONFIG_PM +static int sof_virtio_freeze(struct virtio_device *dev) +{ + /* pause and suspend any streams for this FE */ + return 0; +} + +static int sof_virtio_restore(struct virtio_device *dev) +{ + /* restore and unpause any streams for this FE */ + return 0; +} +#endif + +/* IDs of FEs */ +static const struct virtio_device_id *fe_id_table[] + { +}; + +static struct virtio_driver sof_be_virtio_driver = { + .driver = { + .name = "sof-virtio-be", + .owner = THIS_MODULE, + }, + + .id_table = fe_id_table, + + //const unsigned int *feature_table; + //unsigned int feature_table_size; + //const unsigned int *feature_table_legacy; + //unsigned int feature_table_size_legacy; + + validate = sof_virtio_validate, + probe = sof_virtio_probe, + remove = sof_virtio_remove, + +#ifdef CONFIG_PM + freeze = sof_virtio_freeze, + restore = sof_virtio_restore, +#endif +}; + +/* this will be called by sof core when core is ready */ +int sof_virtio_register(struct snd_sof_dev *sdev) +{ + int ret; + + ret = register_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ + return ret; +} + +/* called by sof core when driver is removed */ +void sof_virtio_unregister(struct snd_sof_dev *sdev) +{ + unregister_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ +} diff --git a/sound/soc/sof/virtio-fe.c b/sound/soc/sof/virtio-fe.c new file mode 100644 index 00000000000000..1019e65876b923 --- /dev/null +++ b/sound/soc/sof/virtio-fe.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Luo Xionghu + * Liam Girdwood + */ + +/* + * virt IO FE driver + * + * The SOF driver thinks this driver is another audio DSP, however the calls + * made by the SOF driver core do not directly go to HW, but over a virtIO + * message Q to the virtIO BE driver. + * + * The virtIO message Q will use the *exact* same IPC structures as we currently + * use in the mailbox. + * + * Guest OS SOF core -> SOF FE -> virtIO Q -> SOF BE -> + * System OS SOF core -> DSP + * + * The mailbox IO and TX/RX msg functions below will do IO on the virt IO Q. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sof-priv.h" +#include "ops.h" +#include "intel.h" + +/* + * IPC Firmware ready. + */ +static int virtio_fe_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + /* not needed for FE ? */ + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void virtio_fe_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + /* write data to message Q buffer before sending message */ +} + +static void virtio_fe_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + /* read data from message Q buffer after receiving message */ +} + +static int virtio_fe_tx_busy(struct snd_sof_dev *sdev) +{ + /* return 1 if tx message Q is busy */ +} + +static int virtio_fe_tx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + /* write msg to the virtio queue message for BE */ + + return 0; +} + +static int virtio_fe_rx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + /* read the virtio queue message from BE and copy to msg */ + return 0; +} + +/* + * Probe and remove. + */ + +static int virtio_fe_probe(struct snd_sof_dev *sdev) +{ + /* register virtio device */ + + /* conenct virt queues to BE */ +} + +static int virtio_fe_remove(struct snd_sof_dev *sdev) +{ + /* free virtio resurces and unregister device */ +} + +/* baytrail ops */ +struct snd_sof_dsp_ops snd_sof_virtio_fe_ops = { + /* device init */ + .probe = virtio_fe_probe, + .remove = virtio_fe_remove, + + /* mailbox */ + .mailbox_read = virtio_fe_mailbox_read, + .mailbox_write = virtio_fe_mailbox_write, + + /* ipc */ + .tx_msg = virtio_fe_tx_msg, + .rx_msg = virtio_fe_rx_msg, + .fw_ready = virtio_fe_fw_ready, + .tx_busy = virtio_fe_tx_busy, + + /* module loading */ +// .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(snd_sof_virtio_fe_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From 89f6c1a4d58ad246dc766f4d3e03f366aa47d522 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:46 +0000 Subject: [PATCH 046/298] ASoC: SOF: Add SPI device support Add support for SPI based DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/hw-spi.c | 250 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-spi-dev.c | 167 ++++++++++++++++++++++++ 2 files changed, 417 insertions(+) create mode 100644 sound/soc/sof/hw-spi.c create mode 100644 sound/soc/sof/sof-spi-dev.c diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c new file mode 100644 index 00000000000000..e20682da09202c --- /dev/null +++ b/sound/soc/sof/hw-spi.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardware interface for audio DSPs via SPI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sof-priv.h" +#include "ops.h" +#include "intel.h" + +/* + * Memory copy. + */ + +static void spi_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + // use spi_write() to copy data to DSP +} + +static void spi_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + // use spi_read() to copy data from DSP +} + +/* + * IPC Firmware ready. + */ +static int spi_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x\n", msg_id); + + // read local buffer with SPI data + + dev_info(sdev->dev, " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void spi_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + //memcpy_toio(dest, message, bytes); + /* + * this will copy to a local memory buffer that will be sent to DSP via + * SPI at next IPC + */ +} + +static void spi_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + //memcpy_fromio(message, src, bytes); + /* + * this will copy from a local memory buffer that will be received from + * DSP via SPI at last IPC + */ +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t spi_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + int ret = IRQ_NONE; + + // on SPI based devices this will likely come via a SoC GPIO IRQ + + // check if GPIO is assetred and if so run thread. + + return ret; +} + +static irqreturn_t spi_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + + // read SPI data into local buffer and determine IPC cmd or reply + + /* + * if reply. Handle Immediate reply from DSP Core and set DSP + * state to ready + */ + //snd_sof_ipc_reply(sdev, ipcx); + + /* if cmd, Handle messages from DSP Core */ + //snd_sof_ipc_msgs_rx(sdev); + + return IRQ_HANDLED; +} + +static int spi_is_ready(struct snd_sof_dev *sdev) +{ + // use local variable to store DSP command state. either DSP is ready + // for new cmd or still processing current cmd. + + return 1; +} + +static int spi_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u64 cmd = msg->header; + + /* send the message */ + spi_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + + return 0; +} + +static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + spi_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%lx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + spi_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +/* + * Probe and remove. + */ + +static int spi_sof_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + int ret = 0; + + /* get IRQ from Device tree or ACPI - register our IRQ */ + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, spi_irq_handler, + spi_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + return ret; +} + +static int spi_sof_remove(struct snd_sof_dev *sdev) +{ + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +/* baytrail ops */ +struct snd_sof_dsp_ops snd_sof_spi_ops = { + /* device init */ + .probe = spi_sof_probe, + .remove = spi_sof_remove, + + /* Block IO */ + .block_read = spi_block_read, + .block_write = spi_block_write, + + /* doorbell */ + .irq_handler = spi_irq_handler, + .irq_thread = spi_irq_thread, + + /* mailbox */ + .mailbox_read = spi_mailbox_read, + .mailbox_write = spi_mailbox_write, + + /* ipc */ + .send_msg = spi_send_msg, + .get_reply = spi_get_reply, + .fw_ready = spi_fw_ready, + .is_ready = spi_is_ready, + .cmd_done = spi_cmd_done, + + /* debug */ + .debug_map = spi_debugfs, + .debug_map_count = ARRAY_SIZE(spi_debugfs), + .dbg_dump = spi_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(snd_sof_spi_ops); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c new file mode 100644 index 00000000000000..fb94f24b824076 --- /dev/null +++ b/sound/soc/sof/sof-spi-dev.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +static void sof_spi_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_platform_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_sof_machine *mach = sof_pdata->machine; + struct device *dev = sof_pdata->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static int sof_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct snd_sof_machine *mach; + struct snd_sof_machine *m; + struct snd_sof_pdata *sof_pdata; + struct sof_platform_priv *priv; + int ret = 0; + + dev_dbg(&spi->dev, "SPI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + spi_set_drvdata(spi, priv); + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + /* use nocodec machine atm */ + dev_err(dev, "No matching ASoC machine driver found - using nocodec\n"); + sof_pdata->drv_name = "sof-nocodec"; + m = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + if (!m) + return -ENOMEM; + + m->drv_name = "sof-nocodec"; + m->sof_fw_filename = desc->nocodec_fw_filename; + m->sof_tplg_filename = desc->nocodec_tplg_filename; + m->ops = desc->machines[0].ops; + m->asoc_plat_name = "sof-platform"; + mach = m; + + sof_pdata->id = pci_id->device; + sof_pdata->name = spi_name(spi); + sof_pdata->machine = mach; + sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + priv->sof_pdata = sof_pdata; + sof_pdata->dev = dev; + sof_pdata->type = SOF_DEVICE_SPI; + + /* register machine driver */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue probing after firmware is loaded */ + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_spi_fw_cb); + if (ret) + platform_device_unregister(sof_pdata->pdev_mach); + + /* allow runtime_pm */ + pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_allow(dev); + + return ret; +} + +static int sof_spi_remove(struct spi_device *spi) +{ + struct sof_platform_priv *priv = spi_get_drvdata(spi); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); +} + +static struct spi_driver wm8731_spi_driver = { + .driver = { + .name = "sof-spi-dev", + .of_match_table = sof_of_match, + }, + .probe = sof_spi_probe, + .remove = sof_spi_remove, +}; + +static const struct snd_sof_machine sof_spi_machines[] = { + { "INT343A", "bxt_alc298s_i2s", "intel/sof-spi.ri", + "intel/sof-spi.tplg", "0000:00:0e.0", &snd_sof_spi_ops }, +}; + +static const struct sof_dev_desc spi_desc = { + .machines = sof_spi_machines, + .nocodec_fw_filename = "intel/sof-spi.ri", + .nocodec_tplg_filename = "intel/sof-spi.tplg" +}; + +static int __init sof_spi_modinit(void) +{ + int ret; + + ret = spi_register_driver(&sof_spi_driver); + if (ret != 0) + pr_err("Failed to register SOF SPI driver: %d\n", ret); + + return ret; +} +module_init(sof_spi_modinit); + +static void __exit sof_spi_modexit(void) +{ + spi_unregister_driver(&sof_spi_driver); +} +module_exit(sof_spi_modexit); + +MODULE_LICENSE("Dual BSD/GPL"); From 6097f86c207b2bd81acf209ca7c243f172e2eb26 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:02 +0000 Subject: [PATCH 047/298] ASoC: SOF: Add ACPI device support Add support ACPI based SOF DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-acpi-dev.c | 358 +++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 sound/soc/sof/sof-acpi-dev.c diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c new file mode 100644 index 00000000000000..d635e6bda64bb3 --- /dev/null +++ b/sound/soc/sof/sof-acpi-dev.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* platform specific devices */ +#include "intel/shim.h" +#include "intel/hda.h" + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) +static struct sof_dev_desc sof_acpi_haswell_desc = { + .machines = snd_soc_acpi_intel_haswell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = -1, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-hsw.ri", + .nocodec_tplg_filename = "intel/sof-hsw-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) +static struct sof_dev_desc sof_acpi_broadwell_desc = { + .machines = snd_soc_acpi_intel_broadwell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = -1, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-bdw.ri", + .nocodec_tplg_filename = "intel/sof-bdw-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + +/* BYTCR uses different IRQ index */ +static struct sof_dev_desc sof_acpi_baytrailcr_desc = { + .machines = snd_soc_acpi_intel_baytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt-nocodec.tplg" +}; + +static struct sof_dev_desc sof_acpi_baytrail_desc = { + .machines = snd_soc_acpi_intel_baytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 5, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt-nocodec.tplg" +}; + +static int is_byt_cr(struct device *dev) +{ + u32 bios_status; + int status; + + if (!IS_ENABLED(CONFIG_IOSF_MBI) || !iosf_mbi_available()) { + dev_info(dev, "IOSF_MBI not enabled - can't determine CPU variant\n"); + return -EIO; + } + + status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */ + MBI_REG_READ, /* 0x10 */ + 0x006, /* BIOS_CONFIG */ + &bios_status); + + if (status) { + dev_err(dev, "error: could not read PUNIT BIOS_CONFIG\n"); + return -EIO; + } + + /* bits 26:27 mirror PMIC options */ + bios_status = (bios_status >> 26) & 3; + + if (bios_status == 1 || bios_status == 3) { + dev_info(dev, "BYT-CR detected\n"); + return 1; + } + + dev_info(dev, "BYT-CR not detected\n"); + return 0; +} + +static struct sof_dev_desc sof_acpi_cherrytrail_desc = { + .machines = snd_soc_acpi_intel_cherrytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 5, + .nocodec_fw_filename = "intel/sof-cht.ri", + .nocodec_tplg_filename = "intel/sof-cht-nocodec.tplg" +}; +#endif + +static struct platform_device * + mfld_new_mach_data(struct snd_sof_pdata *sof_pdata) +{ + struct snd_soc_acpi_mach pmach; + struct device *dev = sof_pdata->dev; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct platform_device *pdev = NULL; + + memset(&pmach, 0, sizeof(pmach)); + memcpy((void *)pmach.id, mach->id, ACPI_ID_LEN); + pmach.drv_name = mach->drv_name; + + pdev = platform_device_register_data(dev, mach->drv_name, -1, + &pmach, sizeof(pmach)); + return pdev; +} + +static void sof_acpi_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_platform_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct device *dev = sof_pdata->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_acpi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static const struct sof_ops_table mach_ops[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) + {&sof_acpi_haswell_desc, &sof_hsw_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + {&sof_acpi_broadwell_desc, &sof_bdw_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + {&sof_acpi_baytrail_desc, &sof_byt_ops, mfld_new_mach_data}, + {&sof_acpi_baytrailcr_desc, &sof_byt_ops, mfld_new_mach_data}, + {&sof_acpi_cherrytrail_desc, &sof_cht_ops, mfld_new_mach_data}, +#endif +}; + +static struct snd_sof_dsp_ops * + sof_acpi_get_ops(const struct sof_dev_desc *d, + struct platform_device *(**new_mach_data) + (struct snd_sof_pdata *)) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mach_ops); i++) { + if (d == mach_ops[i].desc) { + *new_mach_data = mach_ops[i].new_data; + return mach_ops[i].ops; + } + } + + /* not found */ + return NULL; +} + +static int sof_acpi_probe(struct platform_device *pdev) +{ + const struct acpi_device_id *id; + struct device *dev = &pdev->dev; + const struct sof_dev_desc *desc; + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *sof_pdata; + struct sof_platform_priv *priv; + struct snd_sof_dsp_ops *ops; + struct platform_device *(*new_mach_data)(struct snd_sof_pdata *pdata); + int ret = 0; + + dev_dbg(&pdev->dev, "ACPI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + desc = (const struct sof_dev_desc *)id->driver_data; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + if (desc == &sof_acpi_baytrail_desc && is_byt_cr(dev)) + desc = &sof_acpi_baytrailcr_desc; +#endif + + /* get ops for platform */ + new_mach_data = NULL; + ops = sof_acpi_get_ops(desc, &new_mach_data); + if (!ops) { + dev_err(dev, "error: no matching ACPI descriptor ops\n"); + return -ENODEV; + } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + /* force nocodec mode */ + dev_warn(dev, "Force to use nocodec mode\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); + if (ret < 0) + return ret; +#else + /* find machine */ + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) + /* fallback to nocodec mode */ + dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); + if (ret < 0) + return ret; +#else + dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; +#endif + } +#endif + + mach->pdata = ops; + mach->new_mach_data = (struct platform_device * + (*)(void *pdata)) new_mach_data; + + sof_pdata->machine = mach; + /* + * FIXME, this can't work for baytrail cr: + * sof_pdata->desc = (struct sof_dev_desc*) id->driver_data; + */ + sof_pdata->desc = desc; + priv->sof_pdata = sof_pdata; + sof_pdata->dev = &pdev->dev; + sof_pdata->type = SOF_DEVICE_APCI; + dev_set_drvdata(&pdev->dev, priv); + + /* do we need to generate any machine plat data ? */ + if (mach->new_mach_data) + sof_pdata->pdev_mach = mach->new_mach_data(sof_pdata); + else + /* register machine driver, pass machine info as pdata */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + (const void *)mach, + sizeof(*mach)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue SST probing after firmware is loaded */ + dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_acpi_fw_cb); + if (ret) { + platform_device_unregister(sof_pdata->pdev_mach); + dev_err(dev, "error: failed to load firmware %s\n", + mach->sof_fw_filename); + } + + /* allow runtime_pm */ + pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_allow(dev); + + return ret; +} + +static void sof_acpi_shutdown(struct platform_device *pdev) +{ + snd_sof_shutdown(&pdev->dev); +} + +static int sof_acpi_remove(struct platform_device *pdev) +{ + struct sof_platform_priv *priv = dev_get_drvdata(&pdev->dev); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); + + return 0; +} + +static const struct acpi_device_id sof_acpi_match[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) + { "INT33C8", (unsigned long)&sof_acpi_haswell_desc }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + { "INT3438", (unsigned long)&sof_acpi_broadwell_desc }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + { "80860F28", (unsigned long)&sof_acpi_baytrail_desc }, + { "808622A8", (unsigned long)&sof_acpi_cherrytrail_desc }, +#endif + { } +}; +MODULE_DEVICE_TABLE(acpi, sof_acpi_match); + +/* acpi_driver definition */ +static struct platform_driver snd_sof_acpi_driver = { + .probe = sof_acpi_probe, + .remove = sof_acpi_remove, + .shutdown = sof_acpi_shutdown, + .driver = { + .name = "sof-audio-acpi", + .pm = &sof_acpi_pm, + .acpi_match_table = ACPI_PTR(sof_acpi_match), + }, +}; +module_platform_driver(snd_sof_acpi_driver); + +MODULE_LICENSE("Dual BSD/GPL"); From d1580aa1803467458e3874f1e2924d6252e1cf57 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:19 +0000 Subject: [PATCH 048/298] ASoC: SOF: Add PCI device support Add support for PCI based DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-pci-dev.c | 387 ++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 sound/soc/sof/sof-pci-dev.c diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c new file mode 100644 index 00000000000000..c8bf16f75b8416 --- /dev/null +++ b/sound/soc/sof/sof-pci-dev.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* platform specific devices */ +#include "intel/shim.h" +#include "intel/hda.h" + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) +static struct sof_dev_desc bxt_desc = { + .machines = snd_soc_acpi_intel_bxt_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-apl.ri", + .nocodec_tplg_filename = "intel/sof-apl-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) +static struct sof_dev_desc glk_desc = { + .machines = snd_soc_acpi_intel_glk_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-glk.ri", + .nocodec_tplg_filename = "intel/sof-glk-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) +static struct snd_soc_acpi_mach sof_byt_machines[] = { + { + .id = "INT343A", + .drv_name = "edison", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt.tplg", + .asoc_plat_name = "baytrail-pcm-audio", + }, + {} +}; + +static const struct sof_dev_desc byt_desc = { + .machines = sof_byt_machines, + .resindex_lpe_base = 3, /* IRAM, but subtract IRAM offset */ + .resindex_pcicfg_base = -1, + .resindex_imr_base = 0, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) +static const struct sof_dev_desc cnl_desc = { + .machines = snd_soc_acpi_intel_cnl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-cnl.ri", + .nocodec_tplg_filename = "intel/sof-cnl.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) +static struct sof_dev_desc skl_desc = { + .machines = snd_soc_acpi_intel_skl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-skl.ri", + .nocodec_tplg_filename = "intel/sof-skl-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) +static struct sof_dev_desc kbl_desc = { + .machines = snd_soc_acpi_intel_kbl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-kbl.ri", + .nocodec_tplg_filename = "intel/sof-kbl-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) +static const struct sof_dev_desc icl_desc = { + .machines = snd_soc_acpi_intel_icl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-icl.ri", + .nocodec_tplg_filename = "intel/sof-icl-nocodec.tplg" +}; +#endif + +static void sof_pci_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_platform_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct device *dev = sof_pdata->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_pci_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static const struct sof_ops_table mach_ops[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + {&bxt_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) + {&glk_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + {&byt_desc, &sof_byt_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + {&cnl_desc, &sof_cnl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) + {&skl_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) + {&kbl_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) + {&icl_desc, &sof_cnl_ops}, +#endif +}; + +static struct snd_sof_dsp_ops *sof_pci_get_ops(const struct sof_dev_desc *d) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mach_ops); i++) { + if (d == mach_ops[i].desc) + return mach_ops[i].ops; + } + + /* not found */ + return NULL; +} + +static int sof_pci_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct device *dev = &pci->dev; + const struct sof_dev_desc *desc = + (const struct sof_dev_desc *)pci_id->driver_data; + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *sof_pdata; + struct sof_platform_priv *priv; + struct snd_sof_dsp_ops *ops; + int ret = 0; + + dev_dbg(&pci->dev, "PCI DSP detected"); + + /* get ops for platform */ + ops = sof_pci_get_ops(desc); + if (!ops) { + dev_err(dev, "error: no matching PCI descriptor ops\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pci_set_drvdata(pci, priv); + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + ret = pci_enable_device(pci); + if (ret < 0) + return ret; + + ret = pci_request_regions(pci, "Audio DSP"); + if (ret < 0) + goto disable_dev; + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + /* force nocodec mode */ + dev_warn(dev, "Force to use nocodec mode\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); + if (ret < 0) + goto release_regions; +#else + /* find machine */ + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) + /* fallback to nocodec mode */ + dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); + if (ret < 0) + goto release_regions; +#else + dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); + ret = -ENODEV; + goto release_regions; +#endif + } +#endif + + mach->pdata = ops; + + sof_pdata->id = pci_id->device; + sof_pdata->name = pci_name(pci); + sof_pdata->machine = mach; + sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + priv->sof_pdata = sof_pdata; + sof_pdata->dev = &pci->dev; + sof_pdata->type = SOF_DEVICE_PCI; + + /* register machine driver */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(sof_pdata->pdev_mach)) { + ret = PTR_ERR(sof_pdata->pdev_mach); + goto release_regions; + } + + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue probing after firmware is loaded */ + dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_pci_fw_cb); + if (ret) { + platform_device_unregister(sof_pdata->pdev_mach); + dev_err(dev, "error: failed to load firmware %s\n", + mach->sof_fw_filename); + goto release_regions; + } + + /* allow runtime_pm */ + pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_allow(dev); + + return ret; + +release_regions: + pci_release_regions(pci); +disable_dev: + pci_disable_device(pci); + + return ret; +} + +static void sof_pci_shutdown(struct pci_dev *pci) +{ + snd_sof_shutdown(&pci->dev); +} + +static void sof_pci_remove(struct pci_dev *pci) +{ + struct sof_platform_priv *priv = pci_get_drvdata(pci); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + /* unregister machine driver */ + platform_device_unregister(sof_pdata->pdev_mach); + + /* unregister sof-audio platform driver */ + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + + /* release firmware */ + release_firmware(sof_pdata->fw); + + /* release pci regions and disable device */ + pci_release_regions(pci); + pci_disable_device(pci); +} + +/* PCI IDs */ +static const struct pci_device_id sof_pci_ids[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + /* BXT-P & Apollolake */ + { PCI_DEVICE(0x8086, 0x5a98), + .driver_data = (unsigned long)&bxt_desc}, + { PCI_DEVICE(0x8086, 0x1a98), + .driver_data = (unsigned long)&bxt_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) + { PCI_DEVICE(0x8086, 0x3198), + .driver_data = (unsigned long)&glk_desc}, +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + { PCI_DEVICE(0x8086, 0x119a), + .driver_data = (unsigned long)&byt_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + { PCI_DEVICE(0x8086, 0x9dc8), + .driver_data = (unsigned long)&cnl_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) + { PCI_DEVICE(0x8086, 0x9d71), + .driver_data = (unsigned long)&kbl_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) + { PCI_DEVICE(0x8086, 0x9d70), + .driver_data = (unsigned long)&skl_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) + { PCI_DEVICE(0x8086, 0x34C8), + .driver_data = (unsigned long)&icl_desc}, +#endif + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sof_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_driver = { + .name = "sof-audio-pci", + .id_table = sof_pci_ids, + .probe = sof_pci_probe, + .remove = sof_pci_remove, + .shutdown = sof_pci_shutdown, + .driver = { + .pm = &sof_pci_pm, + }, +}; +module_pci_driver(snd_sof_pci_driver); + +MODULE_LICENSE("Dual BSD/GPL"); From add9e403933a60317b64ee82c00dc5b31bf62860 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Jul 2018 14:45:17 -0500 Subject: [PATCH 049/298] ASoC: SOF: add utils Signed-off-by: Keyon Jie Signed-off-by: Liam Girdwood --- sound/soc/sof/utils.c | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 sound/soc/sof/utils.c diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c new file mode 100644 index 00000000000000..91c8417500af8c --- /dev/null +++ b/sound/soc/sof/utils.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Keyon Jie + */ + +#include +#include +#include +#include "sof-priv.h" + +int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, + struct snd_soc_dai_link *links, int link_num, + struct snd_soc_card *card) +{ + char name[32]; + int i; + + if (!ops || !links || !card) + return -EINVAL; + + /* set up BE dai_links */ + for (i = 0; i < link_num; i++) { + snprintf(name, 32, "NoCodec-%d", i); + links[i].name = devm_kstrdup(dev, name, GFP_KERNEL); + if (!links[i].name) + return -ENOMEM; + + links[i].id = i; + links[i].no_pcm = 1; + links[i].cpu_dai_name = ops->drv[i].name; + links[i].platform_name = "sof-audio"; + links[i].codec_dai_name = "snd-soc-dummy-dai"; + links[i].codec_name = "snd-soc-dummy"; + links[i].dpcm_playback = 1; + links[i].dpcm_capture = 1; + } + + card->dai_link = links; + card->num_links = link_num; + + return 0; +} +EXPORT_SYMBOL(sof_bes_setup); + From eaaef4514414426ddf620512ba6ca109bdd8b680 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:40:02 +0000 Subject: [PATCH 050/298] ASoC: SOF: Add Build support for SOF core and Intel drivers Build SOF core and Intel-specific drivers. Signed-off-by: Liam Girdwood --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/sof/Kconfig | 69 +++++++++++++++++++++++ sound/soc/sof/Makefile | 21 +++++++ sound/soc/sof/intel/Kconfig | 104 +++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/Makefile | 13 +++++ 6 files changed, 209 insertions(+) create mode 100644 sound/soc/sof/Kconfig create mode 100644 sound/soc/sof/Makefile create mode 100644 sound/soc/sof/intel/Kconfig create mode 100644 sound/soc/sof/intel/Makefile diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 41af6b9cc3500b..2c0e6e5c7d7eb8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -64,6 +64,7 @@ source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" +source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 06389a5385d71a..06ecbabefe0384 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ +obj-$(CONFIG_SND_SOC) += sof/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig new file mode 100644 index 00000000000000..0036f1c85ba72c --- /dev/null +++ b/sound/soc/sof/Kconfig @@ -0,0 +1,69 @@ +config SND_SOC_SOF_PCI + tristate + +config SND_SOC_SOF_ACPI + tristate + +config SND_SOC_SOF + tristate "Sound Open Firmware Support" + select SND_SOC_TOPOLOGY + select SND_SOC_COMPRESS + help + This adds support for Sound Open Firmware (SOF). SOF is a free and + generic open source audio DSP firmware for multiple devices. + Say Y if you have such a device that is supported by SOF. + If unsure select "N". + +config SND_SOC_SOF_NOCODEC + tristate "SOF nocodec mode Support" + depends on SND_SOC_SOF + help + This adds support for a dummy/nocodec machine driver fallback + option if no known codec is detected. This is typically only + enabled for developers or devices where the sound card is + controlled externally + Say Y if you need this nocodec fallback option + If unsure select "N". + +config SND_SOC_SOF_DEBUG + bool "SOF debugging features" + depends on SND_SOC_SOF + help + This option can be used to enable or disable individual SOF firmware + and driver debugging options. + Say Y if you are debugging SOF FW or drivers. + If unsure select "N". + +config SND_SOC_SOF_FORCE_NOCODEC_MODE + bool "SOF force nocodec Mode" + depends on SND_SOC_SOF_NOCODEC + depends on SND_SOC_SOF_DEBUG + help + This forces SOF to use dummy/nocodec as machine driver, even + though there is a codec detected on the real platform. This is + typically only enabled for developers for debug purposes, before + codec/machine driver is ready, or to exclude the impact of those + drivers + Say Y if you need this force nocodec mode option + If unsure select "N". + +config SND_SOC_SOF_DEBUG_XRUN_STOP + bool "SOF stop on XRUN" + depends on SND_SOC_SOF_DEBUG + help + This option forces PCMs to stop on any XRUN event. This is useful to + preserve any trace data ond pipeline status prior to the XRUN. + Say Y if you are debugging SOF FW pipeline XRUNs. + If unsure select "N". + +config SND_SOC_SOF_DEBUG_VERBOSE_IPC + bool "SOF verbose IPC logs" + depends on SND_SOC_SOF_DEBUG + help + This option enables more verbose IPC logs, with command types in + human-readable form instead of just 32-bit hex dumps. This is useful + if you are trying to debug IPC with the DSP firmware. + If unsure select "N". + +source "sound/soc/sof/intel/Kconfig" +source "sound/soc/sof/xtensa/Kconfig" diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile new file mode 100644 index 00000000000000..e2250db8baeba6 --- /dev/null +++ b/sound/soc/sof/Makefile @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ + control.o trace.o compressed.o utils.o +snd-sof-spi-objs := hw-spi.o + +snd-sof-pci-objs := sof-pci-dev.o +snd-sof-acpi-objs := sof-acpi-dev.o +snd-sof-nocodec-objs := nocodec.o + +obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o +obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o + +obj-$(CONFIG_SND_SOC_SOF_ACPI) += sof-acpi-dev.o +obj-$(CONFIG_SND_SOC_SOF_PCI) += sof-pci-dev.o +obj-$(CONFIG_SND_SOC_SOF_SPI) += sof-spi-dev.o + +obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o + +obj-$(CONFIG_SND_SOC_SOF_INTEL) += intel/ +obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig new file mode 100644 index 00000000000000..85750054a5c90c --- /dev/null +++ b/sound/soc/sof/intel/Kconfig @@ -0,0 +1,104 @@ +config SND_SOC_SOF_INTEL + tristate "SOF support for Intel audio DSPs" + depends on SND_SOC_SOF + depends on SND_DMA_SGBUF + select SND_SOC_INTEL_MACH + select SND_SOC_SOF_XTENSA + help + This adds support for Sound Open Firmware for Intel(R) platforms. + Say Y if you have such a device. + If unsure select "N". + +if SND_SOC_SOF_INTEL + +config SND_SOC_SOF_BAYTRAIL + tristate "SOF support for Baytrail, Braswell and Cherrytrail" + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Baytrail, Braswell or Cherrytrail processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_HASWELL + tristate "SOF support for Haswell" + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Haswell processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_BROADWELL + tristate "SOF support for Broadwell" + depends on SND_SOC_SOF_INTEL + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Broadwell processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_APOLLOLAKE + tristate "SOF support for Apollolake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Apollolake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_GEMINILAKE + tristate "SOF support for GeminiLake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Geminilake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_CANNONLAKE + tristate "SOF support for Cannonlake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Cannonlake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_KABYLAKE + tristate "SOF support for Kabylake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Kabylake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_SKYLAKE + tristate "SOF support for Skylake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Skylake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_ICELAKE + tristate "SOF support for Icelake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Icelake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_HDA_COMMON + tristate + select SND_SOC_SOF_PCI + select SND_SOC_ACPI_INTEL_MATCH + +endif ## SND_SOC_SOF_INTEL diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile new file mode 100644 index 00000000000000..76b5ed0580ab43 --- /dev/null +++ b/sound/soc/sof/intel/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +snd-sof-intel-byt-objs := byt.o +snd-sof-intel-hsw-objs := hsw.o +snd-sof-intel-bdw-objs := bdw.o +snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ + hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o hda-dai.o\ + skl.o apl.o cnl.o + +obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-intel-byt.o +obj-$(CONFIG_SND_SOC_SOF_HASWELL) += snd-sof-intel-hsw.o +obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-intel-bdw.o +obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o From 405e42ae1fe9e9a83e866684baf352f8ad1fdd01 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 31 May 2018 17:35:22 -0500 Subject: [PATCH 051/298] ASoC: Intel: Kconfig: disable SST and legacy drivers when SOF is selected Make sure distros don't run into issues with multiple incompatible drivers selected for the same hardware. Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 0caa1f4eb94d7d..08f00058700023 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n + depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n && SND_SOC_SOF_BAYTRAIL=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -91,7 +91,7 @@ config SND_SST_ATOM_HIFI2_PLATFORM_PCI config SND_SST_ATOM_HIFI2_PLATFORM_ACPI tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms" default ACPI - depends on X86 && ACPI + depends on X86 && ACPI && SND_SOC_SOF_BAYTRAIL=n select SND_SST_IPC_ACPI select SND_SST_ATOM_HIFI2_PLATFORM select SND_SOC_ACPI_INTEL_MATCH From f4610dc50833cf7164d797e652d86536f742668b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 8 Mar 2018 18:36:30 -0600 Subject: [PATCH 052/298] ASoC: Intel: make bytcht_da7213 with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bytcht_da7213.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 2179dedb28ad6d..1e90a9c4b1e892 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -56,6 +56,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"MIC1", NULL, "Headset Mic"}, {"MIC2", NULL, "Mic"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* SOC-codec link */ {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, @@ -64,6 +65,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Playback", NULL, "ssp2 Tx"}, {"ssp2 Rx", NULL, "Capture"}, +#endif }; static int codec_fixup(struct snd_soc_pcm_runtime *rtd, From c1ace86b39aa6be8f73e9abc185f54c1971c17ff Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 8 Mar 2018 18:39:33 -0600 Subject: [PATCH 053/298] ASoC: Intel: make cht_bsw_max98090 work with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_max98090_ti.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index db6976f4ddaa28..3d883289cb72de 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -88,12 +88,14 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Headphone", NULL, "HPR"}, {"Ext Spk", NULL, "SPKL"}, {"Ext Spk", NULL, "SPKR"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"HiFi Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "HiFi Capture"}, +#endif {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, From 1a44448dc9096f5e7fafdb5e4920a23671a74b8a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 10 Nov 2017 14:34:32 -0600 Subject: [PATCH 054/298] ASoC: Intel: cht-bsw-rt5645: work with SOF Disable hard-coded routes when SOF is selected TODO: bytcr is not yet supported Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_rt5645.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index f5a5ea6a093c71..710a5a11215b52 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -160,35 +160,43 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = { }; static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF2 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "AIF2 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp0 Tx"}, {"ssp0 Tx", NULL, "modem_out"}, {"modem_in", NULL, "ssp0 Rx" }, {"ssp0 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF2 Playback", NULL, "ssp0 Tx"}, {"ssp0 Tx", NULL, "modem_out"}, {"modem_in", NULL, "ssp0 Rx" }, {"ssp0 Rx", NULL, "AIF2 Capture"}, +#endif }; static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -585,11 +593,13 @@ static int snd_cht_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* FIXME: bytcr not supported yet */ struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; if (res_info->acpi_ipc_irq_index == 0) is_bytcr = true; +#endif } if (is_bytcr) { From 151cc1a91aa6d38685f086db36b9c61ad7460255 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 29 Nov 2017 19:14:19 -0600 Subject: [PATCH 055/298] ASoC: Intel: cht-bsw-rt5672: work with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_rt5672.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index e5aa13058dd738..3a2754676f3f1c 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -127,12 +127,14 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Ext Spk", NULL, "SPOLN"}, {"Ext Spk", NULL, "SPORP"}, {"Ext Spk", NULL, "SPORN"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx"}, {"codec_in1", NULL, "ssp2 Rx"}, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, From 146c9574ce1a0a98fa7646611c55481423fff52f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 20 Dec 2017 18:36:49 -0600 Subject: [PATCH 056/298] ASoC: Intel: make bytcr_rt5640 work with SOF Disable hard-coded routes when SOF is enabled TODO: bytcr is not yet supported Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bytcr_rt5640.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 33065ba294a93b..9d5f5f2b8037f3 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -740,6 +740,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) { ret = snd_soc_dapm_add_routes(&card->dapm, byt_rt5640_ssp2_aif2_map, @@ -759,6 +760,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } if (ret) return ret; +#endif if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) { ret = snd_soc_dapm_add_routes(&card->dapm, @@ -1073,11 +1075,13 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* FIXME: bytcr not supported yet */ struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; if (res_info->acpi_ipc_irq_index == 0) is_bytcr = true; +#endif } if (is_bytcr) { From 3350847b03e7887cf160d44c91fd117c53e3e95f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 7 Dec 2017 21:48:51 +0000 Subject: [PATCH 057/298] ASoC: Intel: Make sure HSW/BDW based machine drivers build for SOF HSW/BDW use hard coded IPC calls to set SSP, not needed in SOF as SSP is configured via topology. Signed-off-by: Liam Girdwood --- sound/soc/intel/boards/bdw-rt5677.c | 4 ++++ sound/soc/intel/boards/broadwell.c | 4 ++++ sound/soc/intel/boards/haswell.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 6ea360f33575ae..f287f192929ca5 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -181,6 +181,7 @@ static const struct snd_soc_ops bdw_rt5677_ops = { .hw_params = bdw_rt5677_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -199,6 +200,7 @@ static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) { @@ -266,7 +268,9 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = bdw_rt5677_rtd_init, +#endif .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 7b0ee67b4fc8b0..776a1fa7a1e1d6 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -130,6 +130,7 @@ static const struct snd_soc_ops broadwell_rt286_ops = { .hw_params = broadwell_rt286_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -148,6 +149,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif /* broadwell digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broadwell_rt286_dais[] = { @@ -160,7 +162,9 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = broadwell_rtd_init, +#endif .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .dpcm_capture = 1, diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index eab1f439dd3f1a..ce7d7f6422bfb8 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -85,6 +85,7 @@ static const struct snd_soc_ops haswell_rt5640_ops = { .hw_params = haswell_rt5640_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -103,6 +104,7 @@ static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif static struct snd_soc_dai_link haswell_rt5640_dais[] = { /* Front End DAI links */ @@ -114,7 +116,9 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = haswell_rtd_init, +#endif .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .dpcm_capture = 1, From 7d765b4fa767f0a717c8bc8167f08d0ae164de87 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 9 Jan 2018 13:40:20 -0600 Subject: [PATCH 058/298] ASoC: Intel: Kconfig: expose common option between SST and SOF drivers Both drivers rely on the same module, expose it for both configurations Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 08f00058700023..9ff42b4294e755 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -117,12 +117,14 @@ config SND_SOC_INTEL_SKYLAKE GeminiLake or CannonLake platform with the DSP enabled in the BIOS then enable this option by saying Y or m. +endif ## SND_SOC_INTEL_SST_TOPLEVEL + +if SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL 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 From 45ef824c4810b29653b55a4776eabc21aca8f4ed Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 7 Dec 2017 21:46:33 +0000 Subject: [PATCH 059/298] ASoC: Intel: select relevant machine drivers for SOF SOF can only support specific machine drivers, handle dependencies Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 24797482a3d22d..05097128a0822a 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -1,6 +1,6 @@ menuconfig SND_SOC_INTEL_MACH bool "Intel Machine drivers" - depends on SND_SOC_INTEL_SST_TOPLEVEL + depends on SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL 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 @@ -12,7 +12,7 @@ menuconfig SND_SOC_INTEL_MACH if SND_SOC_INTEL_MACH -if SND_SOC_INTEL_HASWELL +if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_HASWELL config SND_SOC_INTEL_HASWELL_MACH tristate "Haswell Lynxpoint" @@ -24,6 +24,10 @@ config SND_SOC_INTEL_HASWELL_MACH Say Y or m if you have such a device. If unsure select "N". +endif ## SND_SOC_INTEL_HASWELL || SND_SOC_SOF_HASWELL + +if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL + config SND_SOC_INTEL_BDW_RT5677_MACH tristate "Broadwell with RT5677 codec" depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM && GPIOLIB @@ -43,7 +47,7 @@ config SND_SOC_INTEL_BROADWELL_MACH Ultrabook platforms. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". -endif ## SND_SOC_INTEL_HASWELL +endif ## SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL if SND_SOC_INTEL_BAYTRAIL @@ -68,7 +72,7 @@ config SND_SOC_INTEL_BYT_RT5640_MACH endif ## SND_SOC_INTEL_BAYTRAIL -if SND_SST_ATOM_HIFI2_PLATFORM +if SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL config SND_SOC_INTEL_BYTCR_RT5640_MACH tristate "Baytrail and Baytrail-CR with RT5640 codec" @@ -158,6 +162,10 @@ config SND_SOC_INTEL_BYT_CHT_ES8316_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL + +if SND_SST_ATOM_HIFI2_PLATFORM + config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH tristate "Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" depends on X86_INTEL_LPSS && I2C && ACPI @@ -212,6 +220,10 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL + +if SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE + config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -239,6 +251,10 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE + +if SND_SOC_INTEL_SKL + config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI From 32806ab4d9c3dd1477250ebb6565cc58254b2cd9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 3 Jul 2018 12:33:03 -0500 Subject: [PATCH 060/298] [SQUASHME] ASoC: Intel: fix Kconfig typos Fix ASoC: Intel: select relevant machine drivers for SOF s/INTEL_SKL/INTEL_SKYLAKE Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 05097128a0822a..c78e357a8309e6 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -220,9 +220,9 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". -endif ## SND_SOC_INTEL_SKL +endif ## SND_SOC_INTEL_SKYLAKE -if SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE +if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_APOLLOLAKE config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" @@ -251,9 +251,9 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". -endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE +endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_APOLLOLAKE -if SND_SOC_INTEL_SKL +if SND_SOC_INTEL_SKYLAKE config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" From c246d823a60f77651951a094c1a6767426b34cee Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 23 Apr 2018 12:06:09 -0500 Subject: [PATCH 061/298] ASoC: Intel: add machine driver for BXT/APL with pcm512x codec This patch adds the machine driver for the Up2 board with the Hifiberry DAC+ codec. Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 4 + sound/soc/intel/boards/bxt_pcm512x.c | 186 +++++++++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_pcm512x.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index c78e357a8309e6..cc2bb74b70d88a 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -251,6 +251,16 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXT_PCM512x_MACH + tristate "Broxton with TI PCM512x codec" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_PCM512x_I2C + help + This adds support for ASoC machine driver for Broxton platforms + with TI PCM512x I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_APOLLOLAKE if SND_SOC_INTEL_SKYLAKE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 92b5507291af49..3352d3fa78ace6 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,8 @@ snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o +snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o +snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -27,6 +29,8 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o +obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c new file mode 100644 index 00000000000000..5bc80853707dd2 --- /dev/null +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -0,0 +1,186 @@ +/* + * bxt-pcm512x.c - ASoc Machine driver for Intel Baytrail and + * Cherrytrail-based platforms, with TI PCM512x codec + * + * Copyright (C) 2016 Intel Corporation + * Author: Pierre-Louis Bossart + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/pcm512x.h" +#include "../atom/sst-atom-controls.h" + +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Speaker */ + {"Ext Spk", NULL, "OUTR"}, + {"Ext Spk", NULL, "OUTL"}, +}; + +static int codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = 48000; + rate->max = 48000; + channels->min = 2; + channels->max = 2; + + /* set SSP5 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int aif1_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); +} + +static void aif1_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); +} + +static int init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + + return 0; +} + +static const struct snd_soc_ops aif1_ops = { + .startup = aif1_startup, + .shutdown = aif1_shutdown, +}; + +static struct snd_soc_dai_link dailink[] = { + /* CODEC<->CODEC link */ + /* back ends */ + { + .name = "SSP5-Codec", + .id = 0, + .cpu_dai_name = "sof-audio", + .platform_name = "sof-audio", + .no_pcm = 1, + .codec_dai_name = "pcm512x-hifi", + .codec_name = "i2c-104C5122:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .init = init, + .be_hw_params_fixup = codec_fixup, + .nonatomic = true, + .dpcm_playback = 1, + }, +}; + +/* SoC card */ +static struct snd_soc_card bxt_pcm512x_card = { + .name = "bxt-pcm512x", + .owner = THIS_MODULE, + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .dapm_widgets = dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + + /* i2c-:00 with HID being 8 chars */ +static char codec_name[SND_ACPI_I2C_ID_LEN]; + +static int bxt_pcm512x_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; + int dai_index = 0; + int ret_val = 0, i; + + mach = (&pdev->dev)->platform_data; + card = &bxt_pcm512x_card; + card->dev = &pdev->dev; + + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(dailink); i++) { + if (!strcmp(dailink[i].codec_name, "i2c-104C5122: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); + dailink[dai_index].codec_name = codec_name; + } + + ret_val = devm_snd_soc_register_card(&pdev->dev, card); + if (ret_val) { + dev_err(&pdev->dev, + "snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, card); + return ret_val; +} + +static struct platform_driver bxt_pcm521x_driver = { + .driver = { + .name = "bxt-pcm512x", + }, + .probe = bxt_pcm512x_probe, +}; +module_platform_driver(bxt_pcm521x_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Broxton + PCM512x Machine driver"); +MODULE_AUTHOR("Pierre-Louis Bossart"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt-pcm512x"); From e083c0e0923dca770077ca3db7ad76f32184de80 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 7 Jun 2018 14:24:12 +0800 Subject: [PATCH 062/298] ASoC: Intel: replace snd_soc_codec to snd_soc_component in bxt-pcm512x Codec may be can not use now, use the component instead. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bxt_pcm512x.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c index 5bc80853707dd2..c375d0c201582e 100644 --- a/sound/soc/intel/boards/bxt_pcm512x.c +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -67,9 +67,10 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, static int aif1_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); return snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, 48000); @@ -78,18 +79,20 @@ static int aif1_startup(struct snd_pcm_substream *substream) static void aif1_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x00); } static int init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); - snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); return 0; } From 79d00ad7ab6194ac108f2c42f6e81162b7313900 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Tue, 10 Jul 2018 10:51:25 +0800 Subject: [PATCH 063/298] ASoC: Intel: fix dai link for bxt_pcm512x Change cpu dai name for SSP5 and add capture capability. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bxt_pcm512x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c index c375d0c201582e..00bd50573827b1 100644 --- a/sound/soc/intel/boards/bxt_pcm512x.c +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -108,7 +108,7 @@ static struct snd_soc_dai_link dailink[] = { { .name = "SSP5-Codec", .id = 0, - .cpu_dai_name = "sof-audio", + .cpu_dai_name = "SSP5 Pin", .platform_name = "sof-audio", .no_pcm = 1, .codec_dai_name = "pcm512x-hifi", @@ -119,6 +119,7 @@ static struct snd_soc_dai_link dailink[] = { .be_hw_params_fixup = codec_fixup, .nonatomic = true, .dpcm_playback = 1, + .dpcm_capture = 1, }, }; From 322c314c1061380e76b4d8b9de9104569fa8f61a Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 23 Nov 2016 19:05:46 +0530 Subject: [PATCH 064/298] ASoC: Intel: Boards: Add CNL RT274 I2S machine driver Add the CNL I2S machine driver using Realtek ALC274 codec in I2S mode. Change-Id: Ife808f52d69e73a8156130c446a3ab0602fff63d Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/12406 Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/Kconfig | 16 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 385 +++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index cc2bb74b70d88a..e4751bf8e89cea 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -309,4 +309,20 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH endif ## SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKYLAKE + +config SND_SOC_INTEL_CNL_RT274_MACH + tristate "Cannonlake with RT274 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT274 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Cannonlake platform + with RT274 I2S audio codec. + 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 ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 3352d3fa78ace6..b62c1a44e4fbd1 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,6 +23,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl-rt274-objs := cnl_rt274.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -48,3 +49,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c new file mode 100644 index 00000000000000..1b788f8e59a22f --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -0,0 +1,385 @@ +/* + * cnl_rt274.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Intel Corp + * Author: Guneshwor Singh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/rt274.h" + +static struct snd_soc_jack cnl_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cnl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route cnl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, +}; + +static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + + if (ret) + return ret; + + snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static unsigned int rates_supported[] = { + 48000, + 32000, + 24000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list rate_constraints = { + .count = ARRAY_SIZE(rates_supported), + .list = rates_supported, +}; + +static int cnl_fe_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &rate_constraints); +} + +static struct snd_soc_ops cnl_fe_ops = { + .startup = cnl_fe_startup, +}; + +static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +#define CNL_FREQ_OUT 19200000 + +static int rt274_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret, ratio = 100; + + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + ratio * params_rate(params), CNL_FREQ_OUT); + if (ret != 0) { + dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops rt274_ops = { + .hw_params = rt274_hw_params, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "rt274.0-001c"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; +#endif + +static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { + { + .name = "CNL Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_playback = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Reference Port", + .stream_name = "Reference Capture", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "CNL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "CNL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "CNL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_rt274_init, + .ops = &rt274_ops, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + }, + { + .name = "dmic01", + .id = 3, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "cnl-audio", + .dai_link = cnl_rt274_msic_dailink, + .num_links = ARRAY_SIZE(cnl_rt274_msic_dailink), + .dapm_widgets = cnl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt274_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .controls = cnl_controls, + .num_controls = ARRAY_SIZE(cnl_controls), +}; + +static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) +{ + snd_soc_card_cnl.dev = &pdev->dev; + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); +} + +static struct platform_driver snd_cnl_rt274_driver = { + .driver = { + .name = "cnl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_cnl_rt274_mc_probe, +}; + +module_platform_driver(snd_cnl_rt274_driver); + +MODULE_AUTHOR("Guneshwor Singh "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt274"); From d724b0a813145a64a9f0beb36a6bcf2820e1c0ec Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 27 Feb 2017 09:09:37 +0530 Subject: [PATCH 065/298] ASoC: Intel: board: Add support for dynamic FE dai link in cnl_rt274 machine FE dai links now come from topology, so remove them from machine driver. Additionally register ops to initialize dai link. Rate constraint is not required as rates will come from topology. So remove the startup ops as well which sets the rate constraint. Change-Id: I0fb07c74450bf55415323539e383ef39ed3ff4c4 Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/13924 Reviewed-by: Koul, Vinod Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 73 ++++-------------------------- 1 file changed, 10 insertions(+), 63 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 1b788f8e59a22f..436a11d9a4f8c1 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -124,30 +124,6 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) return 0; } -static unsigned int rates_supported[] = { - 48000, - 32000, - 24000, - 16000, - 8000, -}; - -static struct snd_pcm_hw_constraint_list rate_constraints = { - .count = ARRAY_SIZE(rates_supported), - .list = rates_supported, -}; - -static int cnl_fe_startup(struct snd_pcm_substream *substream) -{ - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &rate_constraints); -} - -static struct snd_soc_ops cnl_fe_ops = { - .startup = cnl_fe_startup, -}; - static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -204,45 +180,6 @@ static const char cname[] = "i2c-INT34C2:00"; #endif static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { - { - .name = "CNL Audio Port", - .stream_name = "Audio", - .cpu_dai_name = "System Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Deepbuffer Port", - .stream_name = "Deep Buffer Audio", - .cpu_dai_name = "Deepbuffer Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_playback = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Reference Port", - .stream_name = "Reference Capture", - .cpu_dai_name = "Reference Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, /* Trace Buffer DAI links */ { .name = "CNL Trace Buffer0", @@ -351,6 +288,15 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { }, }; +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cnl = { .name = "cnl-audio", @@ -362,6 +308,7 @@ static struct snd_soc_card snd_soc_card_cnl = { .num_dapm_routes = ARRAY_SIZE(cnl_map), .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), + .add_dai_link = cnl_add_dai_link, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) From 88331a3cfda99ffb1051453922f7b34a815904d1 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 4 Apr 2017 08:34:51 +0530 Subject: [PATCH 066/298] ASoC: Intel: board: Move cnl_rt274 clock setting to supply widget During BE-BE loop, codec clocks were not set as it was a part of dai link ops hw_params and no sound was heard due to this reason when use cases involve BE-BE loop. So, move codec clock setting as a part of supply widget and define routes appropriately. Also use macro to define BE rate fixup and use it for both fixup as well as clock computation. Change-Id: Id5a08d2bd6024a61b601dbbe70ad99a52149da5e Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/14595 Reviewed-by: Prusty, Subhransu S Reviewed-by: Kale, Sanyog R Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 89 +++++++++++++++++++----------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 436a11d9a4f8c1..b67330dcfa07d8 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -35,6 +35,56 @@ #include "../../codecs/rt274.h" +#define CNL_FREQ_OUT 19200000 +#define CNL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + +static int cnl_rt274_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; + int ret = 0, ratio = 100; + struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, + RT274_CODEC_DAI); + + /* Codec needs clock for Jack detection and button press */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, + CNL_FREQ_OUT, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret); + return ret; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + CNL_BE_FIXUP_RATE * ratio, + CNL_FREQ_OUT); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL2: %d\n", ret); + return ret; + } + } + + return ret; +} + static struct snd_soc_jack cnl_headset; /* Headset jack detection DAPM pins */ @@ -58,6 +108,9 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, @@ -94,6 +147,9 @@ static const struct snd_soc_dapm_route cnl_map[] = { {"ssp0 Rx", NULL, "AIF1 Capture"}, {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, }; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) @@ -132,7 +188,7 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = 48000; + rate->min = rate->max = CNL_BE_FIXUP_RATE; channels->min = channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), @@ -141,36 +197,6 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -#define CNL_FREQ_OUT 19200000 - -static int rt274_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret, ratio = 100; - - snd_soc_dai_set_bclk_ratio(codec_dai, ratio); - - ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, - ratio * params_rate(params), CNL_FREQ_OUT); - if (ret != 0) { - dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); - if (ret < 0) - dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); - - return ret; -} - -static struct snd_soc_ops rt274_ops = { - .hw_params = rt274_hw_params, -}; - #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) static const char pname[] = "0000:02:18.0"; static const char cname[] = "rt274.0-001c"; @@ -260,7 +286,6 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = cnl_rt274_init, - .ops = &rt274_ops, }, { .name = "SSP1-Codec", From 5c1565b554519958074b6515344766f49cd6e757 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 15 Jun 2017 15:08:06 +0530 Subject: [PATCH 067/298] ASoC: Intel: boards: Remove SSP1-codec dai link from cnl_rt274 machine Since NHLT does not have SSP1 endpoint, remove it from the dai link definitions Change-Id: I7b08f43d21eeff9decb5722e3af4f142f800b3f7 Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15999 Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: Babu, Ramesh Reviewed-by: audio_build Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index b67330dcfa07d8..677724273c2630 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -287,21 +287,9 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, - { - .name = "SSP1-Codec", - .id = 2, - .cpu_dai_name = "SSP1 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .be_hw_params_fixup = cnl_be_fixup, - .ignore_suspend = 1, - .no_pcm = 1, - .dpcm_playback = 1, - }, { .name = "dmic01", - .id = 3, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", From 75187156973e6dec0a751d4df27086d4461f4e5d Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:55 +0530 Subject: [PATCH 068/298] ASoC: Intel: Skylake: Fix codec_dai NULL pointer dereferening Pointer 'codec_dai' returned from call to cnl_get_codec_dai() can be NULL. Check for the valid pointer before dereferencing. Change-Id: I783b6220e32a9b8bf7655b92df7a4b034175a509 Signed-off-by: Pankaj Bharadiya Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/18681 Reviewed-by: Prusty, Subhransu S Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 677724273c2630..6a048213cf509c 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -60,6 +60,8 @@ static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, int ret = 0, ratio = 100; struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, RT274_CODEC_DAI); + if (!codec_dai) + return -EINVAL; /* Codec needs clock for Jack detection and button press */ ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, From eb22dc703e27ad70cd9f856ca825fceb4e8aa748 Mon Sep 17 00:00:00 2001 From: Shahina Shaik Date: Mon, 30 Apr 2018 18:47:02 +0530 Subject: [PATCH 069/298] ASoC: Intel: Boards: Replace codec to component in RT274 machine driver As the framework is changed in kernel 4.17 version, replace codec variable with component and use component specific function to set jack. Change-Id: Id6d1cda7968a5d524a3210f1b38221214c2bb67d Signed-off-by: Shahina Shaik --- sound/soc/intel/boards/cnl_rt274.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 6a048213cf509c..0926e6c4b6ebc1 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -157,7 +157,7 @@ static const struct snd_soc_dapm_route cnl_map[] = { static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_component *component = runtime->codec_dai->component;; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; @@ -168,7 +168,7 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + snd_soc_component_set_jack(component, &cnl_headset, NULL); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); From 8ebf69c592b964c111bbcdd25d6ca362d416cf31 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 23 Jun 2018 09:50:50 -0500 Subject: [PATCH 070/298] ASoC: Intel: cnl-rt274: fix compilation warning Needs to be squashed with component model update, kept separate to avoid merge issues with SST code Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cnl_rt274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 0926e6c4b6ebc1..2d55e4d878b70e 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -157,7 +157,7 @@ static const struct snd_soc_dapm_route cnl_map[] = { static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_component *component = runtime->codec_dai->component;; + struct snd_soc_component *component = runtime->codec_dai->component; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; From aeef8c786a1e03a993a6901296e8e4e04a06edfd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 23 Jun 2018 09:27:25 -0500 Subject: [PATCH 071/298] ASoC: Intel: cnl-rt274: fix clock settings Use 24MHz instead of 19.2 (Mandatory rework) and use helper to find the dai Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cnl_rt274.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 2d55e4d878b70e..0dacf9401e087e 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -35,30 +35,19 @@ #include "../../codecs/rt274.h" -#define CNL_FREQ_OUT 19200000 +#define CNL_FREQ_OUT 24000000 #define CNL_BE_FIXUP_RATE 48000 #define RT274_CODEC_DAI "rt274-aif1" -static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, - const char *dai_name) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strcmp(rtd->codec_dai->name, dai_name)) - return rtd->codec_dai; - } - - return NULL; -} - static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) + struct snd_kcontrol *k, int event) { struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; - int ret = 0, ratio = 100; - struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, + int ret = 0; + int ratio = 100; + + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI); if (!codec_dai) return -EINVAL; From df794aa8ffb13c8649f4ad19ee0ac7a6d7bd0676 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 May 2018 20:43:51 -0500 Subject: [PATCH 072/298] ASoC: Intel: make cnl_rt274 work with SOF (1)refine machine driver to make it work with sof (2)disable DMIC for it is not ready now Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 2 +- sound/soc/intel/boards/cnl_rt274.c | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index e4751bf8e89cea..fd55d7118f8dc7 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -309,7 +309,7 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH endif ## SND_SOC_INTEL_SKYLAKE -if SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_CANNONLAKE config SND_SOC_INTEL_CNL_RT274_MACH tristate "Cannonlake with RT274 I2S mode" diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 0dacf9401e087e..98a7b3015c0d23 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -100,10 +100,11 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -113,6 +114,7 @@ static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +#endif static const struct snd_soc_dapm_route cnl_map[] = { {"Headphone Jack", NULL, "HPO Pin"}, @@ -151,8 +153,9 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dai *codec_dai = runtime->codec_dai; ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &cnl_headset, - cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, + ARRAY_SIZE(cnl_headset_pins)); if (ret) return ret; @@ -172,15 +175,17 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) } static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = CNL_BE_FIXUP_RATE; - channels->min = channels->max = 2; + rate->min = CNL_BE_FIXUP_RATE; + rate->max = CNL_BE_FIXUP_RATE; + channels->min = 2; + channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); @@ -197,6 +202,7 @@ static const char cname[] = "i2c-INT34C2:00"; #endif static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* Trace Buffer DAI links */ { .name = "CNL Trace Buffer0", @@ -261,6 +267,7 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .ignore_suspend = 1, .nonatomic = 1, }, +#endif /* back ends */ { .name = "SSP0-Codec", @@ -268,7 +275,6 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .cpu_dai_name = "SSP0 Pin", .codec_name = cname, .codec_dai_name = "rt274-aif1", - .platform_name = pname, .be_hw_params_fixup = cnl_be_fixup, .ignore_suspend = 1, .no_pcm = 1, @@ -278,18 +284,19 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) { .name = "dmic01", .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", - .platform_name = pname, .ignore_suspend = 1, .no_pcm = 1, .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, +#endif }; static int @@ -313,6 +320,7 @@ static struct snd_soc_card snd_soc_card_cnl = { .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), .add_dai_link = cnl_add_dai_link, + .fully_routed = true, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) From 135409b7ea86e6cfce1ebd58831933b41d0d537c Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 29 Jun 2018 14:24:51 +0800 Subject: [PATCH 073/298] Asoc: Intel: refine rt274 machine driver for SOF Add SOF platform name Signed-off-by: Rander Wang --- sound/soc/intel/boards/cnl_rt274.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 98a7b3015c0d23..0689c63304ea46 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -197,7 +197,11 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, static const char pname[] = "0000:02:18.0"; static const char cname[] = "rt274.0-001c"; #else +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) +static const char pname[] = "sof-audio"; +#else static const char pname[] = "0000:00:1f.3"; +#endif static const char cname[] = "i2c-INT34C2:00"; #endif From d8c38b07fdb91157e04f7219af52c91c0392f2f8 Mon Sep 17 00:00:00 2001 From: "Wagner, Steffen" Date: Tue, 15 May 2018 17:14:50 +0800 Subject: [PATCH 074/298] ASoC: tdf8532: NXP TDF8532 audio class-D amplifier driver This is a basic driver to register the codec, expose the codec DAI and control the power mode of the amplifier. Change-Id: Ie6ab037cd4d6c87e8e139b6d8af6cd4295445bf2 Signed-off-by: Mohit Sinha Signed-off-by: Steffen Wagner Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15296 Reviewed-by: B, Jayachandran Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tdf8532.c | 379 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tdf8532.h | 101 ++++++++++ 4 files changed, 487 insertions(+) create mode 100644 sound/soc/codecs/tdf8532.c create mode 100644 sound/soc/codecs/tdf8532.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 63cf62e9c9aa67..f38a62178cfa76 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -161,6 +161,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C + select SND_SOC_TDF8532 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -975,6 +976,10 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C +config SND_SOC_TDF8532 + tristate + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e023fdf852215d..4f4033353633c4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -172,6 +172,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tdf8532-objs := tdf8532.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -429,6 +430,7 @@ 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_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TDF8532) += snd-soc-tdf8532.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 diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c new file mode 100644 index 00000000000000..59db83da37587a --- /dev/null +++ b/sound/soc/codecs/tdf8532.c @@ -0,0 +1,379 @@ +/* + * Codec driver for NXP Semiconductors - TDF8532 + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdf8532.h" + +static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, + va_list valist, u8 *payload) +{ + int param; + u8 len; + u8 *cmd_payload; + const u8 cmd_offset = 3; + + payload[HEADER_TYPE] = MSG_TYPE_STX; + payload[HEADER_PKTID] = dev_data->pkt_id; + + cmd_payload = &(payload[cmd_offset]); + + param = va_arg(valist, int); + len = 0; + + while (param != END) { + cmd_payload[len] = param; + + len++; + + param = va_arg(valist, int); + } + + payload[HEADER_LEN] = len; + + return len + cmd_offset; +} + +static int __tdf8532_single_write(struct tdf8532_priv *dev_data, + int dummy, ...) +{ + va_list valist; + int ret; + u8 len; + u8 payload[255]; + + va_start(valist, dummy); + + len = __tdf8532_build_pkt(dev_data, valist, payload); + + va_end(valist); + + print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, + payload, len, false); + ret = i2c_master_send(dev_data->i2c, payload, len); + + dev_data->pkt_id++; + + if (ret < 0) { + dev_err(&(dev_data->i2c->dev), + "i2c send packet returned: %d\n", ret); + + return ret; + } + + return 0; +} + + +static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) +{ + uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + unsigned long timeout_point = jiffies + timeout; + int ret; + + do { + ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); + if (ret < 0) + goto out; + } while (time_before(jiffies, timeout_point) && + ack_repl[0] != MSG_TYPE_ACK); + + if (ack_repl[0] != MSG_TYPE_ACK) + return -ETIME; + else + return ack_repl[2]; + +out: + return ret; +} + +static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, + char **repl_buff) +{ + int ret; + uint8_t len; + + struct device *dev = &(dev_data->i2c->dev); + + ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); + + if (ret < 0) { + dev_err(dev, + "Error waiting for ACK reply: %d\n", ret); + goto out; + } + + len = ret + HEADER_SIZE; + + *repl_buff = kzalloc(len, GFP_KERNEL); + + ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); + + print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, + *repl_buff, len, false); + + if (ret < 0 || ret != len) { + dev_err(dev, + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); + goto out_free; + } + + return len; + +out_free: + kfree(*repl_buff); + repl_buff = NULL; +out: + return ret; +} + +static int tdf8532_get_state(struct tdf8532_priv *dev_data, + struct get_dev_status_repl **status_repl) +{ + int ret = 0; + char *repl_buff = NULL; + + ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); + if (ret < 0) + goto out; + + ret = tdf8532_single_read(dev_data, &repl_buff); + if (ret < 0) + goto out; + + *status_repl = (struct get_dev_status_repl *) repl_buff; + +out: + return ret; +} + +static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, + unsigned long timeout) +{ + unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); + int ret; + struct get_dev_status_repl *status_repl; + struct device *dev = &(dev_data->i2c->dev); + + do { + ret = tdf8532_get_state(dev_data, &status_repl); + if (ret < 0) + goto out; + + print_hex_dump_debug("tdf8532-codec: wait_state: ", + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); + } while (time_before(jiffies, timeout_point) + && status_repl->state != req_state); + + if (status_repl->state == req_state) + return 0; + + ret = -ETIME; + + dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + status_repl->state, req_state, ret); + +out: + kfree(status_repl); + return ret; +} + +static int tdf8532_start_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); + if (ret < 0) + return ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, + CHNL_MASK(tdf8532->channels)); + + if (ret >= 0) + ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); + + return ret; +} + + +static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, + CHNL_MASK(tdf8532->channels)); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); + if (ret < 0) + goto out; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_IDLE, ACK_TIMEOUT); + +out: + return ret; +} + + +static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: cmd = %d\n", __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = tdf8532_start_play(tdf8532); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = tdf8532_stop_play(tdf8532); + break; + } + + return ret; +} + +static int tdf8532_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(codec->dev, "%s\n", __func__); + + if (mute) + return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, + CHNL_MASK(CHNL_MAX)); + else + return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, + CHNL_MASK(CHNL_MAX)); +} + +static const struct snd_soc_dai_ops tdf8532_dai_ops = { + .trigger = tdf8532_dai_trigger, + .digital_mute = tdf8532_mute, +}; + +static struct snd_soc_codec_driver soc_codec_tdf8532; + +static struct snd_soc_dai_driver tdf8532_dai[] = { + { + .name = "tdf8532-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 4, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tdf8532_dai_ops, + } +}; + +static int tdf8532_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct tdf8532_priv *dev_data; + struct device *dev = &(i2c->dev); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + dev_data = devm_kzalloc(dev, sizeof(struct tdf8532_priv), GFP_KERNEL); + + if (!dev_data) { + ret = -ENOMEM; + goto out; + } + + if (ret < 0) + dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); + + dev_data->i2c = i2c; + dev_data->pkt_id = 0; + dev_data->channels = 4; + + i2c_set_clientdata(i2c, dev_data); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_tdf8532, + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int tdf8532_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id tdf8532_i2c_id[] = { + { "tdf8532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tdf8532_i2c_id); + +#if CONFIG_ACPI +static const struct acpi_device_id tdf8532_acpi_match[] = { + {"INT34C3", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tdf8532_acpi_match); +#endif + +static struct i2c_driver tdf8532_i2c_driver = { + .driver = { + .name = "tdf8532-codec", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(tdf8532_acpi_match), + }, + .probe = tdf8532_i2c_probe, + .remove = tdf8532_i2c_remove, + .id_table = tdf8532_i2c_id, +}; + +module_i2c_driver(tdf8532_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TDF8532 driver"); +MODULE_AUTHOR("Steffen Wagner "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h new file mode 100644 index 00000000000000..6e3f2c147eace9 --- /dev/null +++ b/sound/soc/codecs/tdf8532.h @@ -0,0 +1,101 @@ +/* + * tdf8532.h - Codec driver for NXP Semiconductors + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 __TDF8532_H_ +#define __TDF8532_H_ + +#define ACK_TIMEOUT 300 + +#define CHNL_MAX 5 + +#define AMP_MOD 0x80 +#define END -1 + +#define MSG_TYPE_STX 0x02 +#define MSG_TYPE_NAK 0x15 +#define MSG_TYPE_ACK 0x6 + +#define HEADER_SIZE 3 +#define HEADER_TYPE 0 +#define HEADER_PKTID 1 +#define HEADER_LEN 2 + +/* Set commands */ +#define SET_CLK_STATE 0x1A +#define CLK_DISCONNECT 0x00 +#define CLK_CONNECT 0x01 + +#define SET_CHNL_ENABLE 0x26 +#define SET_CHNL_DISABLE 0x27 + +#define SET_CHNL_MUTE 0x42 +#define SET_CHNL_UNMUTE 0x43 + +struct header_repl { + u8 msg_type; + u8 pkt_id; + u8 len; +} __packed; + +#define GET_IDENT 0xE0 + +struct get_ident_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 type_name; + u8 hw_major; + u8 hw_minor; + u8 sw_major; + u8 sw_minor; + u8 sw_sub; +} __packed; + +#define GET_ERROR 0xE2 + +struct get_error_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 last_cmd_id; + u8 error; + u8 status; +} __packed; + +#define GET_DEV_STATUS 0x80 + +enum dev_state {STATE_BOOT, STATE_IDLE, STATE_STBY, STATE_LDAG, STATE_PLAY, + STATE_PROT, STATE_SDWN, STATE_CLFA, STATE_NONE }; + +struct get_dev_status_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 state; +} __packed; + +/* Helpers */ +#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) + +#define tdf8532_amp_write(dev_data, ...)\ + __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) + +struct tdf8532_priv { + struct i2c_client *i2c; + u8 channels; + u8 pkt_id; +}; + +#endif From ad877627a943bf1c5a7c437f9ca309fa0cdcdcc9 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:51 +0800 Subject: [PATCH 075/298] ASoC: tdf8532: Fix compilation warnings Initialized the reported variables, listed below warning: 'ret' may be used uninitialized in this function warning: 'status_repl' may be used uninitialized in this function Change-Id: I6ca5a6e017402a582239d75959c122ffaa9f7298 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/17572 Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 59db83da37587a..5f072079fbb76b 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -172,7 +172,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; - struct get_dev_status_repl *status_repl; + struct get_dev_status_repl *status_repl = NULL; struct device *dev = &(dev_data->i2c->dev); do { @@ -318,9 +318,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, goto out; } - if (ret < 0) - dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); - dev_data->i2c = i2c; dev_data->pkt_id = 0; dev_data->channels = 4; From f6a48c691ceac3a483ecf7b63b2e13e391111268 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:52 +0800 Subject: [PATCH 076/298] ASoC: tdf8532: Add delay while reading a packet from I2C While doing the continuous play and stop, the codec may not be ready for I2C reading after successive writes. This triggers BE failure, because I2C reading value is incorrect. Fix this by adding 10ms delay to ensure the smooth I2C read and write. Change-Id: If918e263bc799fecc2c807229f5b4b165e011fa6 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/20404 Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Nc, Shreyas Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 5f072079fbb76b..1860e8264ebab8 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -90,6 +90,7 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, unsigned long timeout_point = jiffies + timeout; int ret; + usleep_range(10000,20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) From 07b6657ed793b563a2f168fa5afbda2656d0da39 Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:24:01 +0800 Subject: [PATCH 077/298] ASoC: tdf8532: fix memleak in tdf8532_wait_state Fix kmemleak issue in tdf8532_wait_state function by releasing the memory getting allocated continuosly in instance of get_dev_status_repl i.e. status_repl before exiting the function. kernel memory leakage in audio stack/kmemleak backtrace: unreferenced object 0xffff88006227cc20 (size 32): comm "irq/25-snd_soc_", pid 2302, jiffies 4294679082 (age 5506.010s) hex dump (first 32 bytes): 02 00 03 80 80 02 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc+0x128/0x210 [] tdf8532_wait_state.constprop.5+0x116/0x260 [snd_soc_tdf8532] [] tdf8532_dai_trigger+0xab/0x15a [snd_soc_tdf8532] [] soc_pcm_trigger+0x75/0x130 [] dpcm_do_trigger.isra.6+0x29/0x90 [] dpcm_be_dai_trigger+0x18d/0x350 Change-Id: I550897d6b1efbd5ebbe15ab47038adf99581a82f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62665 Signed-off-by: Liu Changcheng Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/23270 Reviewed-by: Shaik, ShahinaX Reviewed-by: Singh, Guneshwor O Reviewed-by: Gogineni, GiribabuX Reviewed-by: Tewani, Pradeep D Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 1860e8264ebab8..aaf7254672cbeb 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -174,29 +174,31 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; struct get_dev_status_repl *status_repl = NULL; + u8 cur_state = STATE_NONE; struct device *dev = &(dev_data->i2c->dev); do { ret = tdf8532_get_state(dev_data, &status_repl); if (ret < 0) goto out; - + cur_state = status_repl->state; print_hex_dump_debug("tdf8532-codec: wait_state: ", DUMP_PREFIX_NONE, 32, 1, status_repl, 6, false); + + kfree(status_repl); + status_repl = NULL; } while (time_before(jiffies, timeout_point) - && status_repl->state != req_state); + && cur_state != req_state); - if (status_repl->state == req_state) + if (cur_state == req_state) return 0; +out: ret = -ETIME; dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", - status_repl->state, req_state, ret); - -out: - kfree(status_repl); + cur_state, req_state, ret); return ret; } From 279ff35bac241df404a4fd7121fb48e184a8da2f Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:11:42 +0800 Subject: [PATCH 078/298] ASoC: tdf8532: right free allocated space in case of error 1. Check allocated space before using it. 2. The repl_buff parameter in tdf8523_single_read is used to store the read data from i2c interface. When the data isn't right read, the pre-allocate space should be freed and the content of repl_buff should be set as NULL in case of being wrong used by the caller. 3. In the wrong case i.e. ret != len, return -EINVAL Change-Id: I3d0e12a9fcb6516716efc92eb734a0248ab3fb28 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62665 Signed-off-by: Liu Changcheng Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/23266 Reviewed-by: Shaik, ShahinaX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Gogineni, GiribabuX Reviewed-by: Singh, Guneshwor O Reviewed-by: Tewani, Pradeep D Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index aaf7254672cbeb..685a20d98b6b6a 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -107,11 +107,11 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, return ret; } -static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, +static int tdf8532_single_read(struct tdf8532_priv *dev_data, char **repl_buff) { int ret; - uint8_t len; + int len; struct device *dev = &(dev_data->i2c->dev); @@ -126,6 +126,10 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, len = ret + HEADER_SIZE; *repl_buff = kzalloc(len, GFP_KERNEL); + if (*repl_buff == NULL) { + ret = -ENOMEM; + goto out; + } ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); @@ -136,6 +140,8 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, dev_err(dev, "i2c recv packet returned: %d (expected: %d)\n", ret, len); + + ret = -EINVAL; goto out_free; } @@ -143,7 +149,7 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, out_free: kfree(*repl_buff); - repl_buff = NULL; + *repl_buff = NULL; out: return ret; } From 2463c2d0a956235c952c82a903c5fc747de69880 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Tue, 15 May 2018 17:14:53 +0800 Subject: [PATCH 079/298] ASoC: tdf8532: Fix the codec status error issue on APL-GPMRB Based on the TDF8532 manual: If the wait_state result is ok, we should send CLK_DISCONNECT command to force codec from STANDBY(2) to IDLE(1). If the wait_state result is timeout, the codec state should be at Clockfail(7), we still should send CLK_DISCONNECT command force the codec from Clockfail(7) to Idle(1). Signed-off-by: Wu Zhigang Reviewed-by: Keyon Jie --- sound/soc/codecs/tdf8532.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 685a20d98b6b6a..0ef8fe406270a5 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -236,8 +236,14 @@ static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) goto out; ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); - if (ret < 0) - goto out; + + /* Refer to TDF8532 manual: + * If the wait_state result is ok, we should send CLK_DISCONNECT + * command to force codec from STANDBY(2) to IDLE(1). + * If the wait_state result is timeout, the codec state should be + * at Clockfail(7), we still should send CLK_DISCONNECT command + * force the codec from Clockfail(7) to Idle(1). + */ ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); if (ret < 0) From 66fd51d47d9aab5ad5bc633dec2e0113849dd3ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 23 Jun 2018 08:45:49 -0500 Subject: [PATCH 080/298] ASoC: codecs: tdf8532: move to component model Needs to be squashed for 4.18+ Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/tdf8532.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 0ef8fe406270a5..8a5928a894ca24 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -260,10 +260,10 @@ static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { int ret = 0; - struct snd_soc_codec *codec = dai->codec; - struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); - dev_dbg(codec->dev, "%s: cmd = %d\n", __func__, cmd); + dev_dbg(component->dev, "%s: cmd = %d\n", __func__, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -283,10 +283,10 @@ static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, static int tdf8532_mute(struct snd_soc_dai *dai, int mute) { - struct snd_soc_codec *codec = dai->codec; - struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(dai->codec); + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); - dev_dbg(codec->dev, "%s\n", __func__); + dev_dbg(component->dev, "%s\n", __func__); if (mute) return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, @@ -301,7 +301,7 @@ static const struct snd_soc_dai_ops tdf8532_dai_ops = { .digital_mute = tdf8532_mute, }; -static struct snd_soc_codec_driver soc_codec_tdf8532; +static struct snd_soc_component_driver soc_component_tdf8532; static struct snd_soc_dai_driver tdf8532_dai[] = { { @@ -339,7 +339,7 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, dev_data); - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_tdf8532, + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_tdf8532, tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); if (ret != 0) { dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); @@ -352,8 +352,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, static int tdf8532_i2c_remove(struct i2c_client *i2c) { - snd_soc_unregister_codec(&i2c->dev); - return 0; } From 5fcfc39f23b79b900bf5204f0d1393e1cb83e7be Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:43 +0800 Subject: [PATCH 081/298] ASoC: Intel: Boards: Add BXTP MRB machine driver for NXP TDF8532 This is the machine driver for NXP TDF8532 Change-Id: Ieee7ba1fc2dab6fbe43836b65def88c81360d48f Signed-off-by: Mohit Sinha Signed-off-by: Markus Schweikhardt Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15375 Reviewed-by: Shaik, Kareem M Reviewed-by: B, Jayachandran Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_tdf8532.c | 209 +++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_tdf8532.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index fd55d7118f8dc7..0b67c81a86d048 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -261,6 +261,16 @@ config SND_SOC_INTEL_BXT_PCM512x_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXT_TDF8532_MACH + tristate "ASoC Audio driver for BXT with TDF8532 in I2S mode" + depends on X86 && ACPI && I2C + select SND_SOC_TDF8532 + select SND_SOC_COMPRESS + help + This adds support for ASoC machine driver for Broxton IVI GP MRB platform + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_APOLLOLAKE if SND_SOC_INTEL_SKYLAKE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index b62c1a44e4fbd1..c85f0b9bd3792c 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o +snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o @@ -31,6 +32,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c new file mode 100644 index 00000000000000..027060b17322a3 --- /dev/null +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -0,0 +1,209 @@ +/* + * Intel Broxton-P I2S Machine Driver for IVI reference platform + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include + +static const struct snd_kcontrol_new broxton_tdf8532_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DiranaCp", NULL), + SND_SOC_DAPM_HP("DiranaPb", NULL), + SND_SOC_DAPM_MIC("HdmiIn", NULL), + SND_SOC_DAPM_MIC("TestPinCp", NULL), + SND_SOC_DAPM_HP("TestPinPb", NULL), + SND_SOC_DAPM_MIC("BtHfpDl", NULL), + SND_SOC_DAPM_HP("BtHfpUl", NULL), + SND_SOC_DAPM_MIC("ModemDl", NULL), + SND_SOC_DAPM_HP("ModemUl", NULL), +}; + +static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { + + /* Speaker BE connections */ + { "Speaker", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + + { "dirana_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_aux_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_tuner_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "DiranaPb", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "dirana_out"}, + + { "hdmi_ssp1_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "HdmiIn"}, + + { "TestPin_ssp5_in", NULL, "ssp5 Rx"}, + { "ssp5 Rx", NULL, "TestPinCp"}, + + { "TestPinPb", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "TestPin_ssp5_out"}, + + { "BtHfp_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "BtHfpDl"}, + + { "BtHfpUl", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "BtHfp_ssp0_out"}, + + { "Modem_ssp3_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "ModemDl"}, + + { "ModemUl", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "Modem_ssp3_out"}, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Back End DAI links */ + { + /* SSP0 - BT */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP1 - HDMI-In */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + /* SSP2 - Dirana */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP3 - Modem */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP4 - Amplifier */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "i2c-INT34C3:00", + .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP5 - TestPin */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for TDF8532 */ +static struct snd_soc_card broxton_tdf8532 = { + .name = "broxton_tdf8532", + .dai_link = broxton_tdf8532_dais, + .num_links = ARRAY_SIZE(broxton_tdf8532_dais), + .controls = broxton_tdf8532_controls, + .num_controls = ARRAY_SIZE(broxton_tdf8532_controls), + .dapm_widgets = broxton_tdf8532_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_tdf8532_widgets), + .dapm_routes = broxton_tdf8532_map, + .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_tdf8532_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + broxton_tdf8532.dev = &pdev->dev; + return snd_soc_register_card(&broxton_tdf8532); +} + +static int broxton_tdf8532_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_tdf8532); + return 0; +} + +static struct platform_driver broxton_tdf8532_audio = { + .probe = broxton_tdf8532_audio_probe, + .remove = broxton_tdf8532_audio_remove, + .driver = { + .name = "bxt_tdf8532", + }, +}; + +module_platform_driver(broxton_tdf8532_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpmrb_machine"); From 620f9a6a8b428347de6e4c5383c83f9b85099c7b Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:10:32 +0530 Subject: [PATCH 082/298] ASoC: Intel: Board: DAI links for probe in GPMRB machine driver Added two DAI links for probe playback and capture Change-Id: I0bf364eba3b6a2b779625a6fd1b664c2530a1ab2 Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 027060b17322a3..1e2b8be0012707 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -78,6 +78,27 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Probe DAI links*/ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From 9f8f7dd3deca6d5b13ada6514c6ff9992539e8d1 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:21:19 +0530 Subject: [PATCH 083/298] ASoC: Intel: Boards: Add FW logging DAI-links for GPMRB Add two FW logging DAI for each DSP core Change-Id: Ic825ecb4afbbcacabda6b74e2e5f2969fc722a1f Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 1e2b8be0012707..325b59adaf1c05 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -99,6 +99,27 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .init = NULL, .nonatomic = 1, }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From 481a683ad79ea94425df80e8d214bfb22b143fca Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Wed, 30 Aug 2017 16:46:40 +0530 Subject: [PATCH 084/298] ASoC: Intel: Board: Add pm_ops to fix suspend/resume issue Audio playback not resumed after it is suspended. Add snd_soc_pm_ops to execute power management operation. Change-Id: I84ccf6a0ac7e35c1f79971ee59555f24024d4309 Signed-off-by: Mohit Sinha Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/17914 Reviewed-by: Shaik, Kareem M Reviewed-by: Prusty, Subhransu S Reviewed-by: H S, Vijay Reviewed-by: Kp, Jeeja Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 325b59adaf1c05..c7b7fe3f9ed7c4 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -240,6 +240,7 @@ static struct platform_driver broxton_tdf8532_audio = { .remove = broxton_tdf8532_audio_remove, .driver = { .name = "bxt_tdf8532", + .pm = &snd_soc_pm_ops, }, }; From 4e40639006d495d4a1c3dfaa499c70b9ea579e7f Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Mon, 4 Sep 2017 23:31:17 +0530 Subject: [PATCH 085/298] ASoC: Intel: Board: Add fixup for 32 bit masking Fixup function does the masking of the format to set the SSP2 to 32 bit Change-Id: I1c5f20ce1244f9c3a47a47342d46184fdd718290 Signed-off-by: Mohit Sinha Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/18076 Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/bxt_tdf8532.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index c7b7fe3f9ed7c4..27361e8f72d3f0 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -76,6 +76,18 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { { "ssp3 Tx", NULL, "Modem_ssp3_out"}, }; +static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* set SSP to 32 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + + return 0; +} + /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { /* Probe DAI links*/ @@ -158,6 +170,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .dpcm_capture = 1, .dpcm_playback = 1, .no_pcm = 1, + .be_hw_params_fixup = bxt_tdf8532_ssp2_fixup, }, { /* SSP3 - Modem */ From 765f26429abca1a10ce43184a0fb995db2d9da1b Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:44 +0800 Subject: [PATCH 086/298] ASoC: Intel: bxt-tdf8532: reuse machine driver for GP-MRB Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 27361e8f72d3f0..4dd73d356740f4 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -263,3 +263,4 @@ module_platform_driver(broxton_tdf8532_audio) MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gpmrb_machine"); +MODULE_ALIAS("platform:bxt_tdf8532"); From 132f58cbb40197789c6aba278258ea3066991f1a Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 28 May 2018 13:43:18 +0800 Subject: [PATCH 087/298] ASoC: Intel: bxt-tdf8532: FIX: don't use add_dai_link() for SOF We set ignore_machine for SOF soc platform driver, which will trigger overriding FEs in soc_check_tplg_fes(), but this overriding may be overidden again by add_dai_link() in bxt_tdf8532, e.g. platform_name will be modified to be "0000:00:0e.0", which is not exist in SOF. Here add #ifdef to bypass add_dai_link() for using the machine driver with SOF to fix the overridden again issue. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 4dd73d356740f4..dd8bdfd352b2d1 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -212,6 +212,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { }, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) { @@ -219,6 +220,7 @@ static int bxt_add_dai_link(struct snd_soc_card *card, link->nonatomic = 1; return 0; } +#endif /* broxton audio machine driver for TDF8532 */ static struct snd_soc_card broxton_tdf8532 = { @@ -232,7 +234,9 @@ static struct snd_soc_card broxton_tdf8532 = { .dapm_routes = broxton_tdf8532_map, .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), .fully_routed = true, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .add_dai_link = bxt_add_dai_link, +#endif }; static int broxton_tdf8532_audio_probe(struct platform_device *pdev) From 626ba3bd421c2be462f842cc3f24146115c79026 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 20 Jun 2018 16:50:51 +0800 Subject: [PATCH 088/298] ASoC: Intel: bxt-tdf8532: change probe and trace buffer dai_links to dynamic We should use .dynamic for all FE dai_links, so change probe and trace buffer ones here to align to that. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index dd8bdfd352b2d1..abdc04e389a8fb 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -100,6 +100,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .init = NULL, .nonatomic = 1, + .dynamic = 1, }, { .name = "Bxt Compress Probe capture", @@ -110,6 +111,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .init = NULL, .nonatomic = 1, + .dynamic = 1, }, /* Trace Buffer DAI links */ { @@ -121,6 +123,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .capture_only = true, .ignore_suspend = 1, + .dynamic = 1, }, { .name = "Bxt Trace Buffer1", @@ -131,6 +134,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .capture_only = true, .ignore_suspend = 1, + .dynamic = 1, }, /* Back End DAI links */ { From 35ecce5316b939ae53832e05f36962575e046d94 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 13:31:56 -0500 Subject: [PATCH 089/298] ASoC: codecs: TDF8532: use Linux style Make checkpatch happy, no functionality change Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/tdf8532.c | 82 ++++++++++++++++---------------------- sound/soc/codecs/tdf8532.h | 13 +----- 2 files changed, 37 insertions(+), 58 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 8a5928a894ca24..d8ae9ff441c8d9 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Codec driver for NXP Semiconductors - TDF8532 * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #include @@ -24,7 +16,7 @@ #include "tdf8532.h" static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, - va_list valist, u8 *payload) + va_list valist, u8 *payload) { int param; u8 len; @@ -34,7 +26,7 @@ static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, payload[HEADER_TYPE] = MSG_TYPE_STX; payload[HEADER_PKTID] = dev_data->pkt_id; - cmd_payload = &(payload[cmd_offset]); + cmd_payload = &payload[cmd_offset]; param = va_arg(valist, int); len = 0; @@ -53,7 +45,7 @@ static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, } static int __tdf8532_single_write(struct tdf8532_priv *dev_data, - int dummy, ...) + int dummy, ...) { va_list valist; int ret; @@ -67,14 +59,14 @@ static int __tdf8532_single_write(struct tdf8532_priv *dev_data, va_end(valist); print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, - payload, len, false); + payload, len, false); ret = i2c_master_send(dev_data->i2c, payload, len); dev_data->pkt_id++; if (ret < 0) { - dev_err(&(dev_data->i2c->dev), - "i2c send packet returned: %d\n", ret); + dev_err(&dev_data->i2c->dev, + "i2c send packet returned: %d\n", ret); return ret; } @@ -82,15 +74,14 @@ static int __tdf8532_single_write(struct tdf8532_priv *dev_data, return 0; } - -static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, - unsigned long timeout) +static u8 tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) { - uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + u8 ack_repl[HEADER_SIZE] = {0, 0, 0}; unsigned long timeout_point = jiffies + timeout; int ret; - usleep_range(10000,20000); + usleep_range(10000, 20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) @@ -108,18 +99,18 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, } static int tdf8532_single_read(struct tdf8532_priv *dev_data, - char **repl_buff) + char **repl_buff) { int ret; int len; - struct device *dev = &(dev_data->i2c->dev); + struct device *dev = &dev_data->i2c->dev; ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); if (ret < 0) { dev_err(dev, - "Error waiting for ACK reply: %d\n", ret); + "Error waiting for ACK reply: %d\n", ret); goto out; } @@ -134,12 +125,12 @@ static int tdf8532_single_read(struct tdf8532_priv *dev_data, ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, - *repl_buff, len, false); + *repl_buff, len, false); if (ret < 0 || ret != len) { dev_err(dev, - "i2c recv packet returned: %d (expected: %d)\n", - ret, len); + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); ret = -EINVAL; goto out_free; @@ -155,10 +146,10 @@ static int tdf8532_single_read(struct tdf8532_priv *dev_data, } static int tdf8532_get_state(struct tdf8532_priv *dev_data, - struct get_dev_status_repl **status_repl) + struct get_dev_status_repl **status_repl) { - int ret = 0; char *repl_buff = NULL; + int ret = 0; ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); if (ret < 0) @@ -168,20 +159,20 @@ static int tdf8532_get_state(struct tdf8532_priv *dev_data, if (ret < 0) goto out; - *status_repl = (struct get_dev_status_repl *) repl_buff; + *status_repl = (struct get_dev_status_repl *)repl_buff; out: return ret; } static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, - unsigned long timeout) + unsigned long timeout) { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); - int ret; + struct device *dev = &dev_data->i2c->dev; struct get_dev_status_repl *status_repl = NULL; u8 cur_state = STATE_NONE; - struct device *dev = &(dev_data->i2c->dev); + int ret; do { ret = tdf8532_get_state(dev_data, &status_repl); @@ -189,13 +180,12 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, goto out; cur_state = status_repl->state; print_hex_dump_debug("tdf8532-codec: wait_state: ", - DUMP_PREFIX_NONE, 32, 1, status_repl, - 6, false); + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); kfree(status_repl); status_repl = NULL; - } while (time_before(jiffies, timeout_point) - && cur_state != req_state); + } while (time_before(jiffies, timeout_point) && cur_state != req_state); if (cur_state == req_state) return 0; @@ -204,7 +194,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, ret = -ETIME; dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", - cur_state, req_state, ret); + cur_state, req_state, ret); return ret; } @@ -217,7 +207,7 @@ static int tdf8532_start_play(struct tdf8532_priv *tdf8532) return ret; ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, - CHNL_MASK(tdf8532->channels)); + CHNL_MASK(tdf8532->channels)); if (ret >= 0) ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); @@ -225,13 +215,12 @@ static int tdf8532_start_play(struct tdf8532_priv *tdf8532) return ret; } - static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) { int ret; ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, - CHNL_MASK(tdf8532->channels)); + CHNL_MASK(tdf8532->channels)); if (ret < 0) goto out; @@ -255,13 +244,12 @@ static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) return ret; } - static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { - int ret = 0; struct snd_soc_component *component = dai->component; struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + int ret = 0; dev_dbg(component->dev, "%s: cmd = %d\n", __func__, cmd); @@ -301,7 +289,7 @@ static const struct snd_soc_dai_ops tdf8532_dai_ops = { .digital_mute = tdf8532_mute, }; -static struct snd_soc_component_driver soc_component_tdf8532; +static const struct snd_soc_component_driver soc_component_tdf8532; static struct snd_soc_dai_driver tdf8532_dai[] = { { @@ -318,11 +306,11 @@ static struct snd_soc_dai_driver tdf8532_dai[] = { }; static int tdf8532_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { int ret; struct tdf8532_priv *dev_data; - struct device *dev = &(i2c->dev); + struct device *dev = &i2c->dev; dev_dbg(&i2c->dev, "%s\n", __func__); @@ -340,7 +328,7 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, dev_data); ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_tdf8532, - tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); if (ret != 0) { dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); goto out; diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h index 6e3f2c147eace9..8818252dcda6fa 100644 --- a/sound/soc/codecs/tdf8532.h +++ b/sound/soc/codecs/tdf8532.h @@ -1,18 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * tdf8532.h - Codec driver for NXP Semiconductors * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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 __TDF8532_H_ #define __TDF8532_H_ @@ -87,7 +78,7 @@ struct get_dev_status_repl { } __packed; /* Helpers */ -#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) +#define CHNL_MASK(channels) (u8)((0x00FF << (channels)) >> 8) #define tdf8532_amp_write(dev_data, ...)\ __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) From 40487fb8807f918374257f32fdc68f023daca95b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 13:38:08 -0500 Subject: [PATCH 090/298] ASoC: Intel: bxt-tdf8532: use Linux style Make checkpatch happy, no functionality change Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bxt_tdf8532.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index abdc04e389a8fb..336205309e2c8b 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Broxton-P I2S Machine Driver for IVI reference platform * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #include @@ -37,7 +29,6 @@ static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { }; static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { - /* Speaker BE connections */ { "Speaker", NULL, "ssp4 Tx"}, { "ssp4 Tx", NULL, "codec0_out"}, @@ -77,7 +68,7 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { }; static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); @@ -218,7 +209,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { #if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bxt_add_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *link) + struct snd_soc_dai_link *link) { link->platform_name = "0000:00:0e.0"; link->nonatomic = 1; From c28b18b97da3230d6f15606e68bf2624c567f8b0 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 6 Jun 2018 16:11:26 +0800 Subject: [PATCH 091/298] ASoC: Intel: bytcr_rt5651: work with sof only with SSP2 AIF1 bytcr_rt5651 can work with ssp0 and ssp2, disable ssp0 support for SOF only support SSP2 AIF1 now. Also disable hard code routing for SSP2 AIF1. Signed-off-by: Pan Xiuli Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bytcr_rt5651.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 987720e203f916..7d2dd3e5768584 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -294,6 +294,7 @@ static const struct snd_soc_dapm_route byt_rt5651_ssp0_aif2_map[] = { }; static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx"}, @@ -301,6 +302,7 @@ static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = { {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif2_map[] = { @@ -708,6 +710,7 @@ static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ static char byt_rt5651_long_name[40]; /* = "bytcr-rt5651-*-spk-*-mic" */ +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static bool is_valleyview(void) { static const struct x86_cpu_id cpu_ids[] = { @@ -719,6 +722,7 @@ static bool is_valleyview(void) return false; return true; } +#endif struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ u64 aif_value; /* 1: AIF1, 2: AIF2 */ @@ -767,6 +771,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) "%s%s", "i2c-", i2c_name); byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* * swap SSP0 if bytcr is detected * (will be overridden if DMI quirk is detected) @@ -825,6 +830,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2; } } +#endif /* check quirks before creating card */ dmi_check_system(byt_rt5651_quirk_table); From 0a73ab761e44d5cf4cfc2182227d1226dfe88e8f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 12 Jun 2018 18:51:28 -0500 Subject: [PATCH 092/298] acpi: blacklist: remove quirk for Dell XPS13 when SOF is enabled Audio works well in I2S mode with SOF (Sound Open Firmware), no need for this quirk Signed-off-by: Pierre-Louis Bossart --- drivers/acpi/blacklist.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 995c4d8922b12e..edf821bc582baa 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -91,6 +91,7 @@ static int __init dmi_enable_rev_override(const struct dmi_system_id *d) static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { #ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* * DELL XPS 13 (2015) switches sound between HDA and I2S * depending on the ACPI _REV callback. If userspace supports @@ -105,6 +106,7 @@ static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"), }, }, +#endif { .callback = dmi_enable_rev_override, .ident = "DELL Precision 5520", From a43386b81d351c0626852c4870beb242f18ce34b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 31 Jul 2018 16:47:49 -0500 Subject: [PATCH 093/298] ASoC: Intel: boards: bxt_da7219_max: remove dependency on dmic-codec Not sure why this was ever needed, there are no GPIO controlling the DMICs. Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bxt_da7219_max98357a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 3aba5bcf806aad..09a36fb3547b4c 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -474,8 +474,8 @@ static struct snd_soc_dai_link broxton_dais[] = { .name = "dmic01", .id = 2, .cpu_dai_name = "DMIC01 Pin", - .codec_name = "dmic-codec", - .codec_dai_name = "dmic-hifi", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", .platform_name = "0000:00:0e.0", .ignore_suspend = 1, .be_hw_params_fixup = broxton_dmic_fixup, From a9bb2975556bd2995dcea05cd7e727390a23a845 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 3 Nov 2017 20:56:42 +0000 Subject: [PATCH 094/298] ALSA: HACK: Fix rmmod crash Something appears to corrupt the runtime->status on second playback... Signed-off-by: Liam Girdwood --- sound/core/pcm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index c352bfb973cc29..eae8a7dc69dcaf 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1160,6 +1160,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) { for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { +#if 0 snd_pcm_stream_lock_irq(substream); if (substream->runtime) { if (snd_pcm_running(substream)) @@ -1171,6 +1172,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) wake_up(&substream->runtime->tsleep); } snd_pcm_stream_unlock_irq(substream); +#endif } } From c46546120955e049cf1e454f4ba2c7292284aaf0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Apr 2018 13:20:32 -0500 Subject: [PATCH 095/298] [NOT FOR UPSTREAM] ASoC: SOF: enable DEBUG by default better to use dynamic debug for products but it's really convenient Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/Makefile | 2 ++ sound/soc/sof/intel/Makefile | 2 ++ sound/soc/sof/xtensa/Makefile | 2 ++ 3 files changed, 6 insertions(+) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index e2250db8baeba6..b8f6c67fbb7b70 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +ccflags-y += -DDEBUG + snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o compressed.o utils.o snd-sof-spi-objs := hw-spi.o diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 76b5ed0580ab43..7b5784d88fd154 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +ccflags-y += -DDEBUG + snd-sof-intel-byt-objs := byt.o snd-sof-intel-hsw-objs := hsw.o snd-sof-intel-bdw-objs := bdw.o diff --git a/sound/soc/sof/xtensa/Makefile b/sound/soc/sof/xtensa/Makefile index cc89c7472a3802..34b27be0f7be07 100644 --- a/sound/soc/sof/xtensa/Makefile +++ b/sound/soc/sof/xtensa/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +ccflags-y += -DDEBUG + snd-sof-xtensa-dsp-objs := core.o obj-$(CONFIG_SND_SOC_SOF_XTENSA) += snd-sof-xtensa-dsp.o From b73d79a0f72905146ff30465518aa7e6f0bc1655 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Thu, 23 Aug 2018 00:32:13 +0800 Subject: [PATCH 096/298] ASoC:codecs: rt5651: fix the compiling warning. fix the rt5651 compiling warning issue. Signed-off-by: Wu Zhigang --- sound/soc/intel/boards/bytcr_rt5651.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 7d2dd3e5768584..4380fdff1f6004 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -738,7 +738,9 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) struct byt_rt5651_private *priv; struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) bool is_bytcr = false; +#endif int ret_val = 0; int dai_index = 0; int i; From 875f4355698bebcf0bb299e510d558972442553b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 May 2018 17:34:45 -0500 Subject: [PATCH 097/298] ASoC: wm8804: Add ACPI support HID made of either Wolfson/CirrusLogic PCI ID + 8804 identifier. Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/wm8804-i2c.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index f27464c2c5bad5..79541960f45d94 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "wm8804.h" @@ -40,17 +41,29 @@ static const struct i2c_device_id wm8804_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); +#if defined(CONFIG_OF) static const struct of_device_id wm8804_of_match[] = { { .compatible = "wlf,wm8804", }, { } }; MODULE_DEVICE_TABLE(of, wm8804_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id wm8804_acpi_match[] = { + { "1AEC8804", 0 }, /* Wolfson PCI ID + part ID */ + { "10138804", 0 }, /* Cirrus Logic PCI ID + part ID */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, wm8804_acpi_match); +#endif static struct i2c_driver wm8804_i2c_driver = { .driver = { .name = "wm8804", .pm = &wm8804_pm, - .of_match_table = wm8804_of_match, + .of_match_table = of_match_ptr(wm8804_of_match), + .acpi_match_table = ACPI_PTR(wm8804_acpi_match), }, .probe = wm8804_i2c_probe, .remove = wm8804_i2c_remove, From 9ccf650c51279ec13c7924343d88e983b4df6660 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 May 2018 18:19:44 -0500 Subject: [PATCH 098/298] ASoC: Intel: boards: initial support for Hifiberry Digiplus w/ WM8804 Based on code from sound/soc/bcm/hifiberry-digi.c in RPI tree TODO: add support for DIGI+ IO (capture) and Digi+ PRO Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_wm8804.c | 263 ++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_wm8804.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 0b67c81a86d048..585d6f6016292b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -261,6 +261,16 @@ config SND_SOC_INTEL_BXT_PCM512x_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXT_WM8804_MACH + tristate "Broxton with Wolfson/Cirrus WM8804 codec for Hifiberry Digi+" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_WM8804_I2C + help + This adds support for ASoC machine driver for Broxton platforms + with Hifiberry Digiplus w/ Wolfson/Cirrus WM8804 I2S 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_BXT_TDF8532_MACH tristate "ASoC Audio driver for BXT with TDF8532 in I2S mode" depends on X86 && ACPI && I2C diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index c85f0b9bd3792c..73f8efb98ee1fc 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o +snd-soc-sst-bxt-wm8804-objs := bxt_wm8804.o snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o @@ -32,6 +33,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_WM8804_MACH) += snd-soc-sst-bxt-wm8804.o obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o diff --git a/sound/soc/intel/boards/bxt_wm8804.c b/sound/soc/intel/boards/bxt_wm8804.c new file mode 100644 index 00000000000000..de6a4669731bd2 --- /dev/null +++ b/sound/soc/intel/boards/bxt_wm8804.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * bxt-wm8804.c - ASoC machine driver for Up and Up2 board + * based on WM8804/Hifiberry Digi+ + * Copyright (c) 2018, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/wm8804.h" +#include "../atom/sst-atom-controls.h" +#include + +#include "../../codecs/wm8804.h" + +static short int auto_shutdown_output; +module_param(auto_shutdown_output, short, 0660); +MODULE_PARM_DESC(auto_shutdown_output, "Shutdown SP/DIF output if playback is stopped"); + +#define CLK_44EN_RATE 22579200UL +#define CLK_48EN_RATE 24576000UL + +static bool snd_rpi_hifiberry_is_digipro; +static struct gpio_desc *snd_rpi_hifiberry_clk44gpio; +static struct gpio_desc *snd_rpi_hifiberry_clk48gpio; + +static int samplerate = 44100; + +static uint32_t snd_rpi_hifiberry_digi_enable_clock(int sample_rate) +{ + switch (sample_rate) { + case 11025: + case 22050: + case 44100: + case 88200: + case 176400: + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio, 1); + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio, 0); + return CLK_44EN_RATE; + default: + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio, 1); + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio, 0); + return CLK_48EN_RATE; + } +} + +static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *codec = rtd->codec_dai->component; + + /* enable TX output */ + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); + + /* Initialize Digi+ Pro hardware */ + if (snd_rpi_hifiberry_is_digipro) { + struct snd_soc_dai_link *dai = rtd->dai_link; + + dai->name = "HiFiBerry Digi+ Pro"; + dai->stream_name = "HiFiBerry Digi+ Pro HiFi"; + } + + return 0; +} + +static int snd_rpi_hifiberry_digi_startup(struct snd_pcm_substream *substream) +{ + /* turn on digital output */ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *codec = rtd->codec_dai->component; + + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x3c, 0x00); + return 0; +} + +static void snd_rpi_hifiberry_digi_shutdown(struct snd_pcm_substream *substream) +{ + /* turn off output */ + if (auto_shutdown_output) { + /* turn off output */ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *codec = rtd->codec_dai->component; + + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x3c, 0x3c); + } +} + +static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *codec = codec_dai->component; + + int sysclk = 27000000; /* This is fixed on this board */ + + long mclk_freq = 0; + int mclk_div = 1; + int sampling_freq = 1; + + int ret; + + samplerate = params_rate(params); + + if (samplerate <= 96000) { + mclk_freq = samplerate * 256; + mclk_div = WM8804_MCLKDIV_256FS; + } else { + mclk_freq = samplerate * 128; + mclk_div = WM8804_MCLKDIV_128FS; + } + + if (snd_rpi_hifiberry_is_digipro) + sysclk = snd_rpi_hifiberry_digi_enable_clock(samplerate); + + switch (samplerate) { + case 32000: + sampling_freq = 0x03; + break; + case 44100: + sampling_freq = 0x00; + break; + case 48000: + sampling_freq = 0x02; + break; + case 88200: + sampling_freq = 0x08; + break; + case 96000: + sampling_freq = 0x0a; + break; + case 176400: + sampling_freq = 0x0c; + break; + case 192000: + sampling_freq = 0x0e; + break; + default: + dev_err(codec->dev, + "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n", + samplerate); + } + + snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div); + snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq); + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL, + sysclk, SND_SOC_CLOCK_OUT); + + if (ret < 0) { + dev_err(codec->dev, + "Failed to set WM8804 SYSCLK: %d\n", ret); + return ret; + } + + /* Enable TX output */ + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); + + /* Power on */ + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x9, 0); + + /* set sampling frequency status bits */ + snd_soc_component_update_bits(codec, WM8804_SPDTX4, 0x0f, + sampling_freq); + + return 0; +} + +/* machine stream operations */ +static struct snd_soc_ops snd_rpi_hifiberry_digi_ops = { + .hw_params = snd_rpi_hifiberry_digi_hw_params, + .startup = snd_rpi_hifiberry_digi_startup, + .shutdown = snd_rpi_hifiberry_digi_shutdown, +}; + +static struct snd_soc_dai_link dailink[] = { + /* CODEC<->CODEC link */ + /* back ends */ + { + .name = "SSP5-Codec", + .id = 0, + .cpu_dai_name = "SSP5 Pin", + .platform_name = "sof-audio", + .no_pcm = 1, + .codec_dai_name = "wm8804-spdif", + .codec_name = "i2c-1AEC8804:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .nonatomic = true, + .dpcm_playback = 1, + .ops = &snd_rpi_hifiberry_digi_ops, + .init = snd_rpi_hifiberry_digi_init, + }, +}; + +/* SoC card */ +static struct snd_soc_card bxt_wm8804_card = { + .name = "bxt-wm8804", + .owner = THIS_MODULE, + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), +}; + + /* i2c-:00 with HID being 8 chars */ +static char codec_name[SND_ACPI_I2C_ID_LEN]; + +static int bxt_wm8804_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; + int dai_index = 0; + int ret_val = 0; + int i; + + mach = (&pdev->dev)->platform_data; + card = &bxt_wm8804_card; + card->dev = &pdev->dev; + + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(dailink); i++) { + if (!strcmp(dailink[i].codec_name, "i2c-1AEC8804: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); + dailink[dai_index].codec_name = codec_name; + } + + ret_val = devm_snd_soc_register_card(&pdev->dev, card); + if (ret_val) { + dev_err(&pdev->dev, + "snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, card); + return ret_val; +} + +static struct platform_driver bxt_wm8804_driver = { + .driver = { + .name = "bxt-wm8804", + }, + .probe = bxt_wm8804_probe, +}; +module_platform_driver(bxt_wm8804_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Broxton + WM8804 Machine driver"); +MODULE_AUTHOR("Pierre-Louis Bossart"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt-wm8804"); From 70c7b0cd7fc3a90366dfc46d8712f5da71d66b59 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Fri, 24 Aug 2018 14:50:24 +0800 Subject: [PATCH 099/298] ASoC:topology:fix the oops bug at topology free stage. 1. because the snd_soc_dapm_route{} is allocated in stack, which cause oops during topology free stage. allocated in the heap instead of allocated in stack by changing its definition in snd_sof_route{} can avoid oops in free stage. 2. the string name is allocated in topology's short life buffer, which cause oops during topology free stage. allocated the string buffer with devm_kstrdup() can avoid oops in free stage. Signed-off-by: Wu Zhigang Suggested-by: Keyon Jie --- sound/soc/sof/pm.c | 6 +++--- sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 19 +++++++++++++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 75c1a71feb7981..ddc8c2d54bc5c9 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -85,10 +85,10 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to load route sink %s control %s source %s\n", - sroute->route->sink, - sroute->route->control ? sroute->route->control + sroute->route.sink, + sroute->route.control ? sroute->route.control : "none", - sroute->route->source); + sroute->route.source); return ret; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d9d62461c06d22..c3431c209e35a2 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -267,7 +267,7 @@ struct snd_sof_widget { struct snd_sof_route { struct snd_sof_dev *sdev; - struct snd_soc_dapm_route *route; + struct snd_soc_dapm_route route; struct list_head list; /* list in sdev route list */ void *private; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e5e6399b3987f0..83bc90c5ea2fbd 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1999,7 +1999,22 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, goto err; } - sroute->route = route; + sroute->route.source = kstrdup(route->source, GFP_KERNEL); + if (!sroute->route.source) + goto err; + + sroute->route.sink = kstrdup(route->sink, GFP_KERNEL); + if (!sroute->route.sink) { + kfree(sroute->route.source); + goto err; + } + + sroute->route.control = kstrdup(route->control, GFP_KERNEL); + if (!sroute->route.control) { + kfree(sroute->route.source); + kfree(sroute->route.sink); + goto err; + } sroute->private = connect; /* add route to route list */ @@ -2175,7 +2190,7 @@ void snd_sof_free_topology(struct snd_sof_dev *sdev) list_for_each_entry_safe(sroute, temp, &sdev->route_list, list) { /* delete dapm route */ - snd_soc_dapm_del_routes(dapm, sroute->route, 1); + snd_soc_dapm_del_routes(dapm, &sroute->route, 1); /* free sroute and its private data */ kfree(sroute->private); From 5baad2e7d63b4ff25a532c56ab856a9463f85c61 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Fri, 24 Aug 2018 17:20:00 +0800 Subject: [PATCH 100/298] ASoC:topology:add the remove function to free route. add the remove function to free route in sof level. Signed-off-by: Wu Zhigang --- sound/soc/sof/topology.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 83bc90c5ea2fbd..1c810108181950 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1890,6 +1890,17 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, return 0; } +/* Used for free route in topology free stage */ +static void sof_route_remove(struct snd_soc_dapm_route *route) +{ + if (!route) + return; + + kfree(route->source); + kfree(route->sink); + kfree(route->control); +} + /* DAI link - used for any driver specific init */ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) @@ -2192,6 +2203,8 @@ void snd_sof_free_topology(struct snd_sof_dev *sdev) /* delete dapm route */ snd_soc_dapm_del_routes(dapm, &sroute->route, 1); + sof_route_remove(&sroute->route); + /* free sroute and its private data */ kfree(sroute->private); kfree(sroute); From 4dbb2f8ebcc225aae38783541e94f1c355999ea9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 20 Aug 2018 17:15:03 +0100 Subject: [PATCH 101/298] ASoC: SOF: core: Use private data in ASoC RTD for SOF Don't use SOF specific variable. Signed-off-by: Liam Girdwood --- include/sound/soc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 198cfd5e990a9a..7b8c06c3e75a06 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1132,7 +1132,6 @@ struct snd_soc_pcm_runtime { /* runtime devices */ struct snd_pcm *pcm; struct snd_compr *compr; - struct snd_sof_pcm *sof; struct snd_soc_codec *codec; struct snd_soc_platform *platform; /* will be removed */ struct snd_soc_dai *codec_dai; @@ -1153,6 +1152,9 @@ struct snd_soc_pcm_runtime { /* bit field */ unsigned int dev_registered:1; unsigned int pop_wait:1; + + /* private data - core does not touch */ + void *private; }; /* mixer control */ From e534d9a1b77cd09ac6c354aea3484a7b5bdc7cf3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 20 Aug 2018 17:15:13 +0100 Subject: [PATCH 102/298] ASoC: SOF: pcm: Use private data in ASoC RTD for SOF Don't use SOF specific variable. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 4f9be5f8f4cf95..8b4ac172b71ca1 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -40,7 +40,7 @@ static int create_page_table(struct snd_pcm_substream *substream, snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); int stream = substream->stream; @@ -58,7 +58,7 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; struct sof_ipc_pcm_params pcm; struct sof_ipc_pcm_params_reply ipc_params_reply; int posn_offset; @@ -177,7 +177,7 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; struct sof_ipc_stream stream; struct sof_ipc_reply reply; int ret; @@ -235,7 +235,7 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; struct sof_ipc_stream stream; struct sof_ipc_reply reply; int ret = 0; @@ -335,7 +335,7 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; snd_pcm_uframes_t host = 0, dai = 0; /* nothing todo for BE */ @@ -362,7 +362,7 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; struct snd_soc_tplg_stream_caps *caps = &spcm->pcm.caps[substream->stream]; @@ -428,7 +428,7 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; /* nothing todo for BE */ if (rtd->dai_link->no_pcm) @@ -475,7 +475,7 @@ static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) rtd->dai_link->id); return 0; } - rtd->sof = spcm; + rtd->private = spcm; dev_dbg(sdev->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); From a7dcfd023aae217fa52ed8e05b28a52482e7ddda Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 20 Aug 2018 17:15:25 +0100 Subject: [PATCH 103/298] ASoC: SOF: compressed: Use private data in ASoC RTD for SOF Don't use SOF specific variable. Signed-off-by: Liam Girdwood --- sound/soc/sof/compressed.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/compressed.c b/sound/soc/sof/compressed.c index ca830c216db0a7..a0e3a35f699a9c 100644 --- a/sound/soc/sof/compressed.c +++ b/sound/soc/sof/compressed.c @@ -29,7 +29,7 @@ static int sof_compressed_open(struct snd_compr_stream *cstream) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; mutex_lock(&spcm->mutex); pm_runtime_get_sync(sdev->dev); @@ -44,7 +44,7 @@ static int sof_compressed_free(struct snd_compr_stream *cstream) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; mutex_lock(&spcm->mutex); pm_runtime_put(sdev->dev); @@ -92,7 +92,7 @@ static int sof_compressed_trigger(struct snd_compr_stream *cstream, int cmd) snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; struct sof_ipc_stream stream; struct sof_ipc_reply reply; @@ -133,7 +133,7 @@ static int sof_compressed_pointer(struct snd_compr_stream *cstream, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm = rtd->sof; + struct snd_sof_pcm *spcm = rtd->private; snd_sof_ipc_stream_posn(sdev, spcm, cstream->direction, &posn); From 55762c876caefce47581f8e113fe093ffc45d264 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:20:26 +0100 Subject: [PATCH 104/298] ASoC: sof: core: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- include/sound/sof.h | 18 +++++++++--------- sound/soc/sof/core.c | 16 ++++++++-------- sound/soc/sof/sof-priv.h | 18 +++++++++--------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 835d0ae4770cf0..6285c6f99ea0b2 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -1,12 +1,12 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #ifndef __INCLUDE_SOUND_SOF_H #define __INCLUDE_SOUND_SOF_H diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index e6e40d4e7f3017..024c8a1f39fd1a 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c3431c209e35a2..bd33e9437d0222 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -1,12 +1,12 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #ifndef __SOUND_SOC_SOF_PRIV_H #define __SOUND_SOC_SOF_PRIV_H From a0838ac86812cdfa5e803218c65fcb4571b18682 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:20:47 +0100 Subject: [PATCH 105/298] ASoC: sof: ops: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/ops.c | 16 ++++++++-------- sound/soc/sof/ops.h | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index d1ea6a0c5f6229..48ea9ac32620b5 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 0d6fa6ef1a3e8b..6574707cd69b11 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -1,12 +1,12 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #ifndef __SOUND_SOC_SOF_IO_H #define __SOUND_SOC_SOF_IO_H From eb55e378d6a1e708e958b7c042e0138d77e7aa19 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:21:17 +0100 Subject: [PATCH 106/298] ASoC: sof: compressed: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/compressed.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/compressed.c b/sound/soc/sof/compressed.c index a0e3a35f699a9c..3cd56fb7465a21 100644 --- a/sound/soc/sof/compressed.c +++ b/sound/soc/sof/compressed.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include From e87dbd3bbe1eb52f31849255389d1d6a3469f263 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:21:28 +0100 Subject: [PATCH 107/298] ASoC: sof: control: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/control.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 5effaea6fe5f97..0727b909b7ab98 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// /* Mixer Controls */ From e7fa83368cc669073e7af4bee399aa377862c6c1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:21:56 +0100 Subject: [PATCH 108/298] ASoC: sof: debug: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/debug.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index c604decd3a6615..4e122fd7b7adfb 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - * Yan Wang - * - * Generic debug routines used to export DSP MMIO and memories to userspace - * for firmware debugging. - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// Yan Wang +// +// Generic debug routines used to export DSP MMIO and memories to userspace +// for firmware debugging. +// #include #include From 1cbab24dd0a8b9823e6a1e5162c6633de5112c38 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:22:37 +0100 Subject: [PATCH 109/298] ASoC: sof: topology: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-topology.h | 19 +++++++++---------- sound/soc/sof/topology.c | 16 ++++++++-------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index ba966cd4fb512c..53d92c7b714d93 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -1,13 +1,12 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - * Keyon Jie - */ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// Author: Liam Girdwood +// Keyon Jie +// /* * Topology IDs and tokens. diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 1c810108181950..e3dbded5db8034 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include From 2308262e1718b12eb91b875f9c6c4b903124f65b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:22:46 +0100 Subject: [PATCH 110/298] ASoC: sof: trace: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/trace.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 172ff2500d5d8b..bd72a12bc7a6ac 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - * Yan Wang - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// Yan Wang +// #include #include From ca9434614026ce95b618510c6889343d6ea15bd8 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:22:55 +0100 Subject: [PATCH 111/298] ASoC: sof: utils: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/utils.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c index 91c8417500af8c..a38cd0c01f80e8 100644 --- a/sound/soc/sof/utils.c +++ b/sound/soc/sof/utils.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Author: Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Keyon Jie +// #include #include From 400156b1c5da7226c0882d29c915c34aa778e55e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:23:05 +0100 Subject: [PATCH 112/298] ASoC: sof: pm: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/pm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index ddc8c2d54bc5c9..61fca38555c5bc 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include From 90004ed5f51dbb371ec81dfbf5b8c22aa7c1ce4c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:23:15 +0100 Subject: [PATCH 113/298] ASoC: sof: pcm: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 8b4ac172b71ca1..59bb7d4c9c0418 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -1,14 +1,14 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - * - * PCM Layer, interface between ALSA and IPC. - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// +// PCM Layer, interface between ALSA and IPC. +// #include #include From 2cae655ba31de768a597fe1991f580777011782f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:23:29 +0100 Subject: [PATCH 114/298] ASoC: sof: loader: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/loader.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 210a8d30989f21..03bbdc342b8ee4 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -1,14 +1,14 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - * - * Generic firmware loader. - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// +// Generic firmware loader. +// #include #include From d61fa45ccaf8947780c83429b8905242450e2db5 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:23:41 +0100 Subject: [PATCH 115/298] ASoC: sof: ipc: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-ipc.h | 20 ++++++++++---------- sound/soc/sof/ipc.c | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index b520e3b24ed6c7..985919995b5f31 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -1,13 +1,13 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - * Keyon Jie - */ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// Keyon Jie +// #ifndef __INCLUDE_UAPI_SOF_IPC_H__ #define __INCLUDE_UAPI_SOF_IPC_H__ diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 02ae60455a9a66..7aa0240ee8491d 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -1,15 +1,15 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - * - * Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided - * by platform driver code. - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// +// Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided +// by platform driver code. +// #include #include From 24bf99ace5505d02c4e29e2ca9281ec60a8afb14 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:23:54 +0100 Subject: [PATCH 116/298] ASoC: sof: nocodec: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/nocodec.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 6fe1fc3a87a954..3ec658f96b3d08 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include From 283ba0c83ef13aec7c311f0aa90ce5a5c686a7bc Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:24:07 +0100 Subject: [PATCH 117/298] ASoC: sof: spi: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/hw-spi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c index e20682da09202c..db5dcea73dee3e 100644 --- a/sound/soc/sof/hw-spi.c +++ b/sound/soc/sof/hw-spi.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// /* * Hardware interface for audio DSPs via SPI From 5d4007bbd763789d955f8333f949d3170001bfaf Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:24:30 +0100 Subject: [PATCH 118/298] ASoC: sof: abi: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-abi.h | 14 +++++++------- include/uapi/sound/sof-eq.h | 18 +++++++++--------- include/uapi/sound/sof-fw.h | 16 +++++++--------- include/uapi/sound/sof-tone.h | 17 ++++++++--------- 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/include/uapi/sound/sof-abi.h b/include/uapi/sound/sof-abi.h index 4e78fa14465e35..ff208d2f7573ee 100644 --- a/include/uapi/sound/sof-abi.h +++ b/include/uapi/sound/sof-abi.h @@ -1,10 +1,10 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - */ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// #ifndef __INCLUDE_UAPI_ABI_H__ #define __INCLUDE_UAPI_ABI_H__ diff --git a/include/uapi/sound/sof-eq.h b/include/uapi/sound/sof-eq.h index 96aefd1e1950b9..f94712667ecee3 100644 --- a/include/uapi/sound/sof-eq.h +++ b/include/uapi/sound/sof-eq.h @@ -1,12 +1,12 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Seppo Ingalsuo - */ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo +// #ifndef EQ_H #define EQ_H diff --git a/include/uapi/sound/sof-fw.h b/include/uapi/sound/sof-fw.h index 2f9bd827d2f02d..3bf86ce00f93f3 100644 --- a/include/uapi/sound/sof-fw.h +++ b/include/uapi/sound/sof-fw.h @@ -1,12 +1,10 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// /* * Firmware file format . diff --git a/include/uapi/sound/sof-tone.h b/include/uapi/sound/sof-tone.h index f9c9bf5c482d85..81f7bc1eca8e88 100644 --- a/include/uapi/sound/sof-tone.h +++ b/include/uapi/sound/sof-tone.h @@ -1,12 +1,11 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Seppo Ingalsuo - */ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// Author: Seppo Ingalsuo +// #ifndef TONE_H #define TONE_H From 13c0a0d142d4932bebe1d110838c190e7745b1a0 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:24:42 +0100 Subject: [PATCH 119/298] ASoC: sof: acpi-dev: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-acpi-dev.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index d635e6bda64bb3..b1bf0ee9c9b427 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include From 245a556873977682966fabe7b69de8a083913cd8 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:24:51 +0100 Subject: [PATCH 120/298] ASoC: sof: spi-dev: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-spi-dev.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index fb94f24b824076..c0e651ce48376c 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include From abfbcb4217a3e25c6477317b9a08449042f9f92f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:25:02 +0100 Subject: [PATCH 121/298] ASoC: sof: pci-dev: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-pci-dev.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index c8bf16f75b8416..c5371faa08e641 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// #include #include From 339d9ed6c43c8f3d9458bb8cb1c189a68e1ee2c6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:25:14 +0100 Subject: [PATCH 122/298] ASoC: sof: bdw: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/bdw.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index ef272108593f64..8ed608ee6e3055 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// /* * Hardwre interface for audio DSP on Haswell and Broadwell From 23528d217df93b91995c9ba37acff5cb8a01bd79 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:25:24 +0100 Subject: [PATCH 123/298] ASoC: sof: byt: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/byt.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 7b6d549b0d439b..14cf6b1082b85e 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// /* * Hardware interface for audio DSP on Baytrail, Braswell and Cherrytrail. From efe9ea3dc75aa281540446c1bd00628ca4db8c98 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:26:10 +0100 Subject: [PATCH 124/298] ASoC: sof: hsw: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hsw.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index c0139855cd6da6..e4d13b952e9c5f 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Liam Girdwood - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// /* * Hardwre interface for audio DSP on Haswell From 8ada60cdf70fbeae90f842d2ef9a3ae026e4afad Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:27:32 +0100 Subject: [PATCH 125/298] ASoC: sof: intekl: apl/cnl: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index a3a814a74742b1..f9f1720c4c3150 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for generic Intel audio DSP HDA IP From e66bc03324a6cc5b8c8f4c333fdda3a8a099aa38 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:27:53 +0100 Subject: [PATCH 126/298] ASoC: sof: intekl: hda-ctrl: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ctrl.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 57b877bde8a8f4..825deb59527dfa 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for generic Intel audio DSP HDA IP From 8f766b9339f25548aecc0c0fbb50aaec868372dc Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:28:26 +0100 Subject: [PATCH 127/298] ASoC: sof: intel: hda-dsp.c: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-dsp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 86d474c083a920..640bf0692413c0 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for generic Intel audio DSP HDA IP From 384e1803c5764645bc30e708b38bc0bea4087da2 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:28:47 +0100 Subject: [PATCH 128/298] ASoC: sof: intel: pcm: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-pcm.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 5511277985284d..fb256a12a8fd5e 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for generic Intel audio DSP HDA IP From 19e2bd948d19344781d054035e977c9bc3e73132 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:28:56 +0100 Subject: [PATCH 129/298] ASoC: sof: intel: stream: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-stream.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index a9e81a505aa6c6..620a023c17384a 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for generic Intel audio DSP HDA IP From ffc1e25d1c2907ee52de1fce57e0c5a9428d5fe2 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:29:04 +0100 Subject: [PATCH 130/298] ASoC: sof: intel: trace: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-trace.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 2b88768cb903bb..273ac6d1a78ec3 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for generic Intel audio DSP HDA IP From 11fec04f00ef343ec89efce746a63bdfc869b245 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:29:18 +0100 Subject: [PATCH 131/298] ASoC: sof: intel: ipc: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ipc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 0fb3f48ef776a6..6e7aaf9235acbe 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for generic Intel audio DSP HDA IP From e5c0f8ded390528fe009e5e1d579ffe6935cf3cb Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:34:46 +0100 Subject: [PATCH 132/298] ASoC: sof: intel: skl, cnl, apl dai: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-dai.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index d5067db0d9f5c6..4cb67bc836657a 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Authors: Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Keyon Jie +// #include #include "../sof-priv.h" From f3a1f5a72e29d7f3f7f7d0de2bf91a73c6c08530 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:34:55 +0100 Subject: [PATCH 133/298] ASoC: sof: intel: loader: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-loader.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index d5476bbdc70d6f..5dc24b9067d278 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for HDA DSP code loader From a7a244075beb22ce9d46dcde50493fb90f791ac2 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 16:35:10 +0100 Subject: [PATCH 134/298] ASoC: sof: intel: skl, cnl, apl diff: update comment header to C++ style. Update the header comment to C++ style re upstream review comments. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/apl.c | 24 ++++++++++++------------ sound/soc/sof/intel/cnl.c | 24 ++++++++++++------------ sound/soc/sof/intel/skl.c | 24 ++++++++++++------------ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index d3816b99498584..3ba4ed5e8582c7 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for audio DSP on Apollolake and GeminiLake diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index f98e699f0d8631..0fb531b08ea3af 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for audio DSP on Cannonlake. diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index 4c1ae35f371897..ab7cd2b689cba9 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Authors: Liam Girdwood - * Ranjani Sridharan - * Jeeja KP - * Rander Wang - * Keyon Jie - */ +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Ranjani Sridharan +// Jeeja KP +// Rander Wang +// Keyon Jie +// /* * Hardware interface for audio DSP on Skylake and Kabylake. From c142507add207c9eec8c1ddcb45c83e6b6b3503f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 22 Aug 2018 17:27:23 +0100 Subject: [PATCH 135/298] ASoC: sof: core: Add comments for compressed page table. Also removed unused page table size function. Signed-off-by: Liam Girdwood --- sound/soc/sof/core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 024c8a1f39fd1a..070a8d3799a8d7 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -124,11 +124,6 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, return NULL; } -static inline unsigned int sof_get_pages(size_t size) -{ - return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; -} - /* * FW Panic/fault handling. */ @@ -191,6 +186,9 @@ EXPORT_SYMBOL(snd_sof_get_status); /* * Generic buffer page table creation. + * Take the each physical page address and drop the least significant unused + * bites from each (based on PAGE_SIZE). Then pack valid page address bits + * into compressed page table. */ int snd_sof_create_page_table(struct snd_sof_dev *sdev, From d58e046f55e5cd4211924378f13a21c7877ebf88 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 24 Aug 2018 14:21:41 +0100 Subject: [PATCH 136/298] ASoC: sof: pcm: check return value of PM calls. Make sure runtime PM return values are checked. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 59bb7d4c9c0418..c34e4af1abaaa9 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -365,6 +365,7 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) struct snd_sof_pcm *spcm = rtd->private; struct snd_soc_tplg_stream_caps *caps = &spcm->pcm.caps[substream->stream]; + int ret; /* nothing todo for BE */ if (rtd->dai_link->no_pcm) @@ -375,7 +376,13 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) mutex_lock(&spcm->mutex); - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: pcm open failed to resume %d\n", + ret); + mutex_unlock(&spcm->mutex); + return ret; + } /* set any runtime constraints based on topology */ snd_pcm_hw_constraint_step(substream->runtime, 0, @@ -429,6 +436,7 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm = rtd->private; + int err; /* nothing todo for BE */ if (rtd->dai_link->no_pcm) @@ -441,7 +449,12 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) mutex_lock(&spcm->mutex); pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); + + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: pcm close failed to idle %d\n", + err); + mutex_unlock(&spcm->mutex); return 0; } @@ -667,7 +680,7 @@ static int sof_pcm_probe(struct snd_soc_component *component) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pdata *plat_data = dev_get_platdata(component->dev); - int ret; + int ret, err; /* load the default topology */ sdev->component = component; @@ -684,7 +697,9 @@ static int sof_pcm_probe(struct snd_soc_component *component) SND_SOF_SUSPEND_DELAY); pm_runtime_use_autosuspend(component->dev); pm_runtime_enable(component->dev); - pm_runtime_idle(component->dev); + err = pm_runtime_idle(component->dev); + if (err < 0) + dev_err(sdev->dev, "error: failed to enter PM idle %d\n", err); err: return ret; From 6e7b81d6af44f111f7e97e98be1c03b0f44adc75 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 24 Aug 2018 14:21:51 +0100 Subject: [PATCH 137/298] ASoC: sof: debug: check return value of PM calls. Make sure runtime PM return values are checked. Signed-off-by: Liam Girdwood --- sound/soc/sof/debug.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 4e122fd7b7adfb..74d6f66eef782d 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -59,9 +59,19 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, return -ENOMEM; /* copy from DSP MMIO */ - pm_runtime_get(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: debugFS failed to resume %zd\n", + ret); + return ret; + } + memcpy_fromio(buf, dfse->buf + pos, size); - pm_runtime_put(sdev->dev); + + ret = pm_runtime_put(sdev->dev); + if (ret < 0) + dev_err(sdev->dev, "error: debugFS failed to idle %zd\n", + ret); /* copy to userspace */ ret = copy_to_user(buffer, buf, count); From 22a564e1487222cfe53816c77b604fdbc8f30d39 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 24 Aug 2018 14:22:19 +0100 Subject: [PATCH 138/298] ASoC: sof: control: check return value of PM calls. Make sure runtime PM return values are checked. Signed-off-by: Liam Girdwood --- sound/soc/sof/control.c | 80 +++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 0727b909b7ab98..03c1a980f141b2 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -49,8 +49,14 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; + int err, ret; - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: volume get failed to resume %d\n", + ret); + return ret; + } /* get all the mixer data from DSP */ snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, @@ -64,7 +70,10 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, scontrol->volume_table, sm->max + 1); pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: volume get failed to idle %d\n", + err); return 0; } @@ -77,8 +86,14 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; + int ret, err; - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: volume put failed to resume %d\n", + ret); + return ret; + } /* update each channel */ for (i = 0; i < channels; i++) { @@ -94,7 +109,10 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, SOF_CTRL_CMD_VOLUME); pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: volume put failed to idle %d\n", + err); return 0; } @@ -107,8 +125,14 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; + int err, ret; - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: enum get failed to resume %d\n", + ret); + return ret; + } /* get all the mixer data from DSP */ snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, @@ -120,7 +144,10 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[i] = cdata->chanv[i].value; pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: enum get failed to idle %d\n", + ret); return 0; } @@ -133,8 +160,14 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; + int ret, err; - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: enum put failed to resume %d\n", + ret); + return ret; + } /* update each channel */ for (i = 0; i < channels; i++) @@ -146,7 +179,10 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, SOF_CTRL_CMD_ENUM); pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: enum put failed to idle %d\n", + err); return 0; } @@ -160,9 +196,14 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; size_t size; - int ret = 0; + int ret, err; - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: bytes get failed to resume %d\n", + ret); + return ret; + } /* get all the mixer data from DSP */ snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_DATA, @@ -180,7 +221,10 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, out: pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: bytes get failed to idle %d\n", + err); return ret; } @@ -193,9 +237,14 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; - int ret = 0; + int ret, err; - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: bytes put failed to resume %d\n", + ret); + return ret; + } if (data->size > be->max) { dev_err(sdev->dev, "error: size too big %d bytes max is %d\n", @@ -213,6 +262,9 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, out: pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: volume get failed to idle %d\n", + err); return ret; } From 3bdd3783af8b1e4f5873f3500dc1e29e814028ba Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 24 Aug 2018 14:22:29 +0100 Subject: [PATCH 139/298] ASoC: sof: compressed: check return value of PM calls. Make sure runtime PM return values are checked. Signed-off-by: Liam Girdwood --- sound/soc/sof/compressed.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/compressed.c b/sound/soc/sof/compressed.c index 3cd56fb7465a21..e747293fe4fac2 100644 --- a/sound/soc/sof/compressed.c +++ b/sound/soc/sof/compressed.c @@ -30,11 +30,15 @@ static int sof_compressed_open(struct snd_compr_stream *cstream) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm = rtd->private; + int ret; mutex_lock(&spcm->mutex); - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) + dev_err(sdev->dev, "error: comp open failed to resume %d\n", + ret); mutex_unlock(&spcm->mutex); - return 0; + return ret; } static int sof_compressed_free(struct snd_compr_stream *cstream) @@ -45,11 +49,15 @@ static int sof_compressed_free(struct snd_compr_stream *cstream) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm = rtd->private; + int err; mutex_lock(&spcm->mutex); - pm_runtime_put(sdev->dev); + err = pm_runtime_put(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: comp close failed to idle %d\n", + err); mutex_unlock(&spcm->mutex); - return 0; + return err; } static int sof_vorbis_set_params(struct snd_compr_stream *cstream, From 7481f7af7ac711504ae52fa9ae5cb5c3322ea91f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 24 Aug 2018 15:29:41 +0100 Subject: [PATCH 140/298] [SQUASHME] ASoC: SOF: pm: remove unused variable. State set but not used. Signed-off-by: Liam Girdwood --- sound/soc/sof/pm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 61fca38555c5bc..bdbd7084e8895a 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -164,10 +164,11 @@ static void sof_suspend_streams(struct snd_sof_dev *sdev) { struct snd_sof_pcm *spcm; struct snd_pcm_substream *substream; - int state, dir; + int dir; /* suspend all running streams */ list_for_each_entry(spcm, &sdev->pcm_list, list) { + mutex_lock(&spcm->mutex); /* suspend running playback stream */ @@ -175,7 +176,7 @@ static void sof_suspend_streams(struct snd_sof_dev *sdev) substream = spcm->stream[dir].substream; if (substream && substream->runtime) { - state = substream->runtime->status->state; + snd_pcm_suspend(substream); /* @@ -190,7 +191,7 @@ static void sof_suspend_streams(struct snd_sof_dev *sdev) substream = spcm->stream[dir].substream; if (substream && substream->runtime) { - state = substream->runtime->status->state; + snd_pcm_suspend(substream); /* From 4a2a987d4fd1d9f33510c00075b57c3054ae2db8 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Tue, 21 Aug 2018 13:25:11 +0800 Subject: [PATCH 141/298] ASoC: SOF: apl: clear BUSY bit after done with received ipc When handle DSP to HOST ipc, BUSY bit should be clear after the IPC is handled by HOST. Otherwise DSP may send another IPC before HOST read the IPC message from the mailbox and handle it. The new come IPC message will cover the old one, and the HOST will miss one IPC messages. Signed-off-by: Pan Xiuli Signed-off-by: Rander Wang --- sound/soc/sof/intel/hda-ipc.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 6e7aaf9235acbe..e00a8ccfaefbe5 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -45,6 +45,12 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) HDA_DSP_REG_HIPCT, HDA_DSP_REG_HIPCT_BUSY, HDA_DSP_REG_HIPCT_BUSY); + + /* unmask BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_BUSY, + HDA_DSP_REG_HIPCCTL_BUSY); } else { /* * set DONE bit - tell DSP we have received the reply msg @@ -197,11 +203,11 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) snd_sof_ipc_msgs_rx(sdev); } - /* clear busy interrupt */ - snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCT, - HDA_DSP_REG_HIPCT_BUSY, - HDA_DSP_REG_HIPCT_BUSY); + /* mask BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_BUSY, 0); + ret = IRQ_HANDLED; } From f40e5817464891d4b6a417672ff7f12f9b801d71 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Tue, 28 Aug 2018 17:39:00 +0800 Subject: [PATCH 142/298] ASoC:topology:regression fix: check pointer before allocation. check the control string pointer before allocation. in the upper function, sometimes this pointer will be set NULL value. Signed-off-by: Wu Zhigang --- sound/soc/sof/topology.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e3dbded5db8034..32bef181dde9f7 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2020,12 +2020,15 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, goto err; } - sroute->route.control = kstrdup(route->control, GFP_KERNEL); - if (!sroute->route.control) { - kfree(sroute->route.source); - kfree(sroute->route.sink); - goto err; + if (route->control) { + sroute->route.control = kstrdup(route->control, GFP_KERNEL); + if (!sroute->route.control) { + kfree(sroute->route.source); + kfree(sroute->route.sink); + goto err; + } } + sroute->private = connect; /* add route to route list */ From f9c923e5ca9b38df7ddc71f26b55eb2bd1ffed46 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 28 Aug 2018 14:21:58 +0100 Subject: [PATCH 143/298] ASoC: core: Fix merge issue and remove codec,platform runtime pointers Fix a merge issue and remove some pointers from snd_soc_pcm_runtime. Signed-off-by: Liam Girdwood --- include/sound/soc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 7b8c06c3e75a06..16e8fe6f4575a8 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1132,8 +1132,6 @@ struct snd_soc_pcm_runtime { /* runtime devices */ struct snd_pcm *pcm; struct snd_compr *compr; - struct snd_soc_codec *codec; - struct snd_soc_platform *platform; /* will be removed */ struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; From a31aa5f7ecfbe3107ec5b037d4c8f41db61d0c24 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 1 Jun 2018 22:53:49 -0500 Subject: [PATCH 144/298] ALSA: hdac: Remove usage of struct hdac_ext_device and use hdac_device instead This patch removes the hdac_ext_device structure. The legacy and enhanced HDaudio capabilities can be handled in a backward-compatible way without separate definitions. Follow-up patches in this series handle the bus and driver definitions. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/hdaudio_ext.h | 36 +--- sound/hda/ext/hdac_ext_bus.c | 25 +-- sound/soc/codecs/hdac_hdmi.c | 396 +++++++++++++++++------------------ 3 files changed, 204 insertions(+), 253 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 9c14e21dda85cf..c1a5ad0e6e3983 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -181,48 +181,20 @@ struct hda_dai_map { u32 maxbps; }; -#define HDA_MAX_NIDS 16 - -/** - * struct hdac_ext_device - HDAC Ext device - * - * @hdac: hdac core device - * @nid_list - the dai map which matches the dai-name with the nid - * @map_cur_idx - the idx in use in dai_map - * @ops - the hda codec ops common to all codec drivers - * @pvt_data - private data, for asoc contains asoc codec object - */ -struct hdac_ext_device { - struct hdac_device hdev; - struct hdac_ext_bus *ebus; - - /* soc-dai to nid map */ - struct hda_dai_map nid_list[HDA_MAX_NIDS]; - unsigned int map_cur_idx; - - /* codec ops */ - struct hdac_ext_codec_ops ops; - - struct snd_card *card; - void *scodec; - void *private_data; -}; - struct hdac_ext_dma_params { u32 format; u8 stream_tag; }; -#define to_ehdac_device(dev) (container_of((dev), \ - struct hdac_ext_device, hdev)) + /* * HD-audio codec base driver */ struct hdac_ext_driver { struct hdac_driver hdac; - int (*probe)(struct hdac_ext_device *dev); - int (*remove)(struct hdac_ext_device *dev); - void (*shutdown)(struct hdac_ext_device *dev); + int (*probe)(struct hdac_device *dev); + int (*remove)(struct hdac_device *dev); + void (*shutdown)(struct hdac_device *dev); }; int snd_hda_ext_driver_register(struct hdac_ext_driver *drv); diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 0daf31383084d2..0e4823fdd41197 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -137,17 +137,16 @@ static void default_release(struct device *dev) */ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) { - struct hdac_ext_device *edev; struct hdac_device *hdev = NULL; struct hdac_bus *bus = ebus_to_hbus(ebus); char name[15]; int ret; - edev = kzalloc(sizeof(*edev), GFP_KERNEL); - if (!edev) + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (!hdev) return -ENOMEM; - hdev = &edev->hdev; - edev->ebus = ebus; + + hdev->bus = bus; snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); @@ -176,10 +175,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); */ void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) { - struct hdac_ext_device *edev = to_ehdac_device(hdev); - snd_hdac_device_exit(hdev); - kfree(edev); + kfree(hdev); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); @@ -212,27 +209,25 @@ static inline struct hdac_ext_driver *get_edrv(struct device *dev) return edrv; } -static inline struct hdac_ext_device *get_edev(struct device *dev) +static inline struct hdac_device *get_hdev(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); - struct hdac_ext_device *edev = to_ehdac_device(hdev); - - return edev; + return hdev; } static int hda_ext_drv_probe(struct device *dev) { - return (get_edrv(dev))->probe(get_edev(dev)); + return (get_edrv(dev))->probe(get_hdev(dev)); } static int hdac_ext_drv_remove(struct device *dev) { - return (get_edrv(dev))->remove(get_edev(dev)); + return (get_edrv(dev))->remove(get_hdev(dev)); } static void hdac_ext_drv_shutdown(struct device *dev) { - return (get_edrv(dev))->shutdown(get_edev(dev)); + return (get_edrv(dev))->shutdown(get_hdev(dev)); } /** diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 84f7a7a36e4b56..f1e235817a6596 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -85,7 +85,7 @@ struct hdac_hdmi_pin { bool mst_capable; struct hdac_hdmi_port *ports; int num_ports; - struct hdac_ext_device *edev; + struct hdac_device *hdev; }; struct hdac_hdmi_port { @@ -126,6 +126,9 @@ struct hdac_hdmi_drv_data { }; struct hdac_hdmi_priv { + struct hdac_device *hdev; + struct snd_soc_component *component; + struct snd_card *card; struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS]; struct list_head pin_list; struct list_head cvt_list; @@ -139,7 +142,7 @@ struct hdac_hdmi_priv { struct snd_soc_dai_driver *dai_drv; }; -#define hdev_to_hdmi_priv(_hdev) ((to_ehdac_device(_hdev))->private_data) +#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev) static struct hdac_hdmi_pcm * hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, @@ -158,7 +161,7 @@ hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port, bool is_connect) { - struct hdac_ext_device *edev = port->pin->edev; + struct hdac_device *hdev = port->pin->hdev; if (is_connect) snd_soc_dapm_enable_pin(port->dapm, port->jack_pin); @@ -172,7 +175,7 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, * ports. */ if (pcm->jack_event == 0) { - dev_dbg(&edev->hdev.dev, + dev_dbg(&hdev->dev, "jack report for pcm=%d\n", pcm->pcm_id); snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT, @@ -198,19 +201,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 *edev, hda_nid_t nid) +static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid) { unsigned int caps; unsigned int type, param; - caps = get_wcaps(&edev->hdev, nid); + caps = get_wcaps(hdev, nid); type = get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) return 0; - param = snd_hdac_read_parm_uncached(&edev->hdev, nid, - AC_PAR_DEVLIST_LEN); + param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN); if (param == -1) return param; @@ -222,10 +224,10 @@ static int hdac_hdmi_get_port_len(struct hdac_ext_device *edev, 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 *edev, +static int hdac_hdmi_port_select_get(struct hdac_device *hdev, struct hdac_hdmi_port *port) { - return snd_hdac_codec_read(&edev->hdev, port->pin->nid, + return snd_hdac_codec_read(hdev, port->pin->nid, 0, AC_VERB_GET_DEVICE_SEL, 0); } @@ -233,7 +235,7 @@ static int hdac_hdmi_port_select_get(struct hdac_ext_device *edev, * 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 *edev, +static int hdac_hdmi_port_select_set(struct hdac_device *hdev, struct hdac_hdmi_port *port) { int num_ports; @@ -242,8 +244,7 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev, return 0; /* AC_PAR_DEVLIST_LEN is 0 based. */ - num_ports = hdac_hdmi_get_port_len(edev, port->pin->nid); - + num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid); if (num_ports < 0) return -EIO; /* @@ -253,13 +254,13 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev, if (num_ports + 1 < port->id) return 0; - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_DEVICE_SEL, port->id); - if (port->id != hdac_hdmi_port_select_get(edev, port)) + if (port->id != hdac_hdmi_port_select_get(hdev, port)) return -EIO; - dev_dbg(&edev->hdev.dev, "Selected the port=%d\n", port->id); + dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id); return 0; } @@ -277,13 +278,6 @@ static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, return NULL; } -static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) -{ - struct hdac_device *hdev = dev_to_hdac_dev(dev); - - return to_ehdac_device(hdev); -} - static unsigned int sad_format(const u8 *sad) { return ((sad[0] >> 0x3) & 0x1f); @@ -324,15 +318,13 @@ static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime, } static void -hdac_hdmi_set_dip_index(struct hdac_ext_device *edev, hda_nid_t pin_nid, +hdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid, int packet_index, int byte_index) { int val; val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hdac_codec_write(&edev->hdev, pin_nid, 0, - AC_VERB_SET_HDMI_DIP_INDEX, val); + snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); } struct dp_audio_infoframe { @@ -347,14 +339,14 @@ struct dp_audio_infoframe { u8 LFEPBL01_LSV36_DM_INH7; }; -static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev, +static int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev, 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 = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_cvt *cvt = pcm->cvt; u8 *dip; int ret; @@ -363,11 +355,11 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev, u8 conn_type; int channels, ca; - ca = snd_hdac_channel_allocation(&edev->hdev, port->eld.info.spk_alloc, + ca = snd_hdac_channel_allocation(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(&edev->hdev, cvt->nid, channels); + hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels); snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, pcm->channels, pcm->chmap, pcm->chmap_set); @@ -400,32 +392,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev, break; default: - dev_err(&edev->hdev.dev, "Invalid connection type: %d\n", - conn_type); + dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type); return -EIO; } /* stop infoframe transmission */ - hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&edev->hdev, pin->nid, 0, + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); /* Fill infoframe. Index auto-incremented */ - hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { for (i = 0; i < sizeof(buffer); i++) - snd_hdac_codec_write(&edev->hdev, pin->nid, 0, + snd_hdac_codec_write(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(&edev->hdev, pin->nid, 0, + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_DATA, dip[i]); } /* Start infoframe */ - hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&edev->hdev, pin->nid, 0, + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); return 0; @@ -435,12 +426,12 @@ static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { - 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_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdev.dev, "%s: strm_tag: %d\n", __func__, tx_mask); + dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, tx_mask); dai_map = &hdmi->dai_map[dai->id]; @@ -455,8 +446,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 *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_port *port; struct hdac_hdmi_pcm *pcm; @@ -469,7 +460,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(&edev->hdev.dev, + dev_err(&hdev->dev, "device is not configured for this pin:port%d:%d\n", port->pin->nid, port->id); return -ENODEV; @@ -489,28 +480,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 *edev, +static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev, struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - if (!(get_wcaps(&edev->hdev, pin->nid) & AC_WCAP_CONN_LIST)) { - dev_warn(&edev->hdev.dev, + if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdev->dev, "HDMI: pin %d wcaps %#x does not support connection list\n", - pin->nid, get_wcaps(&edev->hdev, pin->nid)); + pin->nid, get_wcaps(hdev, pin->nid)); return -EINVAL; } - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; - port->num_mux_nids = snd_hdac_get_connections(&edev->hdev, pin->nid, + port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid, port->mux_nids, HDA_MAX_CONNECTIONS); if (port->num_mux_nids == 0) - dev_warn(&edev->hdev.dev, + dev_warn(&hdev->dev, "No connections found for pin:port %d:%d\n", pin->nid, port->id); - dev_dbg(&edev->hdev.dev, "num_mux_nids %d for pin:port %d:%d\n", + dev_dbg(&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; @@ -526,7 +517,7 @@ static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *edev, * connected. */ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( - struct hdac_ext_device *edev, + struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, struct hdac_hdmi_cvt *cvt) { @@ -541,7 +532,7 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( list_for_each_entry(port, &pcm->port_list, head) { mutex_lock(&pcm->lock); - ret = hdac_hdmi_query_port_connlist(edev, + ret = hdac_hdmi_query_port_connlist(hdev, port->pin, port); mutex_unlock(&pcm->lock); if (ret < 0) @@ -568,8 +559,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 *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_port *port; @@ -578,7 +569,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(edev, hdmi, cvt); + port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); /* * To make PA and other userland happy. @@ -589,7 +580,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { - dev_warn(&edev->hdev.dev, + dev_warn(&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); @@ -611,8 +602,7 @@ 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 *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; @@ -695,10 +685,10 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, route->connected = handler; } -static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, +static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev, struct hdac_hdmi_port *port) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = NULL; struct hdac_hdmi_port *p; @@ -715,33 +705,32 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, return NULL; } -static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, +static void hdac_hdmi_set_power_state(struct hdac_device *hdev, hda_nid_t nid, unsigned int pwr_state) { int count; unsigned int state; - if (get_wcaps(&edev->hdev, nid) & AC_WCAP_POWER) { - if (!snd_hdac_check_power_state(&edev->hdev, nid, pwr_state)) { + if (get_wcaps(hdev, nid) & AC_WCAP_POWER) { + if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) { for (count = 0; count < 10; count++) { - snd_hdac_codec_read(&edev->hdev, nid, 0, + snd_hdac_codec_read(hdev, nid, 0, AC_VERB_SET_POWER_STATE, pwr_state); - state = snd_hdac_sync_power_state(&edev->hdev, + state = snd_hdac_sync_power_state(hdev, nid, pwr_state); if (!(state & AC_PWRST_ERROR)) break; } } - } } -static void hdac_hdmi_set_amp(struct hdac_ext_device *edev, +static void hdac_hdmi_set_amp(struct hdac_device *hdev, hda_nid_t nid, int val) { - if (get_wcaps(&edev->hdev, nid) & AC_WCAP_OUT_AMP) - snd_hdac_codec_write(&edev->hdev, nid, 0, + if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) + snd_hdac_codec_write(hdev, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); } @@ -750,40 +739,40 @@ static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); - pcm = hdac_hdmi_get_pcm(edev, port); + pcm = hdac_hdmi_get_pcm(hdev, port); if (!pcm) return -EIO; /* set the device if pin is mst_capable */ - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; switch (event) { case SND_SOC_DAPM_PRE_PMU: - hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D0); + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0); /* Enable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_UNMUTE); + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE); - return hdac_hdmi_setup_audio_infoframe(edev, pcm, port); + return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); case SND_SOC_DAPM_POST_PMD: - hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_MUTE); + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE); /* Disable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D3); + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3); break; } @@ -795,11 +784,11 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_cvt *cvt = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt); @@ -808,29 +797,29 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0); + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0); /* Enable transmission */ - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_1, 1); /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0); - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, pcm->format); break; case SND_SOC_DAPM_POST_PMD: - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); - hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3); + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3); break; } @@ -842,10 +831,10 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); int mux_idx; - dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); if (!kc) @@ -854,11 +843,11 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, mux_idx = dapm_kcontrol_get_value(kc); /* set the device if pin is mst_capable */ - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; if (mux_idx > 0) { - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); } @@ -877,8 +866,8 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(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 = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = NULL; const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; @@ -931,12 +920,12 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, * care of selecting the right one and leaving all other inputs selected to * "NONE" */ -static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, +static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev, struct hdac_hdmi_port *port, struct snd_soc_dapm_widget *widget, const char *widget_name) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin = port->pin; struct snd_kcontrol_new *kc; struct hdac_hdmi_cvt *cvt; @@ -948,17 +937,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->hdev.dev, sizeof(*kc), GFP_KERNEL); + kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL); if (!kc) return -ENOMEM; - se = devm_kzalloc(&edev->hdev.dev, sizeof(*se), GFP_KERNEL); + se = devm_kzalloc(&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->hdev.dev, kc_name, GFP_KERNEL); + kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL); if (!kc->name) return -ENOMEM; @@ -976,35 +965,35 @@ 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->hdev.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&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->hdev.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); if (!items[i]) return -ENOMEM; } - se->texts = devm_kmemdup(&edev->hdev.dev, items, + se->texts = devm_kmemdup(&hdev->dev, items, (num_items * sizeof(char *)), GFP_KERNEL); if (!se->texts) return -ENOMEM; - return hdac_hdmi_fill_widget_info(&edev->hdev.dev, widget, + return hdac_hdmi_fill_widget_info(&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); } /* Add cvt <- input <- mux route map */ -static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev, +static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev, struct snd_soc_dapm_widget *widgets, struct snd_soc_dapm_route *route, int rindex) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); const struct snd_kcontrol_new *kc; struct soc_enum *se; int mux_index = hdmi->num_cvt + hdmi->num_ports; @@ -1046,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 = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv; char widget_name[NAME_SIZE]; struct hdac_hdmi_cvt *cvt; @@ -1099,7 +1088,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) for (j = 0; j < pin->num_ports; j++) { sprintf(widget_name, "Pin%d-Port%d Mux", pin->nid, pin->ports[j].id); - ret = hdac_hdmi_create_pin_port_muxs(edev, + ret = hdac_hdmi_create_pin_port_muxs(hdev, &pin->ports[j], &widgets[i], widget_name); if (ret < 0) @@ -1134,7 +1123,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) } } - hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i); + hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i); snd_soc_dapm_new_controls(dapm, widgets, ((2 * hdmi->num_ports) + hdmi->num_cvt)); @@ -1146,9 +1135,9 @@ 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) +static int hdac_hdmi_init_dai_map(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; int dai_id = 0; @@ -1164,7 +1153,7 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) dai_id++; if (dai_id == HDA_MAX_CVTS) { - dev_warn(&edev->hdev.dev, + dev_warn(&hdev->dev, "Max dais supported: %d\n", dai_id); break; } @@ -1173,9 +1162,9 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) return 0; } -static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) +static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_cvt *cvt; char name[NAME_SIZE]; @@ -1190,10 +1179,10 @@ 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->hdev, cvt); + return hdac_hdmi_query_cvt_params(hdev, cvt); } -static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, +static int hdac_hdmi_parse_eld(struct hdac_device *hdev, struct hdac_hdmi_port *port) { unsigned int ver, mnl; @@ -1202,7 +1191,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->hdev.dev, "HDMI: Unknown ELD version %d\n", ver); + dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver); return -EINVAL; } @@ -1210,7 +1199,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->hdev.dev, "HDMI: MNL Invalid %d\n", mnl); + dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl); return -EINVAL; } @@ -1222,8 +1211,8 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, 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 = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = pin->hdev; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; int size = 0; int port_id = -1; @@ -1241,14 +1230,14 @@ 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->hdev, pin->nid, port_id, + size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, &port->eld.monitor_present, port->eld.eld_buffer, ELD_MAX_SIZE); if (size > 0) { size = min(size, ELD_MAX_SIZE); - if (hdac_hdmi_parse_eld(edev, port) < 0) + if (hdac_hdmi_parse_eld(hdev, port) < 0) size = -EINVAL; } @@ -1260,11 +1249,11 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, port->eld.eld_size = 0; } - pcm = hdac_hdmi_get_pcm(edev, port); + pcm = hdac_hdmi_get_pcm(hdev, port); if (!port->eld.monitor_present || !port->eld.eld_valid) { - dev_err(&edev->hdev.dev, "%s: disconnect for pin:port %d:%d\n", + dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", __func__, pin->nid, port->id); /* @@ -1316,9 +1305,9 @@ static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, return 0; } -static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) +static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin; int ret; @@ -1328,7 +1317,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) pin->nid = nid; pin->mst_capable = false; - pin->edev = edev; + pin->hdev = hdev; ret = hdac_hdmi_add_ports(hdmi, pin); if (ret < 0) return ret; @@ -1459,15 +1448,14 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev, * Parse all nodes and store the cvt/pin nids in array * Add one time initialization for pin and cvt widgets */ -static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, +static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, struct snd_soc_dai_driver **dais, int *num_dais) { hda_nid_t nid; int i, num_nodes; 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; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int ret; hdac_hdmi_skl_enable_all_pins(hdev); @@ -1492,13 +1480,13 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, switch (type) { case AC_WID_AUD_OUT: - ret = hdac_hdmi_add_cvt(edev, nid); + ret = hdac_hdmi_add_cvt(hdev, nid); if (ret < 0) goto free_widgets; break; case AC_WID_PIN: - ret = hdac_hdmi_add_pin(edev, nid); + ret = hdac_hdmi_add_pin(hdev, nid); if (ret < 0) goto free_widgets; break; @@ -1518,7 +1506,7 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, } *num_dais = hdmi->num_cvt; - ret = hdac_hdmi_init_dai_map(edev); + ret = hdac_hdmi_init_dai_map(hdev); if (ret < 0) goto free_widgets; @@ -1544,17 +1532,17 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) { - struct hdac_ext_device *edev = aptr; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = aptr; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin = NULL; struct hdac_hdmi_port *hport = NULL; - struct snd_soc_component *component = edev->scodec; + struct snd_soc_component *component = hdmi->component; int i; /* Don't know how this mapping is derived */ hda_nid_t pin_nid = port + 0x04; - dev_dbg(&edev->hdev.dev, "%s: for pin:%d port=%d\n", __func__, + dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, pin_nid, pipe); /* @@ -1567,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->hdev.in_pm)) + if (atomic_read(&hdev->in_pm)) return; list_for_each_entry(pin, &hdmi->pin_list, head) { @@ -1614,15 +1602,15 @@ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, /* create jack pin kcontrols */ static int create_fill_jack_kcontrols(struct snd_soc_card *card, - struct hdac_ext_device *edev) + struct hdac_device *hdev) { struct hdac_hdmi_pin *pin; struct snd_kcontrol_new *kc; char kc_name[NAME_SIZE], xname[NAME_SIZE]; char *name; int i = 0, j; - struct snd_soc_component *component = edev->scodec; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct snd_soc_component *component = hdmi->component; kc = devm_kcalloc(component->dev, hdmi->num_ports, sizeof(*kc), GFP_KERNEL); @@ -1659,8 +1647,8 @@ static int create_fill_jack_kcontrols(struct snd_soc_card *card, int hdac_hdmi_jack_port_init(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm) { - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_pin *pin; struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *route; @@ -1715,7 +1703,7 @@ int hdac_hdmi_jack_port_init(struct snd_soc_component *component, return ret; /* Add Jack Pin switch Kcontrol */ - ret = create_fill_jack_kcontrols(dapm->card, edev); + ret = create_fill_jack_kcontrols(dapm->card, hdev); if (ret < 0) return ret; @@ -1735,8 +1723,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, struct snd_soc_jack *jack) { struct snd_soc_component *component = dai->component; - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_pcm *pcm; struct snd_pcm *snd_pcm; int err; @@ -1758,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->hdev.dev, + dev_err(&hdev->dev, "chmap control add failed with err: %d for pcm: %d\n", err, device); kfree(pcm); @@ -1772,7 +1760,7 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, } EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); -static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, +static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { int i; @@ -1781,7 +1769,7 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, list_for_each_entry(pin, &hdmi->pin_list, head) { if (detect_pin_caps) { - if (hdac_hdmi_get_port_len(edev, pin->nid) == 0) + if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0) pin->mst_capable = false; else pin->mst_capable = true; @@ -1798,68 +1786,68 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, static int hdmi_codec_probe(struct snd_soc_component *component) { - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct hdac_ext_link *hlink = NULL; int ret; - edev->scodec = component; + hdmi->component = component; /* * 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->hdev.dev)); + hlink = snd_hdac_ext_bus_get_link(hbus_to_ebus(hdev->bus), + dev_name(&hdev->dev)); if (!hlink) { - dev_err(&edev->hdev.dev, "hdac link not found\n"); + dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(edev->ebus, hlink); + snd_hdac_ext_bus_link_get(hbus_to_ebus(hdev->bus), hlink); ret = create_fill_widget_route_map(dapm); if (ret < 0) return ret; - aops.audio_ptr = edev; + aops.audio_ptr = hdev; ret = snd_hdac_i915_register_notifier(&aops); if (ret < 0) { - dev_err(&edev->hdev.dev, "notifier register failed: err: %d\n", - ret); + dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret); return ret; } - hdac_hdmi_present_sense_all_pins(edev, hdmi, true); + hdac_hdmi_present_sense_all_pins(hdev, hdmi, true); /* Imp: Store the card pointer in hda_codec */ - edev->card = dapm->card->snd_card; + hdmi->card = dapm->card->snd_card; /* * 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->hdev.dev); - pm_runtime_put(&edev->hdev.dev); - pm_runtime_suspend(&edev->hdev.dev); + pm_runtime_enable(&hdev->dev); + pm_runtime_put(&hdev->dev); + pm_runtime_suspend(&hdev->dev); return 0; } static void hdmi_codec_remove(struct snd_soc_component *component) { - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; - pm_runtime_disable(&edev->hdev.dev); + pm_runtime_disable(&hdev->dev); } #ifdef CONFIG_PM static int hdmi_codec_prepare(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); - pm_runtime_get_sync(&edev->hdev.dev); + pm_runtime_get_sync(&hdev->dev); /* * Power down afg. @@ -1876,16 +1864,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 = hdev_to_hdmi_priv(&edev->hdev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); /* Power up afg */ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - hdac_hdmi_skl_enable_all_pins(&edev->hdev); - hdac_hdmi_skl_enable_dp12(&edev->hdev); + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); /* * As the ELD notify callback request is not entertained while the @@ -1893,9 +1880,9 @@ static void hdmi_codec_complete(struct device *dev) * all pins here. pin capablity change is not support, so use the * already set pin caps. */ - hdac_hdmi_present_sense_all_pins(edev, hdmi, false); + hdac_hdmi_present_sense_all_pins(hdev, hdmi, false); - pm_runtime_put_sync(&edev->hdev.dev); + pm_runtime_put_sync(&hdev->dev); } #else #define hdmi_codec_prepare NULL @@ -1922,7 +1909,6 @@ static void hdac_hdmi_get_chmap(struct hdac_device *hdev, 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(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; @@ -1938,7 +1924,7 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); list_for_each_entry(port, &pcm->port_list, head) if (prepared) - hdac_hdmi_setup_audio_infoframe(edev, pcm, port); + hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); mutex_unlock(&pcm->lock); } @@ -1987,10 +1973,9 @@ static struct hdac_hdmi_drv_data intel_drv_data = { .vendor_nid = INTEL_VENDOR_NID, }; -static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) +static int hdac_hdmi_dev_probe(struct hdac_device *hdev) { - struct hdac_device *hdev = &edev->hdev; - struct hdac_hdmi_priv *hdmi_priv; + struct hdac_hdmi_priv *hdmi_priv = NULL; struct snd_soc_dai_driver *hdmi_dais = NULL; struct hdac_ext_link *hlink = NULL; int num_dais = 0; @@ -1999,24 +1984,25 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) 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->hdev.dev)); + hlink = snd_hdac_ext_bus_get_link(hbus_to_ebus(hdev->bus), + dev_name(&hdev->dev)); if (!hlink) { - dev_err(&edev->hdev.dev, "hdac link not found\n"); + dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(edev->ebus, hlink); + snd_hdac_ext_bus_link_get(hbus_to_ebus(hdev->bus), hlink); 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(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; hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + hdmi_priv->hdev = hdev; if (!hdac_id) return -ENODEV; @@ -2027,7 +2013,7 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) else hdmi_priv->drv_data = &intel_drv_data; - dev_set_drvdata(&hdev->dev, edev); + dev_set_drvdata(&hdev->dev, hdmi_priv); INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); @@ -2038,15 +2024,15 @@ 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->hdev.bus, true); + ret = snd_hdac_display_power(hdev->bus, true); if (ret < 0) { - dev_err(&edev->hdev.dev, + dev_err(&hdev->dev, "Cannot turn on display power on i915 err: %d\n", ret); return ret; } - ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); + ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais); if (ret < 0) { dev_err(&hdev->dev, "Failed in parse and map nid with err: %d\n", ret); @@ -2058,14 +2044,14 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, hdmi_dais, num_dais); - snd_hdac_ext_bus_link_put(edev->ebus, hlink); + snd_hdac_ext_bus_link_put(hbus_to_ebus(hdev->bus), hlink); return ret; } -static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) +static int hdac_hdmi_dev_remove(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin, *pin_next; struct hdac_hdmi_cvt *cvt, *cvt_next; struct hdac_hdmi_pcm *pcm, *pcm_next; @@ -2105,8 +2091,7 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) #ifdef CONFIG_PM static int hdac_hdmi_runtime_suspend(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; @@ -2129,7 +2114,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) AC_PWRST_D3); err = snd_hdac_display_power(bus, false); if (err < 0) { - dev_err(bus->dev, "Cannot turn on display power on i915\n"); + dev_err(dev, "Cannot turn on display power on i915\n"); return err; } @@ -2146,8 +2131,7 @@ 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 *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; @@ -2169,12 +2153,12 @@ static int hdac_hdmi_runtime_resume(struct device *dev) err = snd_hdac_display_power(bus, true); if (err < 0) { - dev_err(bus->dev, "Cannot turn on display power on i915\n"); + dev_err(dev, "Cannot turn on display power on i915\n"); return err; } - hdac_hdmi_skl_enable_all_pins(&edev->hdev); - hdac_hdmi_skl_enable_dp12(&edev->hdev); + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); /* Power up afg */ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, From cc6e80ef812bfbdae7ad88bbfd099f91a100f32e Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 1 Jun 2018 22:53:50 -0500 Subject: [PATCH 145/298] ALSA: hdac: Remove usage of struct hdac_ext_bus and use hdac_bus instead This patch removes the hdac_ext_bus structure. The legacy and enhanced HDaudio capabilities can be handled in a backward-compatible way without separate definitions. Follow-up patches in this series handle the driver definition. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 15 ++ include/sound/hdaudio_ext.h | 74 ++++------ sound/hda/ext/hdac_ext_bus.c | 27 ++-- sound/hda/ext/hdac_ext_controller.c | 55 ++++---- sound/hda/ext/hdac_ext_stream.c | 104 ++++++-------- sound/soc/codecs/hdac_hdmi.c | 22 ++- sound/soc/intel/skylake/skl-messages.c | 50 +++---- sound/soc/intel/skylake/skl-nhlt.c | 8 +- sound/soc/intel/skylake/skl-pcm.c | 112 ++++++++------- sound/soc/intel/skylake/skl-topology.c | 20 ++- sound/soc/intel/skylake/skl-topology.h | 6 +- sound/soc/intel/skylake/skl.c | 184 +++++++++++-------------- sound/soc/intel/skylake/skl.h | 7 +- 13 files changed, 308 insertions(+), 376 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index c052afc2754721..9735b51aef0896 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -250,6 +250,11 @@ struct hdac_rb { * @mlcap: MultiLink capabilities pointer * @gtscap: gts capabilities pointer * @drsmcap: dma resume capabilities pointer + * @num_streams: streams supported + * @idx: HDA link index + * @hlink_list: link list of HDA links + * @lock: lock for link mgmt + * @cmd_dma_state: state of cmd DMAs: CORB and RIRB */ struct hdac_bus { struct device *dev; @@ -317,6 +322,16 @@ struct hdac_bus { /* i915 component interface */ struct i915_audio_component *audio_component; int i915_power_refcount; + + /* parameters required for enhanced capabilities */ + int num_streams; + int idx; + + struct list_head hlink_list; + + struct mutex lock; + bool cmd_dma_state; + }; int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index c1a5ad0e6e3983..e5b0cd1ade19c2 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -4,38 +4,14 @@ #include -/** - * hdac_ext_bus: HDAC extended bus for extended HDA caps - * - * @bus: hdac bus - * @num_streams: streams supported - * @hlink_list: link list of HDA links - * @lock: lock for link mgmt - * @cmd_dma_state: state of cmd DMAs: CORB and RIRB - */ -struct hdac_ext_bus { - struct hdac_bus bus; - int num_streams; - int idx; - - struct list_head hlink_list; - - struct mutex lock; - bool cmd_dma_state; -}; - -int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev, +int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, const struct hdac_io_ops *io_ops); -void snd_hdac_ext_bus_exit(struct hdac_ext_bus *sbus); -int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *sbus, int addr); +void snd_hdac_ext_bus_exit(struct hdac_bus *bus); +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr); void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev); -void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); - -#define ebus_to_hbus(ebus) (&(ebus)->bus) -#define hbus_to_ebus(_bus) \ - container_of(_bus, struct hdac_ext_bus, bus) +void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus); #define HDA_CODEC_REV_EXT_ENTRY(_vid, _rev, _name, drv_data) \ { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \ @@ -44,14 +20,14 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); #define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \ HDA_CODEC_REV_EXT_ENTRY(_vid, _revid, _name, _drv_data) -void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable); -void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable); +void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *chip, bool enable); +void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *chip, bool enable); -void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *chip, +void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *chip, bool enable, int index); -int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *bus); -struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *bus, +int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus); +struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name); enum hdac_ext_stream_type { @@ -100,28 +76,28 @@ struct hdac_ext_stream { #define stream_to_hdac_ext_stream(s) \ container_of(s, struct hdac_ext_stream, hstream) -void snd_hdac_ext_stream_init(struct hdac_ext_bus *bus, +void snd_hdac_ext_stream_init(struct hdac_bus *bus, struct hdac_ext_stream *stream, int idx, int direction, int tag); -int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, +int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, int num_stream, int dir); -void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus); -void snd_hdac_link_free_all(struct hdac_ext_bus *ebus); -struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *bus, +void snd_hdac_stream_free_all(struct hdac_bus *bus); +void snd_hdac_link_free_all(struct hdac_bus *bus); +struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); -void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *bus, +void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); -void snd_hdac_ext_stop_streams(struct hdac_ext_bus *sbus); +void snd_hdac_ext_stop_streams(struct hdac_bus *bus); -int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value); -int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, struct hdac_ext_stream *stream); -void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, bool enable, int index); -int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value); int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value); @@ -144,17 +120,15 @@ struct hdac_ext_link { int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link); int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link); -int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus); -int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus); +int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus); +int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus); void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, int stream); void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, int stream); -int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, - struct hdac_ext_link *link); -int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, - struct hdac_ext_link *link); +int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, struct hdac_ext_link *link); +int snd_hdac_ext_bus_link_put(struct hdac_bus *bus, struct hdac_ext_link *link); /* update register macro */ #define snd_hdac_updatel(addr, reg, mask, val) \ diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 0e4823fdd41197..77547ede9ae84c 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -87,7 +87,7 @@ static const struct hdac_io_ops hdac_ext_default_io = { * * Returns 0 if successful, or a negative error code. */ -int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, +int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, const struct hdac_io_ops *io_ops) { @@ -98,15 +98,15 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, if (io_ops == NULL) io_ops = &hdac_ext_default_io; - ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops); + ret = snd_hdac_bus_init(bus, dev, ops, io_ops); if (ret < 0) return ret; - INIT_LIST_HEAD(&ebus->hlink_list); - ebus->idx = idx++; + INIT_LIST_HEAD(&bus->hlink_list); + bus->idx = idx++; - mutex_init(&ebus->lock); - ebus->cmd_dma_state = true; + mutex_init(&bus->lock); + bus->cmd_dma_state = true; return 0; } @@ -116,10 +116,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus * @ebus: the pointer to extended bus object */ -void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus) +void snd_hdac_ext_bus_exit(struct hdac_bus *bus) { - snd_hdac_bus_exit(&ebus->bus); - WARN_ON(!list_empty(&ebus->hlink_list)); + snd_hdac_bus_exit(bus); + WARN_ON(!list_empty(&bus->hlink_list)); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); @@ -135,10 +135,9 @@ static void default_release(struct device *dev) * * Returns zero for success or a negative error code. */ -int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr) { struct hdac_device *hdev = NULL; - struct hdac_bus *bus = ebus_to_hbus(ebus); char name[15]; int ret; @@ -148,7 +147,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) hdev->bus = bus; - snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); + snprintf(name, sizeof(name), "ehdaudio%dD%d", bus->idx, addr); ret = snd_hdac_device_init(hdev, bus, name, addr); if (ret < 0) { @@ -185,14 +184,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); * * @ebus: HD-audio extended bus */ -void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus) +void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus) { struct hdac_device *codec, *__codec; /* * we need to remove all the codec devices objects created in the * snd_hdac_ext_bus_device_init */ - list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) { + list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) { snd_hdac_device_unregister(codec); put_device(&codec->dev); } diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 84f3b81687164d..72774119dd1123 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -39,9 +39,8 @@ * @ebus: HD-audio extended core bus * @enable: flag to turn on/off the capability */ -void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable) +void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable) { - struct hdac_bus *bus = &ebus->bus; if (!bus->ppcap) { dev_err(bus->dev, "Address of PP capability is NULL"); @@ -60,9 +59,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); * @ebus: HD-audio extended core bus * @enable: flag to enable/disable interrupt */ -void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable) +void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable) { - struct hdac_bus *bus = &ebus->bus; if (!bus->ppcap) { dev_err(bus->dev, "Address of PP capability is NULL\n"); @@ -89,12 +87,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); * in hlink_list of extended hdac bus * Note: this will be freed on bus exit by driver */ -int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) +int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus) { int idx; u32 link_count; struct hdac_ext_link *hlink; - struct hdac_bus *bus = &ebus->bus; link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; @@ -114,7 +111,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) /* since link in On, update the ref */ hlink->ref_count = 1; - list_add_tail(&hlink->list, &ebus->hlink_list); + list_add_tail(&hlink->list, &bus->hlink_list); } return 0; @@ -127,12 +124,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities); * @ebus: HD-audio ext core bus */ -void snd_hdac_link_free_all(struct hdac_ext_bus *ebus) +void snd_hdac_link_free_all(struct hdac_bus *bus) { struct hdac_ext_link *l; - while (!list_empty(&ebus->hlink_list)) { - l = list_first_entry(&ebus->hlink_list, struct hdac_ext_link, list); + while (!list_empty(&bus->hlink_list)) { + l = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list); list_del(&l->list); kfree(l); } @@ -144,7 +141,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); * @ebus: HD-audio extended core bus * @codec_name: codec name */ -struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus, +struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name) { int i; @@ -153,10 +150,10 @@ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus, if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2) return NULL; - if (ebus->idx != bus_idx) + if (bus->idx != bus_idx) return NULL; - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { for (i = 0; i < HDA_MAX_CODECS; i++) { if (hlink->lsdiid & (0x1 << addr)) return hlink; @@ -219,12 +216,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down); * snd_hdac_ext_bus_link_power_up_all -power up all hda link * @ebus: HD-audio extended bus */ -int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus) +int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink = NULL; int ret; - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA); ret = check_hdac_link_power_active(hlink, true); @@ -240,12 +237,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all); * snd_hdac_ext_bus_link_power_down_all -power down all hda link * @ebus: HD-audio extended bus */ -int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus) +int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink = NULL; int ret; - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0); ret = check_hdac_link_power_active(hlink, false); if (ret < 0) @@ -256,39 +253,39 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus) } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); -int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, +int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, struct hdac_ext_link *link) { int ret = 0; - mutex_lock(&ebus->lock); + mutex_lock(&bus->lock); /* * if we move from 0 to 1, count will be 1 so power up this link * as well, also check the dma status and trigger that */ if (++link->ref_count == 1) { - if (!ebus->cmd_dma_state) { - snd_hdac_bus_init_cmd_io(&ebus->bus); - ebus->cmd_dma_state = true; + if (!bus->cmd_dma_state) { + snd_hdac_bus_init_cmd_io(bus); + bus->cmd_dma_state = true; } ret = snd_hdac_ext_bus_link_power_up(link); } - mutex_unlock(&ebus->lock); + mutex_unlock(&bus->lock); return ret; } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get); -int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, +int snd_hdac_ext_bus_link_put(struct hdac_bus *bus, struct hdac_ext_link *link) { int ret = 0; struct hdac_ext_link *hlink; bool link_up = false; - mutex_lock(&ebus->lock); + mutex_lock(&bus->lock); /* * if we move from 1 to 0, count will be 0 @@ -301,7 +298,7 @@ int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, * now check if all links are off, if so turn off * cmd dma as well */ - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { if (hlink->ref_count) { link_up = true; break; @@ -309,12 +306,12 @@ int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, } if (!link_up) { - snd_hdac_bus_stop_cmd_io(&ebus->bus); - ebus->cmd_dma_state = false; + snd_hdac_bus_stop_cmd_io(bus); + bus->cmd_dma_state = false; } } - mutex_unlock(&ebus->lock); + mutex_unlock(&bus->lock); return ret; } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index c96d7a7a36af03..1bd27576db98d5 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -25,7 +25,7 @@ /** * snd_hdac_ext_stream_init - initialize each stream (aka device) - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: HD-audio ext core stream object to initialize * @idx: stream index number * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) @@ -34,18 +34,16 @@ * initialize the stream, if ppcap is enabled then init those and then * invoke hdac stream initialization routine */ -void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_init(struct hdac_bus *bus, struct hdac_ext_stream *stream, int idx, int direction, int tag) { - struct hdac_bus *bus = &ebus->bus; - if (bus->ppcap) { stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + AZX_PPHC_INTERVAL * idx; stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + - AZX_PPLC_MULTI * ebus->num_streams + + AZX_PPLC_MULTI * bus->num_streams + AZX_PPLC_INTERVAL * idx; } @@ -71,12 +69,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); /** * snd_hdac_ext_stream_init_all - create and initialize the stream objects * for an extended hda bus - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @start_idx: start index for streams * @num_stream: number of streams to initialize * @dir: direction of streams */ -int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, +int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, int num_stream, int dir) { int stream_tag = 0; @@ -88,7 +86,7 @@ int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, if (!stream) return -ENOMEM; tag = ++stream_tag; - snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag); + snd_hdac_ext_stream_init(bus, stream, idx, dir, tag); idx++; } @@ -100,17 +98,16 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); /** * snd_hdac_stream_free_all - free hdac extended stream objects * - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus */ -void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus) +void snd_hdac_stream_free_all(struct hdac_bus *bus) { struct hdac_stream *s, *_s; struct hdac_ext_stream *stream; - struct hdac_bus *bus = ebus_to_hbus(ebus); list_for_each_entry_safe(s, _s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); list_del(&s->list); kfree(stream); } @@ -119,15 +116,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); /** * snd_hdac_ext_stream_decouple - decouple the hdac stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: HD-audio ext core stream object to initialize * @decouple: flag to decouple */ -void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *stream, bool decouple) { struct hdac_stream *hstream = &stream->hstream; - struct hdac_bus *bus = &ebus->bus; u32 val; int mask = AZX_PPCTL_PROCEN(hstream->index); @@ -251,19 +247,18 @@ void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); static struct hdac_ext_stream * -hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, +hdac_ext_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; - if (!hbus->ppcap) { - dev_err(hbus->dev, "stream type not supported\n"); + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); return NULL; } - list_for_each_entry(stream, &hbus->stream_list, list) { + list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, hstream); @@ -277,34 +272,33 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, } if (!hstream->link_locked) { - snd_hdac_ext_stream_decouple(ebus, hstream, true); + snd_hdac_ext_stream_decouple(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&hbus->reg_lock); + spin_lock_irq(&bus->reg_lock); res->link_locked = 1; res->link_substream = substream; - spin_unlock_irq(&hbus->reg_lock); + spin_unlock_irq(&bus->reg_lock); } return res; } static struct hdac_ext_stream * -hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, +hdac_ext_host_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; - if (!hbus->ppcap) { - dev_err(hbus->dev, "stream type not supported\n"); + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); return NULL; } - list_for_each_entry(stream, &hbus->stream_list, list) { + list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, hstream); @@ -313,17 +307,17 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, if (!stream->opened) { if (!hstream->decoupled) - snd_hdac_ext_stream_decouple(ebus, hstream, true); + snd_hdac_ext_stream_decouple(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&hbus->reg_lock); + spin_lock_irq(&bus->reg_lock); res->hstream.opened = 1; res->hstream.running = 0; res->hstream.substream = substream; - spin_unlock_irq(&hbus->reg_lock); + spin_unlock_irq(&bus->reg_lock); } return res; @@ -331,7 +325,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, /** * snd_hdac_ext_stream_assign - assign a stream for the PCM - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @substream: PCM substream to assign * @type: type of stream (coupled, host or link stream) * @@ -346,27 +340,26 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, * the same stream object when it's used beforehand. when a stream is * decoupled, it becomes a host stream and link stream. */ -struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *ebus, +struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type) { struct hdac_ext_stream *hstream = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - stream = snd_hdac_stream_assign(hbus, substream); + stream = snd_hdac_stream_assign(bus, substream); if (stream) hstream = container_of(stream, struct hdac_ext_stream, hstream); return hstream; case HDAC_EXT_STREAM_TYPE_HOST: - return hdac_ext_host_stream_assign(ebus, substream); + return hdac_ext_host_stream_assign(bus, substream); case HDAC_EXT_STREAM_TYPE_LINK: - return hdac_ext_link_stream_assign(ebus, substream); + return hdac_ext_link_stream_assign(bus, substream); default: return NULL; @@ -384,7 +377,6 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) { struct hdac_bus *bus = stream->hstream.bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: @@ -393,13 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) case HDAC_EXT_STREAM_TYPE_HOST: if (stream->decoupled && !stream->link_locked) - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); snd_hdac_stream_release(&stream->hstream); break; case HDAC_EXT_STREAM_TYPE_LINK: if (stream->decoupled && !stream->hstream.opened) - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); spin_lock_irq(&bus->reg_lock); stream->link_locked = 0; stream->link_substream = NULL; @@ -415,16 +407,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); /** * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @enable: flag to enable/disable SPIB * @index: stream index for which SPIB need to be enabled */ -void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, bool enable, int index) { u32 mask = 0; u32 register_mask = 0; - struct hdac_bus *bus = &ebus->bus; if (!bus->spbcap) { dev_err(bus->dev, "Address of SPB capability is NULL\n"); @@ -446,14 +437,13 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); /** * snd_hdac_ext_stream_set_spib - sets the spib value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * @value: spib value to set */ -int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value) { - struct hdac_bus *bus = &ebus->bus; if (!bus->spbcap) { dev_err(bus->dev, "Address of SPB capability is NULL\n"); @@ -468,15 +458,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); /** * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * * Return maxfifo for the stream */ -int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, struct hdac_ext_stream *stream) { - struct hdac_bus *bus = &ebus->bus; if (!bus->spbcap) { dev_err(bus->dev, "Address of SPB capability is NULL\n"); @@ -490,11 +479,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); /** * snd_hdac_ext_stop_streams - stop all stream if running - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus */ -void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus) +void snd_hdac_ext_stop_streams(struct hdac_bus *bus) { - struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_stream *stream; if (bus->chip_init) { @@ -507,16 +495,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); /** * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @enable: flag to enable/disable DRSM * @index: stream index for which DRSM need to be enabled */ -void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, bool enable, int index) { u32 mask = 0; u32 register_mask = 0; - struct hdac_bus *bus = &ebus->bus; if (!bus->drsmcap) { dev_err(bus->dev, "Address of DRSM capability is NULL\n"); @@ -538,14 +525,13 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); /** * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * @value: dpib value to set */ -int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value) { - struct hdac_bus *bus = &ebus->bus; if (!bus->drsmcap) { dev_err(bus->dev, "Address of DRSM capability is NULL\n"); @@ -560,7 +546,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); /** * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * @value: lpib value to set */ diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index f1e235817a6596..c3ccc8d9c91d10 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1799,14 +1799,13 @@ static int hdmi_codec_probe(struct snd_soc_component *component) * 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(hbus_to_ebus(hdev->bus), - dev_name(&hdev->dev)); + hlink = snd_hdac_ext_bus_get_link(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(hbus_to_ebus(hdev->bus), hlink); + snd_hdac_ext_bus_link_get(hdev->bus, hlink); ret = create_fill_widget_route_map(dapm); if (ret < 0) @@ -1984,14 +1983,13 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) 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(hbus_to_ebus(hdev->bus), - dev_name(&hdev->dev)); + hlink = snd_hdac_ext_bus_get_link(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(hbus_to_ebus(hdev->bus), hlink); + snd_hdac_ext_bus_link_get(hdev->bus, hlink); hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) @@ -2044,7 +2042,7 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, hdmi_dais, num_dais); - snd_hdac_ext_bus_link_put(hbus_to_ebus(hdev->bus), hlink); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return ret; } @@ -2093,7 +2091,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; int err; @@ -2118,13 +2115,13 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) return err; } - hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + hlink = snd_hdac_ext_bus_get_link(bus, dev_name(dev)); if (!hlink) { dev_err(dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_put(ebus, hlink); + snd_hdac_ext_bus_link_put(bus, hlink); return 0; } @@ -2133,7 +2130,6 @@ static int hdac_hdmi_runtime_resume(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; int err; @@ -2143,13 +2139,13 @@ static int hdac_hdmi_runtime_resume(struct device *dev) if (!bus) return 0; - hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + hlink = snd_hdac_ext_bus_get_link(bus, dev_name(dev)); if (!hlink) { dev_err(dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(ebus, hlink); + snd_hdac_ext_bus_link_get(bus, hlink); err = snd_hdac_display_power(bus, true); if (err < 0) { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index d5f9c30eba32cb..8bfb8b0fa3d595 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -33,8 +33,7 @@ static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); if (!bus) return -ENODEV; @@ -44,8 +43,7 @@ static int skl_alloc_dma_buf(struct device *dev, static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); if (!bus) return -ENODEV; @@ -89,8 +87,7 @@ void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) static int skl_dsp_setup_spib(struct device *dev, unsigned int size, int stream_tag, int enable) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream = snd_hdac_get_stream(bus, SNDRV_PCM_STREAM_PLAYBACK, stream_tag); struct hdac_ext_stream *estream; @@ -100,10 +97,10 @@ static int skl_dsp_setup_spib(struct device *dev, unsigned int size, estream = stream_to_hdac_ext_stream(stream); /* enable/disable SPIB for this hdac stream */ - snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index); + snd_hdac_ext_stream_spbcap_enable(bus, enable, stream->index); /* set the spib value */ - snd_hdac_ext_stream_set_spib(ebus, estream, size); + snd_hdac_ext_stream_set_spib(bus, estream, size); return 0; } @@ -111,8 +108,7 @@ static int skl_dsp_setup_spib(struct device *dev, unsigned int size, static int skl_dsp_prepare(struct device *dev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_ext_stream *estream; struct hdac_stream *stream; struct snd_pcm_substream substream; @@ -124,7 +120,7 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, memset(&substream, 0, sizeof(substream)); substream.stream = SNDRV_PCM_STREAM_PLAYBACK; - estream = snd_hdac_ext_stream_assign(ebus, &substream, + estream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST); if (!estream) return -ENODEV; @@ -143,9 +139,8 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; - struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; @@ -163,10 +158,9 @@ static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) static int skl_dsp_cleanup(struct device *dev, struct snd_dma_buffer *dmab, int stream_tag) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; struct hdac_ext_stream *estream; - struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; @@ -270,8 +264,7 @@ const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct skl_dsp_loader_ops loader_ops; int irq = bus->irq; const struct skl_dsp_ops *ops; @@ -279,8 +272,8 @@ int skl_init_dsp(struct skl *skl) int ret; /* enable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); + snd_hdac_ext_bus_ppcap_enable(bus, true); + snd_hdac_ext_bus_ppcap_int_enable(bus, true); /* read the BAR of the ADSP MMIO */ mmio_base = pci_ioremap_bar(skl->pci, 4); @@ -335,12 +328,11 @@ int skl_init_dsp(struct skl *skl) int skl_free_dsp(struct skl *skl) { - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct skl_sst *ctx = skl->skl_sst; /* disable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); + snd_hdac_ext_bus_ppcap_int_enable(bus, false); ctx->dsp_ops->cleanup(bus->dev, ctx); @@ -383,10 +375,11 @@ int skl_suspend_late_dsp(struct skl *skl) int skl_suspend_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + struct hdac_bus *bus = skl_to_bus(skl); int ret; /* if ppcap is not supported return 0 */ - if (!skl->ebus.bus.ppcap) + if (!bus->ppcap) return 0; ret = skl_dsp_sleep(ctx->dsp); @@ -394,8 +387,8 @@ int skl_suspend_dsp(struct skl *skl) return ret; /* disable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); - snd_hdac_ext_bus_ppcap_enable(&skl->ebus, false); + snd_hdac_ext_bus_ppcap_int_enable(bus, false); + snd_hdac_ext_bus_ppcap_enable(bus, false); return 0; } @@ -403,15 +396,16 @@ int skl_suspend_dsp(struct skl *skl) int skl_resume_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + struct hdac_bus *bus = skl_to_bus(skl); int ret; /* if ppcap is not supported return 0 */ - if (!skl->ebus.bus.ppcap) + if (!bus->ppcap) return 0; /* enable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); + snd_hdac_ext_bus_ppcap_enable(bus, true); + snd_hdac_ext_bus_ppcap_int_enable(bus, true); /* check if DSP 1st boot is done */ if (skl->skl_sst->is_first_boot == true) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index b9b140275be099..01a050cf877537 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -141,7 +141,7 @@ struct nhlt_specific_cfg { struct nhlt_fmt *fmt; struct nhlt_endpoint *epnt; - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct device *dev = bus->dev; struct nhlt_specific_cfg *sp_config; struct nhlt_acpi_table *nhlt = skl->nhlt; @@ -228,7 +228,7 @@ static void skl_nhlt_trim_space(char *trim) int skl_nhlt_update_topology_bin(struct skl *skl) { struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct device *dev = bus->dev; dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", @@ -248,8 +248,8 @@ static ssize_t skl_nhlt_platform_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; char platform_id[32]; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1f4dd08d36c55e..823e39103edd30 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -67,16 +67,15 @@ struct hdac_ext_stream *get_hdac_ext_stream(struct snd_pcm_substream *substream) return substream->runtime->private_data; } -static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream) +static struct hdac_bus *get_bus_ctx(struct snd_pcm_substream *substream) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct hdac_stream *hstream = hdac_stream(stream); struct hdac_bus *bus = hstream->bus; - - return hbus_to_ebus(bus); + return bus; } -static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus, +static int skl_substream_alloc_pages(struct hdac_bus *bus, struct snd_pcm_substream *substream, size_t size) { @@ -95,7 +94,7 @@ static int skl_substream_free_pages(struct hdac_bus *bus, return snd_pcm_lib_free_pages(substream); } -static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, +static void skl_set_pcm_constrains(struct hdac_bus *bus, struct snd_pcm_runtime *runtime) { snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -105,9 +104,9 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, 20, 178000000); } -static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus) +static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) { - if ((ebus_to_hbus(ebus))->ppcap) + if (bus->ppcap) return HDAC_EXT_STREAM_TYPE_HOST; else return HDAC_EXT_STREAM_TYPE_COUPLED; @@ -123,9 +122,9 @@ static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *e static void skl_set_suspend_active(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool enable) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct snd_soc_dapm_widget *w; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; @@ -140,8 +139,7 @@ static void skl_set_suspend_active(struct snd_pcm_substream *substream, int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; @@ -153,7 +151,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) return -EINVAL; stream = stream_to_hdac_ext_stream(hstream); - snd_hdac_ext_stream_decouple(ebus, stream, true); + snd_hdac_ext_stream_decouple(bus, stream, true); format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, params->format, params->host_bps, 0); @@ -177,8 +175,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; @@ -190,7 +187,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) return -EINVAL; stream = stream_to_hdac_ext_stream(hstream); - snd_hdac_ext_stream_decouple(ebus, stream, true); + snd_hdac_ext_stream_decouple(bus, stream, true); format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, params->format, params->link_bps, 0); @@ -201,7 +198,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) snd_hdac_ext_link_stream_setup(stream, format_val); - list_for_each_entry(link, &ebus->hlink_list, list) { + list_for_each_entry(link, &bus->hlink_list, list) { if (link->index == params->link_index) snd_hdac_ext_link_set_stream_id(link, hstream->stream_tag); @@ -215,7 +212,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) static int skl_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream; struct snd_pcm_runtime *runtime = substream->runtime; struct skl_dma_params *dma_params; @@ -224,12 +221,12 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - stream = snd_hdac_ext_stream_assign(ebus, substream, - skl_get_host_stream_type(ebus)); + stream = snd_hdac_ext_stream_assign(bus, substream, + skl_get_host_stream_type(bus)); if (stream == NULL) return -EBUSY; - skl_set_pcm_constrains(ebus, runtime); + skl_set_pcm_constrains(bus, runtime); /* * disable WALLCLOCK timestamps for capture streams @@ -301,7 +298,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct skl_pipe_params p_params = {0}; @@ -309,7 +306,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, int ret, dma_id; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - ret = skl_substream_alloc_pages(ebus, substream, + ret = skl_substream_alloc_pages(bus, substream, params_buffer_bytes(params)); if (ret < 0) return ret; @@ -343,14 +340,14 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct skl_dma_params *dma_params = NULL; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); struct skl_module_cfg *mconfig; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(ebus)); + snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(bus)); dma_params = snd_soc_dai_get_dma_data(dai, substream); /* @@ -380,7 +377,7 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, static int skl_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct skl *skl = get_skl_ctx(dai->dev); struct skl_module_cfg *mconfig; @@ -400,7 +397,7 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; - return skl_substream_free_pages(ebus_to_hbus(ebus), substream); + return skl_substream_free_pages(bus, substream); } static int skl_be_hw_params(struct snd_pcm_substream *substream, @@ -420,8 +417,7 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream; int start; unsigned long cookie; @@ -470,7 +466,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl *skl = get_skl_ctx(dai->dev); struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_soc_dapm_widget *w; int ret; @@ -492,9 +488,9 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, * dpib & lpib position to resume before starting the * DMA */ - snd_hdac_ext_stream_drsm_enable(ebus, true, + snd_hdac_ext_stream_drsm_enable(bus, true, hdac_stream(stream)->index); - snd_hdac_ext_stream_set_dpibr(ebus, stream, + snd_hdac_ext_stream_set_dpibr(bus, stream, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); } @@ -528,14 +524,14 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, ret = skl_decoupled_trigger(substream, cmd); if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) { /* save the dpib and lpib positions */ - stream->dpib = readl(ebus->bus.remap_addr + + stream->dpib = readl(bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index)); stream->lpib = snd_hdac_stream_get_pos_lpib( hdac_stream(stream)); - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); } break; @@ -546,11 +542,12 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } + static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *link_dev; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct snd_soc_dai *codec_dai = rtd->codec_dai; @@ -558,14 +555,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_link *link; int stream_tag; - link_dev = snd_hdac_ext_stream_assign(ebus, substream, + link_dev = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_LINK); if (!link_dev) return -EBUSY; snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); - link = snd_hdac_ext_bus_get_link(ebus, codec_dai->component->name); + link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); if (!link) return -EINVAL; @@ -610,7 +607,7 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream, { struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); @@ -626,7 +623,7 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: snd_hdac_ext_link_stream_clear(link_dev); if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); break; default: @@ -638,7 +635,7 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream, static int skl_link_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); @@ -648,7 +645,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_dai->component->name); + link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); if (!link) return -EINVAL; @@ -1042,8 +1039,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream) static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream; struct snd_pcm_substream *s; bool start; @@ -1116,9 +1112,9 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream, static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); - if (!(ebus_to_hbus(ebus))->ppcap) + if (!bus->ppcap) return skl_coupled_trigger(substream, cmd); return 0; @@ -1128,7 +1124,7 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer (struct snd_pcm_substream *substream) { struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); unsigned int pos; /* @@ -1153,12 +1149,12 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE + + pos = readl(bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(hstream)->index)); } else { udelay(20); - readl(ebus->bus.remap_addr + + readl(bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(hstream)->index)); @@ -1243,11 +1239,11 @@ static void skl_pcm_free(struct snd_pcm *pcm) static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct snd_pcm *pcm = rtd->pcm; unsigned int size; int retval = 0; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); if (dai->driver->playback.channels_min || dai->driver->capture.channels_min) { @@ -1357,19 +1353,19 @@ static int skl_populate_modules(struct skl *skl) static int skl_platform_soc_probe(struct snd_soc_component *component) { - struct hdac_ext_bus *ebus = dev_get_drvdata(component->dev); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = dev_get_drvdata(component->dev); + struct skl *skl = bus_to_skl(bus); const struct skl_dsp_ops *ops; int ret; pm_runtime_get_sync(component->dev); - if ((ebus_to_hbus(ebus))->ppcap) { + if (bus->ppcap) { skl->component = component; /* init debugfs */ skl->debugfs = skl_debugfs_init(skl); - ret = skl_tplg_init(component, ebus); + ret = skl_tplg_init(component, bus); if (ret < 0) { dev_err(component->dev, "Failed to init topology!\n"); return ret; @@ -1426,10 +1422,10 @@ static const struct snd_soc_component_driver skl_component = { int skl_platform_register(struct device *dev) { int ret; - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct skl *skl = ebus_to_skl(ebus); struct snd_soc_dai_driver *dais; int num_dais = ARRAY_SIZE(skl_platform_dai); + struct hdac_bus *bus = dev_get_drvdata(dev); + struct skl *skl = bus_to_skl(bus); INIT_LIST_HEAD(&skl->ppl_list); INIT_LIST_HEAD(&skl->bind_list); @@ -1465,8 +1461,8 @@ int skl_platform_register(struct device *dev) int skl_platform_unregister(struct device *dev) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); + struct skl *skl = bus_to_skl(bus); struct skl_module_deferred_bind *modules, *tmp; if (!list_empty(&skl->bind_list)) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 647e52aecdc334..76dde12cc2bb22 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -934,7 +934,7 @@ static int skl_tplg_find_moduleid_from_uuid(struct skl *skl, struct soc_bytes_ext *sb = (void *) k->private_value; struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private; struct skl_kpb_params *uuid_params, *params; - struct hdac_bus *bus = ebus_to_hbus(skl_to_ebus(skl)); + struct hdac_bus *bus = skl_to_bus(skl); int i, size, module_id; if (bc->set_params == SKL_PARAM_BIND && bc->max) { @@ -3029,9 +3029,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_dapm_widget *tplg_w) { int ret; - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = bus_to_skl(bus); struct skl_module_cfg *mconfig; if (!tplg_w->priv.size) @@ -3138,8 +3137,7 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt, struct soc_bytes_ext *sb; struct snd_soc_tplg_bytes_control *tplg_bc; struct snd_soc_tplg_enum_control *tplg_ec; - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); struct soc_enum *se; switch (hdr->ops.info) { @@ -3623,9 +3621,8 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_manifest *manifest) { - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = bus_to_skl(bus); /* proceed only if we have private data defined */ if (manifest->priv.size == 0) @@ -3714,12 +3711,11 @@ static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) /* * SKL topology init routine */ -int skl_tplg_init(struct snd_soc_component *component, struct hdac_ext_bus *ebus) +int skl_tplg_init(struct snd_soc_component *component, struct hdac_bus *bus) { int ret; const struct firmware *fw; - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); struct skl_pipeline *ppl; ret = request_firmware(&fw, skl->tplg_name, bus->dev); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index af198ea0379e64..82282cac9751ed 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -458,9 +458,9 @@ enum skl_channel { static inline struct skl *get_skl_ctx(struct device *dev) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = dev_get_drvdata(dev); - return ebus_to_skl(ebus); + return bus_to_skl(bus); } int skl_tplg_be_update_params(struct snd_soc_dai *dai, @@ -470,7 +470,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_component *component, - struct hdac_ext_bus *ebus); + struct hdac_bus *ebus); struct skl_module_cfg *skl_tplg_fe_get_cpr_module( struct snd_soc_dai *dai, int stream); int skl_tplg_update_pipe_params(struct device *dev, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 670ff9aaca55de..bde9ed7f63f61a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -53,7 +53,7 @@ static void skl_update_pci_byte(struct pci_dev *pci, unsigned int reg, static void skl_init_pci(struct skl *skl) { - struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = skl_to_bus(skl); /* * Clear bits 0-2 of PCI register TCSEL (at offset 0x44) @@ -62,7 +62,7 @@ static void skl_init_pci(struct skl *skl) * codecs. * The PCI register TCSEL is defined in the Intel manuals. */ - dev_dbg(ebus_to_hbus(ebus)->dev, "Clearing TCSEL\n"); + dev_dbg(bus->dev, "Clearing TCSEL\n"); skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0); } @@ -102,8 +102,7 @@ static void skl_enable_miscbdcge(struct device *dev, bool enable) static void skl_clock_power_gating(struct device *dev, bool enable) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); u32 val; /* Update PDCGE bit of CGCTL register */ @@ -126,7 +125,6 @@ static void skl_clock_power_gating(struct device *dev, bool enable) */ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) { - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink; int ret; @@ -134,7 +132,7 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) ret = snd_hdac_bus_init_chip(bus, full_reset); /* Reset stream-to-link mapping */ - list_for_each_entry(hlink, &ebus->hlink_list, list) + list_for_each_entry(hlink, &bus->hlink_list, list) bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); skl_enable_miscbdcge(bus->dev, true); @@ -145,8 +143,7 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) void skl_update_d0i3c(struct device *dev, bool enable) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); u8 reg; int timeout = 50; @@ -196,8 +193,7 @@ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) static irqreturn_t skl_interrupt(int irq, void *dev_id) { - struct hdac_ext_bus *ebus = dev_id; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_id; u32 status; if (!pm_runtime_active(bus->dev)) @@ -226,8 +222,7 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) static irqreturn_t skl_threaded_handler(int irq, void *dev_id) { - struct hdac_ext_bus *ebus = dev_id; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_id; u32 status; status = snd_hdac_chip_readl(bus, INTSTS); @@ -237,16 +232,15 @@ static irqreturn_t skl_threaded_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) +static int skl_acquire_irq(struct hdac_bus *bus, int do_disconnect) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); int ret; ret = request_threaded_irq(skl->pci->irq, skl_interrupt, skl_threaded_handler, IRQF_SHARED, - KBUILD_MODNAME, ebus); + KBUILD_MODNAME, bus); if (ret) { dev_err(bus->dev, "unable to grab IRQ %d, disabling device\n", @@ -263,21 +257,20 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) static int skl_suspend_late(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); return skl_suspend_late_dsp(skl); } #ifdef CONFIG_PM -static int _skl_suspend(struct hdac_ext_bus *ebus) +static int _skl_suspend(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); struct pci_dev *pci = to_pci_dev(bus->dev); int ret; - snd_hdac_ext_bus_link_power_down_all(ebus); + snd_hdac_ext_bus_link_power_down_all(bus); ret = skl_suspend_dsp(skl); if (ret < 0) @@ -294,10 +287,9 @@ static int _skl_suspend(struct hdac_ext_bus *ebus) return 0; } -static int _skl_resume(struct hdac_ext_bus *ebus) +static int _skl_resume(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); skl_init_pci(skl); skl_init_chip(bus, true); @@ -313,9 +305,8 @@ static int _skl_resume(struct hdac_ext_bus *ebus) static int skl_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); int ret = 0; /* @@ -324,15 +315,15 @@ static int skl_suspend(struct device *dev) */ if (skl->supend_active) { /* turn off the links and stop the CORB/RIRB DMA if it is On */ - snd_hdac_ext_bus_link_power_down_all(ebus); + snd_hdac_ext_bus_link_power_down_all(bus); - if (ebus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(&ebus->bus); + if (bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); enable_irq_wake(bus->irq); pci_save_state(pci); } else { - ret = _skl_suspend(ebus); + ret = _skl_suspend(bus); if (ret < 0) return ret; skl->skl_sst->fw_loaded = false; @@ -351,9 +342,8 @@ static int skl_suspend(struct device *dev) static int skl_resume(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); struct hdac_ext_link *hlink = NULL; int ret; @@ -373,32 +363,32 @@ static int skl_resume(struct device *dev) */ if (skl->supend_active) { pci_restore_state(pci); - snd_hdac_ext_bus_link_power_up_all(ebus); + snd_hdac_ext_bus_link_power_up_all(bus); disable_irq_wake(bus->irq); /* * turn On the links which are On before active suspend * and start the CORB/RIRB DMA if On before * active suspend. */ - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { if (hlink->ref_count) snd_hdac_ext_bus_link_power_up(hlink); } - if (ebus->cmd_dma_state) - snd_hdac_bus_init_cmd_io(&ebus->bus); ret = 0; + if (bus->cmd_dma_state) + snd_hdac_bus_init_cmd_io(bus); } else { - ret = _skl_resume(ebus); + ret = _skl_resume(bus); /* turn off the links which are off before suspend */ - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { if (!hlink->ref_count) snd_hdac_ext_bus_link_power_down(hlink); } - if (!ebus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(&ebus->bus); + if (!bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); } return ret; @@ -409,23 +399,21 @@ static int skl_resume(struct device *dev) static int skl_runtime_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); dev_dbg(bus->dev, "in %s\n", __func__); - return _skl_suspend(ebus); + return _skl_suspend(bus); } static int skl_runtime_resume(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); dev_dbg(bus->dev, "in %s\n", __func__); - return _skl_resume(ebus); + return _skl_resume(bus); } #endif /* CONFIG_PM */ @@ -438,20 +426,19 @@ static const struct dev_pm_ops skl_pm = { /* * destructor */ -static int skl_free(struct hdac_ext_bus *ebus) +static int skl_free(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); skl->init_done = 0; /* to be sure */ - snd_hdac_ext_stop_streams(ebus); + snd_hdac_ext_stop_streams(bus); if (bus->irq >= 0) - free_irq(bus->irq, (void *)ebus); + free_irq(bus->irq, (void *)bus); snd_hdac_bus_free_stream_pages(bus); - snd_hdac_stream_free_all(ebus); - snd_hdac_link_free_all(ebus); + snd_hdac_stream_free_all(bus); + snd_hdac_link_free_all(bus); if (bus->remap_addr) iounmap(bus->remap_addr); @@ -459,11 +446,11 @@ static int skl_free(struct hdac_ext_bus *ebus) pci_release_regions(skl->pci); pci_disable_device(skl->pci); - snd_hdac_ext_bus_exit(ebus); + snd_hdac_ext_bus_exit(bus); cancel_work_sync(&skl->probe_work); if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) - snd_hdac_i915_exit(&ebus->bus); + snd_hdac_i915_exit(bus); return 0; } @@ -487,8 +474,8 @@ static struct skl_ssp_clk skl_ssp_clks[] = { static int skl_find_machine(struct skl *skl, void *driver_data) { + struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach = driver_data; - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct skl_machine_pdata *pdata; mach = snd_soc_acpi_find_machine(mach); @@ -511,7 +498,7 @@ static int skl_find_machine(struct skl *skl, void *driver_data) static int skl_machine_device_register(struct skl *skl) { - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach = skl->mach; struct platform_device *pdev; int ret; @@ -545,7 +532,7 @@ static void skl_machine_device_unregister(struct skl *skl) static int skl_dmic_device_register(struct skl *skl) { - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct platform_device *pdev; int ret; @@ -644,9 +631,8 @@ static void skl_clock_device_unregister(struct skl *skl) /* * Probe the given codec address */ -static int probe_codec(struct hdac_ext_bus *ebus, int addr) +static int probe_codec(struct hdac_bus *bus, int addr) { - struct hdac_bus *bus = ebus_to_hbus(ebus); unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; @@ -659,13 +645,12 @@ static int probe_codec(struct hdac_ext_bus *ebus, int addr) return -EIO; dev_dbg(bus->dev, "codec #%d probed OK\n", addr); - return snd_hdac_ext_bus_device_init(ebus, addr); + return snd_hdac_ext_bus_device_init(bus, addr); } /* Codec initialization */ -static void skl_codec_create(struct hdac_ext_bus *ebus) +static void skl_codec_create(struct hdac_bus *bus) { - struct hdac_bus *bus = ebus_to_hbus(ebus); int c, max_slots; max_slots = HDA_MAX_CODECS; @@ -673,7 +658,7 @@ static void skl_codec_create(struct hdac_ext_bus *ebus) /* First try to probe all given codec slots */ for (c = 0; c < max_slots; c++) { if ((bus->codec_mask & (1 << c))) { - if (probe_codec(ebus, c) < 0) { + if (probe_codec(bus, c) < 0) { /* * Some BIOSen give you wrong codec addresses * that don't exist @@ -723,8 +708,7 @@ static int skl_i915_init(struct hdac_bus *bus) static void skl_probe_work(struct work_struct *work) { struct skl *skl = container_of(work, struct skl, probe_work); - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct hdac_ext_link *hlink = NULL; int err; @@ -745,7 +729,7 @@ static void skl_probe_work(struct work_struct *work) dev_info(bus->dev, "no hda codecs found!\n"); /* create codec instances */ - skl_codec_create(ebus); + skl_codec_create(bus); /* register platform dai and controls */ err = skl_platform_register(bus->dev); @@ -774,8 +758,8 @@ static void skl_probe_work(struct work_struct *work) /* * we are done probing so decrement link counts */ - list_for_each_entry(hlink, &ebus->hlink_list, list) - snd_hdac_ext_bus_link_put(ebus, hlink); + list_for_each_entry(hlink, &bus->hlink_list, list) + snd_hdac_ext_bus_link_put(bus, hlink); /* configure PM */ pm_runtime_put_noidle(bus->dev); @@ -797,7 +781,7 @@ static int skl_create(struct pci_dev *pci, struct skl **rskl) { struct skl *skl; - struct hdac_ext_bus *ebus; + struct hdac_bus *bus; int err; @@ -812,23 +796,22 @@ static int skl_create(struct pci_dev *pci, pci_disable_device(pci); return -ENOMEM; } - ebus = &skl->ebus; - snd_hdac_ext_bus_init(ebus, &pci->dev, &bus_core_ops, io_ops); - ebus->bus.use_posbuf = 1; + + bus = skl_to_bus(skl); + snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops); + bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); - - ebus->bus.bdl_pos_adj = 0; + bus->bdl_pos_adj = 0; *rskl = skl; return 0; } -static int skl_first_init(struct hdac_ext_bus *ebus) +static int skl_first_init(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); struct pci_dev *pci = skl->pci; int err; unsigned short gcap; @@ -849,7 +832,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) snd_hdac_bus_parse_capabilities(bus); - if (skl_acquire_irq(ebus, 0) < 0) + if (skl_acquire_irq(bus, 0) < 0) return -EBUSY; pci_set_master(pci); @@ -873,14 +856,14 @@ static int skl_first_init(struct hdac_ext_bus *ebus) if (!pb_streams && !cp_streams) return -EIO; - ebus->num_streams = cp_streams + pb_streams; + bus->num_streams = cp_streams + pb_streams; /* initialize streams */ snd_hdac_ext_stream_init_all - (ebus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); + (bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); start_idx = cp_streams; snd_hdac_ext_stream_init_all - (ebus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); + (bus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); err = snd_hdac_bus_alloc_stream_pages(bus); if (err < 0) @@ -896,7 +879,6 @@ static int skl_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { struct skl *skl; - struct hdac_ext_bus *ebus = NULL; struct hdac_bus *bus = NULL; int err; @@ -905,10 +887,9 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) return err; - ebus = &skl->ebus; - bus = ebus_to_hbus(ebus); + bus = skl_to_bus(skl); - err = skl_first_init(ebus); + err = skl_first_init(bus); if (err < 0) goto out_free; @@ -929,7 +910,7 @@ static int skl_probe(struct pci_dev *pci, skl_nhlt_update_topology_bin(skl); - pci_set_drvdata(skl->pci, ebus); + pci_set_drvdata(skl->pci, bus); /* check if dsp is there */ if (bus->ppcap) { @@ -951,7 +932,7 @@ static int skl_probe(struct pci_dev *pci, skl->skl_sst->clock_power_gating = skl_clock_power_gating; } if (bus->mlcap) - snd_hdac_ext_bus_get_ml_capabilities(ebus); + snd_hdac_ext_bus_get_ml_capabilities(bus); snd_hdac_bus_stop_chip(bus); @@ -971,31 +952,30 @@ static int skl_probe(struct pci_dev *pci, out_nhlt_free: skl_nhlt_free(skl->nhlt); out_free: - skl_free(ebus); + skl_free(bus); return err; } static void skl_shutdown(struct pci_dev *pci) { - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); struct hdac_stream *s; struct hdac_ext_stream *stream; struct skl *skl; - if (ebus == NULL) + if (!bus) return; - skl = ebus_to_skl(ebus); + skl = bus_to_skl(bus); if (!skl->init_done) return; - snd_hdac_ext_stop_streams(ebus); + snd_hdac_ext_stop_streams(bus); list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); } snd_hdac_bus_stop_chip(bus); @@ -1003,15 +983,15 @@ static void skl_shutdown(struct pci_dev *pci) static void skl_remove(struct pci_dev *pci) { - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); release_firmware(skl->tplg); pm_runtime_get_noresume(&pci->dev); /* codec removal, invoke bus_device_remove */ - snd_hdac_ext_bus_device_remove(ebus); + snd_hdac_ext_bus_device_remove(bus); skl->debugfs = NULL; skl_platform_unregister(&pci->dev); @@ -1021,7 +1001,7 @@ static void skl_remove(struct pci_dev *pci) skl_clock_device_unregister(skl); skl_nhlt_remove_sysfs(skl); skl_nhlt_free(skl->nhlt); - skl_free(ebus); + skl_free(bus); dev_set_drvdata(&pci->dev, NULL); } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 0d5375cbcf6ec3..78aa8bdcb61929 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -71,7 +71,7 @@ struct skl_fw_config { }; struct skl { - struct hdac_ext_bus ebus; + struct hdac_bus hbus; struct pci_dev *pci; unsigned int init_done:1; /* delayed init status */ @@ -105,9 +105,8 @@ struct skl { struct snd_soc_acpi_mach *mach; }; -#define skl_to_ebus(s) (&(s)->ebus) -#define ebus_to_skl(sbus) \ - container_of(sbus, struct skl, sbus) +#define skl_to_bus(s) (&(s)->hbus) +#define bus_to_skl(bus) container_of(bus, struct skl, hbus) /* to pass dai dma data */ struct skl_dma_params { From e3a048eda91a10e52f49a9aaa7dbc7c466ad91e8 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 1 Jun 2018 22:53:51 -0500 Subject: [PATCH 146/298] ALSA: hdac: Remove usage of struct hdac_ext_driver, use hdac_driver instead This patch removes the hdac_ext_driver structure. The legacy and enhanced HDaudio capabilities can be handled in a backward-compatible way without separate definitions. Signed-off-by: Rakesh Ughreja Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 5 +++++ include/sound/hdaudio_ext.h | 17 ++--------------- sound/hda/ext/hdac_ext_bus.c | 30 ++++++++++++++---------------- sound/soc/codecs/hdac_hdmi.c | 12 +++++------- 4 files changed, 26 insertions(+), 38 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 9735b51aef0896..59ffe63cf1942d 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -188,6 +188,11 @@ struct hdac_driver { const struct hda_device_id *id_table; int (*match)(struct hdac_device *dev, struct hdac_driver *drv); void (*unsol_event)(struct hdac_device *dev, unsigned int event); + + /* fields used by ext bus APIs */ + int (*probe)(struct hdac_device *dev); + int (*remove)(struct hdac_device *dev); + void (*shutdown)(struct hdac_device *dev); }; #define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index e5b0cd1ade19c2..3c302477750b8c 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -160,20 +160,7 @@ struct hdac_ext_dma_params { u8 stream_tag; }; -/* - * HD-audio codec base driver - */ -struct hdac_ext_driver { - struct hdac_driver hdac; - - int (*probe)(struct hdac_device *dev); - int (*remove)(struct hdac_device *dev); - void (*shutdown)(struct hdac_device *dev); -}; - -int snd_hda_ext_driver_register(struct hdac_ext_driver *drv); -void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv); - -#define to_ehdac_driver(_drv) container_of(_drv, struct hdac_ext_driver, hdac) +int snd_hda_ext_driver_register(struct hdac_driver *drv); +void snd_hda_ext_driver_unregister(struct hdac_driver *drv); #endif /* __SOUND_HDAUDIO_EXT_H */ diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 77547ede9ae84c..52f07766fff327 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -200,12 +200,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); #define dev_to_hdac(dev) (container_of((dev), \ struct hdac_device, dev)) -static inline struct hdac_ext_driver *get_edrv(struct device *dev) +static inline struct hdac_driver *get_hdrv(struct device *dev) { struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); - struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv); - - return edrv; + return hdrv; } static inline struct hdac_device *get_hdev(struct device *dev) @@ -216,17 +214,17 @@ static inline struct hdac_device *get_hdev(struct device *dev) static int hda_ext_drv_probe(struct device *dev) { - return (get_edrv(dev))->probe(get_hdev(dev)); + return (get_hdrv(dev))->probe(get_hdev(dev)); } static int hdac_ext_drv_remove(struct device *dev) { - return (get_edrv(dev))->remove(get_hdev(dev)); + return (get_hdrv(dev))->remove(get_hdev(dev)); } static void hdac_ext_drv_shutdown(struct device *dev) { - return (get_edrv(dev))->shutdown(get_hdev(dev)); + return (get_hdrv(dev))->shutdown(get_hdev(dev)); } /** @@ -234,20 +232,20 @@ static void hdac_ext_drv_shutdown(struct device *dev) * * @drv: ext hda driver structure */ -int snd_hda_ext_driver_register(struct hdac_ext_driver *drv) +int snd_hda_ext_driver_register(struct hdac_driver *drv) { - drv->hdac.type = HDA_DEV_ASOC; - drv->hdac.driver.bus = &snd_hda_bus_type; + drv->type = HDA_DEV_ASOC; + drv->driver.bus = &snd_hda_bus_type; /* we use default match */ if (drv->probe) - drv->hdac.driver.probe = hda_ext_drv_probe; + drv->driver.probe = hda_ext_drv_probe; if (drv->remove) - drv->hdac.driver.remove = hdac_ext_drv_remove; + drv->driver.remove = hdac_ext_drv_remove; if (drv->shutdown) - drv->hdac.driver.shutdown = hdac_ext_drv_shutdown; + drv->driver.shutdown = hdac_ext_drv_shutdown; - return driver_register(&drv->hdac.driver); + return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); @@ -256,8 +254,8 @@ EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); * * @drv: ext hda driver structure */ -void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv) +void snd_hda_ext_driver_unregister(struct hdac_driver *drv) { - driver_unregister(&drv->hdac.driver); + driver_unregister(&drv->driver); } EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index c3ccc8d9c91d10..3e3a2a9ef3108d 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -2186,14 +2186,12 @@ static const struct hda_device_id hdmi_list[] = { MODULE_DEVICE_TABLE(hdaudio, hdmi_list); -static struct hdac_ext_driver hdmi_driver = { - . hdac = { - .driver = { - .name = "HDMI HDA Codec", - .pm = &hdac_hdmi_pm, - }, - .id_table = hdmi_list, +static struct hdac_driver hdmi_driver = { + .driver = { + .name = "HDMI HDA Codec", + .pm = &hdac_hdmi_pm, }, + .id_table = hdmi_list, .probe = hdac_hdmi_dev_probe, .remove = hdac_hdmi_dev_remove, }; From 685081326dc494dd858880229d41233ace6ede49 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 1 Jun 2018 22:54:00 -0500 Subject: [PATCH 147/298] ALSA: hdac: ext: add wait for codec to respond after link reset As per HDA spec section 4.3 - Codec Discovery, the software shall wait for atleast 521usec for codec to respond after link reset. With the multi-link capability each link is turned ON/OFF individually. Link controller drives reset signal when it is turned ON. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- sound/hda/ext/hdac_ext_controller.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 72774119dd1123..5bc4a1d587d4f1 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -271,6 +271,15 @@ int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, } ret = snd_hdac_ext_bus_link_power_up(link); + + /* + * wait for 521usec for codec to report status + * HDA spec section 4.3 - Codec Discovery + */ + udelay(521); + bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); + dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); + snd_hdac_chip_writew(bus, STATESTS, bus->codec_mask); } mutex_unlock(&bus->lock); From 677418f6a0d983fbd1450165d6697a157fc76748 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 1 Jun 2018 22:53:56 -0500 Subject: [PATCH 148/298] ALSA: hda: split snd_hda_codec_new function Split snd_hda_codec_new into two separate functions. snd_hda_codec_device_init allocates memory and registers with bus. snd_hda_codec_device_new initialializes the fields and performs snd_device_new. This enables reuse of legacy HDA codec drivers as ASoC codec drivers. In addition mark some functions with EXPORT_SYMBOL_GPL so that it can be called by ASoC wrapper around the legacy HDA driver (hdac_hda). Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 68 +++++++++++++++++++++++++++++---------- sound/pci/hda/hda_codec.h | 2 ++ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 20a171ac4bb2f7..3fd0c16fa602de 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -858,6 +858,39 @@ static void snd_hda_codec_dev_release(struct device *dev) kfree(codec); } +#define DEV_NAME_LEN 31 + +static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp) +{ + char name[DEV_NAME_LEN]; + struct hda_codec *codec; + int err; + + dev_dbg(card->dev, "%s: entry\n", __func__); + + if (snd_BUG_ON(!bus)) + return -EINVAL; + if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) + return -EINVAL; + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + sprintf(name, "hdaudioC%dD%d", card->number, codec_addr); + err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); + if (err < 0) { + kfree(codec); + return err; + } + + codec->core.type = HDA_DEV_LEGACY; + *codecp = codec; + + return err; +} + /** * snd_hda_codec_new - create a HDA codec * @bus: the bus to assign @@ -869,7 +902,19 @@ static void snd_hda_codec_dev_release(struct device *dev) int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp) { - struct hda_codec *codec; + int ret; + + ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp); + if (ret < 0) + return ret; + + return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_new); + +int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec *codec) +{ char component[31]; hda_nid_t fg; int err; @@ -879,25 +924,14 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, .dev_free = snd_hda_codec_dev_free, }; + dev_dbg(card->dev, "%s: entry\n", __func__); + if (snd_BUG_ON(!bus)) return -EINVAL; if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) return -EINVAL; - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - - sprintf(component, "hdaudioC%dD%d", card->number, codec_addr); - err = snd_hdac_device_init(&codec->core, &bus->core, component, - codec_addr); - if (err < 0) { - kfree(codec); - return err; - } - codec->core.dev.release = snd_hda_codec_dev_release; - codec->core.type = HDA_DEV_LEGACY; codec->core.exec_verb = codec_exec_verb; codec->bus = bus; @@ -957,15 +991,13 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, if (err < 0) goto error; - if (codecp) - *codecp = codec; return 0; error: put_device(hda_codec_dev(codec)); return err; } -EXPORT_SYMBOL_GPL(snd_hda_codec_new); +EXPORT_SYMBOL_GPL(snd_hda_codec_device_new); /** * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults @@ -2992,6 +3024,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) sync_power_up_states(codec); return 0; } +EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls); /* * PCM stuff @@ -3197,6 +3230,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) return 0; } +EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms); /* assign all PCMs of the given codec */ int snd_hda_codec_build_pcms(struct hda_codec *codec) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index a8b1b31f161c26..e03b5c1ccc5cf5 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -308,6 +308,8 @@ struct hda_codec { */ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); +int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec *codec); int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); From 841de563ba90e5c330202ff6a66877d0061074f0 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 1 Jun 2018 22:53:57 -0500 Subject: [PATCH 149/298] ALSA: hdac: remove memory allocation from snd_hdac_ext_bus_device_init Remove memory allocation within snd_hdac_ext_bus_device_init, to make its behaviour identical to snd_hdac_bus_device_init. So that caller can allocate the parent data structure containing hdac_device. This API change helps in reusing the legacy HDA codec drivers with ASoC platform drivers. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/hdaudio_ext.h | 3 ++- sound/hda/ext/hdac_ext_bus.c | 8 ++------ sound/soc/intel/skylake/skl.c | 8 +++++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 3c302477750b8c..c188b801239f98 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -9,7 +9,8 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_io_ops *io_ops); void snd_hdac_ext_bus_exit(struct hdac_bus *bus); -int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr); +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, + struct hdac_device *hdev); void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev); void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus); diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 52f07766fff327..1eb58244688e78 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -135,16 +135,12 @@ static void default_release(struct device *dev) * * Returns zero for success or a negative error code. */ -int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr) +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, + struct hdac_device *hdev) { - struct hdac_device *hdev = NULL; char name[15]; int ret; - hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); - if (!hdev) - return -ENOMEM; - hdev->bus = bus; snprintf(name, sizeof(name), "ehdaudio%dD%d", bus->idx, addr); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index bde9ed7f63f61a..e67e28b103ea29 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -636,6 +636,8 @@ static int probe_codec(struct hdac_bus *bus, int addr) unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; + struct skl *skl = bus_to_skl(bus); + struct hdac_device *hdev; mutex_lock(&bus->cmd_mutex); snd_hdac_bus_send_cmd(bus, cmd); @@ -645,7 +647,11 @@ static int probe_codec(struct hdac_bus *bus, int addr) return -EIO; dev_dbg(bus->dev, "codec #%d probed OK\n", addr); - return snd_hdac_ext_bus_device_init(bus, addr); + hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + return snd_hdac_ext_bus_device_init(bus, addr, hdev); } /* Codec initialization */ From da1a2a345842413351846da98afc1ddbc1097502 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 1 Jun 2018 22:53:58 -0500 Subject: [PATCH 150/298] ALSA: hdac: add extended ops in the hdac_bus Add extended ops in the hdac_bus to allow calling the ASoC HDAC library ops to reuse the legacy HDA codec drivers with ASoC framework. Extended ops are used by the legacy codec drivers to call into hdac_hda library, in the subsequent patches.. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 9 +++++++++ include/sound/hdaudio_ext.h | 3 ++- sound/hda/ext/hdac_ext_bus.c | 4 +++- sound/soc/intel/skylake/skl.c | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 59ffe63cf1942d..f1baaa88e766df 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -213,6 +213,14 @@ struct hdac_bus_ops { int (*link_power)(struct hdac_bus *bus, bool enable); }; +/* + * ops used for ASoC HDA codec drivers + */ +struct hdac_ext_bus_ops { + int (*hdev_attach)(struct hdac_device *hdev); + int (*hdev_detach)(struct hdac_device *hdev); +}; + /* * Lowlevel I/O operators */ @@ -265,6 +273,7 @@ struct hdac_bus { struct device *dev; const struct hdac_bus_ops *ops; const struct hdac_io_ops *io_ops; + const struct hdac_ext_bus_ops *ext_ops; /* h/w resources */ unsigned long addr; diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index c188b801239f98..f34aced69ca803 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -6,7 +6,8 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, - const struct hdac_io_ops *io_ops); + const struct hdac_io_ops *io_ops, + const struct hdac_ext_bus_ops *ext_ops); void snd_hdac_ext_bus_exit(struct hdac_bus *bus); int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 1eb58244688e78..9c37d9af3023f6 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -89,7 +89,8 @@ static const struct hdac_io_ops hdac_ext_default_io = { */ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, - const struct hdac_io_ops *io_ops) + const struct hdac_io_ops *io_ops, + const struct hdac_ext_bus_ops *ext_ops) { int ret; static int idx; @@ -102,6 +103,7 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, if (ret < 0) return ret; + bus->ext_ops = ext_ops; INIT_LIST_HEAD(&bus->hlink_list); bus->idx = idx++; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index e67e28b103ea29..dce64948564940 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -804,7 +804,7 @@ static int skl_create(struct pci_dev *pci, } bus = skl_to_bus(skl); - snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops); + snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, NULL); bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); From 12da3da3a6f1ffbea3da33f9b9b3da19ab0c4725 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 22 Aug 2018 15:24:57 -0500 Subject: [PATCH 151/298] ALSA: hda: move hda_codec.h to include/sound As suggested by Takashi, move this header file to make it easier to include from e.g. the Intel Skylake driver in follow-up patches Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- {sound/pci/hda => include/sound}/hda_codec.h | 0 sound/pci/hda/hda_auto_parser.c | 2 +- sound/pci/hda/hda_beep.h | 2 +- sound/pci/hda/hda_bind.c | 2 +- sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_controller.h | 2 +- sound/pci/hda/hda_eld.c | 2 +- sound/pci/hda/hda_generic.c | 2 +- sound/pci/hda/hda_hwdep.c | 2 +- sound/pci/hda/hda_intel.c | 2 +- sound/pci/hda/hda_jack.c | 2 +- sound/pci/hda/hda_proc.c | 2 +- sound/pci/hda/hda_sysfs.c | 2 +- sound/pci/hda/hda_tegra.c | 2 +- sound/pci/hda/patch_analog.c | 2 +- sound/pci/hda/patch_ca0110.c | 2 +- sound/pci/hda/patch_ca0132.c | 2 +- sound/pci/hda/patch_cirrus.c | 2 +- sound/pci/hda/patch_cmedia.c | 2 +- sound/pci/hda/patch_conexant.c | 2 +- sound/pci/hda/patch_hdmi.c | 2 +- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/hda/patch_si3054.c | 2 +- sound/pci/hda/patch_sigmatel.c | 2 +- sound/pci/hda/patch_via.c | 2 +- 25 files changed, 24 insertions(+), 24 deletions(-) rename {sound/pci/hda => include/sound}/hda_codec.h (100%) diff --git a/sound/pci/hda/hda_codec.h b/include/sound/hda_codec.h similarity index 100% rename from sound/pci/hda/hda_codec.h rename to include/sound/hda_codec.h diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index b9a6b66aeb0ef7..df0d636145f819 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -13,7 +13,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index d1a6a9c1329a06..f1457c6b39695b 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -9,7 +9,7 @@ #ifndef __SOUND_HDA_BEEP_H #define __SOUND_HDA_BEEP_H -#include "hda_codec.h" +#include #define HDA_BEEP_MODE_OFF 0 #define HDA_BEEP_MODE_ON 1 diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index d361bb77ca00d1..2222b47d4ec4fb 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -11,7 +11,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" /* diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3fd0c16fa602de..e18c14a66129bc 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -27,7 +27,7 @@ #include #include #include -#include "hda_codec.h" +#include #include #include #include diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index a68e75b00ea3bd..55760e5231e62b 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -20,7 +20,7 @@ #include #include #include -#include "hda_codec.h" +#include #include #define AZX_MAX_CODECS HDA_MAX_CODECS diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index ba7fe9b6655c1f..806b12ed44a233 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -27,7 +27,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" enum eld_versions { diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index db773e219aaa8e..000c89f56a2a95 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -32,7 +32,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index cc009a4a3d1d20..268bba6ec985a7 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -23,7 +23,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include #include diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1ae1850b3bfdb3..9b14fd6089805a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -63,7 +63,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_controller.h" #include "hda_intel.h" diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index a33234e04d4f7a..c499727920e61c 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -15,7 +15,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index c6b778b2580c12..a657404196502e 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -25,7 +25,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" static int dump_coef = -1; diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 6ec79c58d48de7..c154b19a0c4594 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -14,7 +14,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include #include diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 0621920f7617f9..4bc5232eac1cb4 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -35,7 +35,7 @@ #include #include -#include "hda_codec.h" +#include #include "hda_controller.h" /* Defines for Nvidia Tegra HDA support */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 757857313426b1..bd77a91053d6c9 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -24,7 +24,7 @@ #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_beep.h" diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index c2d9ee9cfdc009..21d0f06109131a 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -22,7 +22,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 321e95c409c142..4225fcf790d782 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -32,7 +32,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index d6e079f4ec09d2..85cb4a68e1efb6 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -23,7 +23,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 1b2195dd2b2658..52642ba3e2c0d3 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -25,7 +25,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index f641c20095f71b..cdf780e1661caf 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -27,7 +27,7 @@ #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_beep.h" diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 8a49415aebacb7..cb18160499c2f1 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -41,7 +41,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f6af3e1c2b932d..ae0549169cd197 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -32,7 +32,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index f63acb1b965c0e..c49d25bcd7f2a9 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -27,7 +27,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" /* si3054 verbs */ diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 63d15b545b333b..cd65ee6a006405 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -32,7 +32,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_beep.h" diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index fc30d1e8aa76a6..5e2b84636ba00f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -52,7 +52,7 @@ #include #include #include -#include "hda_codec.h" +#include #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" From ef2a4bb6bd49bdc8429c2f4f5cb2dd013fc06402 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 22 Aug 2018 15:24:58 -0500 Subject: [PATCH 152/298] ASoC: Intel: common: add table for HDA-based platforms Expose a table containing machine driver information for HDAudio-based platforms handled by ASoC on Intel hardware. We only set constant values that are valid across multiple platforms. The firmware name used by the DSP will be set dynamically for each platform. The table is made of a single entry for now, if we need more complicated set-up where HDAudio is mixed with ACPI-enumerated devices (I2C, SoundWire) then we'd expect the differentiation to be handled through information provided by the BIOS (as done for KBL Chromebooks). Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- include/sound/soc-acpi-intel-match.h | 6 +++ sound/soc/intel/common/Makefile | 4 +- .../intel/common/soc-acpi-intel-hda-match.c | 40 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-hda-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 1de9ed784c1b23..022319ce3580c4 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -36,4 +36,10 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; +/* + * generic table used for HDA codec-based platforms, possibly with + * additional ACPI-enumerated codecs + */ +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_hda_machines[]; + #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index fd52419f1c814a..9511eb77c5eed3 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,9 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o \ + soc-acpi-intel-cnl-match.o \ + soc-acpi-intel-hda-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-hda-match.c b/sound/soc/intel/common/soc-acpi-intel-hda-match.c new file mode 100644 index 00000000000000..533c1064f84b33 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-hda-match.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, Intel Corporation. + +/* + * soc-apci-intel-hda-match.c - tables and support for HDA+ACPI enumeration. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata hda_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_hda_machines[] = { + { + /* .id is not used in this file */ + .drv_name = "skl_hda_dsp_generic", + + /* .fw_filename is dynamically set in skylake driver */ + + /* .sof_fw_filename is dynamically set in sof/intel driver */ + + .sof_tplg_filename = "intel/sof-hda-generic.tplg", + + /* + * .machine_quirk and .quirk_data are not used here but + * can be used if we need a more complicated machine driver + * combining HDA+other device (e.g. DMIC). + */ + .pdata = &hda_pdata, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_hda_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); From 8ca6d9c900410276b7e1aeb0ca2444a72b07e82a Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Wed, 22 Aug 2018 15:24:59 -0500 Subject: [PATCH 153/298] ASoC: Intel: Boards: Machine driver for SKL+ w/ HDAudio codecs Add machine driver for Intel platforms (SKL/KBL/BXT/APL) with HDA and iDisp codecs. This patch adds support for only iDisp (HDMI/DP) codec. In the following patches support for HDA codecs will be added. This should work for other Intel platforms as well e.g. GLK,CNL however this series is not tested on all the platforms. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 8 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/skl_hda_dsp_common.c | 103 +++++++++++++ sound/soc/intel/boards/skl_hda_dsp_common.h | 38 +++++ sound/soc/intel/boards/skl_hda_dsp_generic.c | 144 +++++++++++++++++++ sound/soc/intel/skylake/skl.h | 2 + 6 files changed, 297 insertions(+) create mode 100644 sound/soc/intel/boards/skl_hda_dsp_common.c create mode 100644 sound/soc/intel/boards/skl_hda_dsp_common.h create mode 100644 sound/soc/intel/boards/skl_hda_dsp_generic.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 585d6f6016292b..468588f683b4a6 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -325,6 +325,14 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for DA7219 + MAX98357A I2S audio codec. Say Y if you have such a device. + +config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH + tristate "SKL/KBL/BXT/APL with HDA Codecs" + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Intel platforms + SKL/KBL/BXT/APL with iDisp, HDA audio codecs. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 73f8efb98ee1fc..454bec41398137 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,6 +23,7 @@ snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o +snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl-rt274-objs := cnl_rt274.o @@ -53,4 +54,5 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c new file mode 100644 index 00000000000000..f9917e0f2ba8c3 --- /dev/null +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-18 Intel Corporation. + +/* + * Common functions used in different Intel machine drivers + */ +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" +#include "skl_hda_dsp_common.h" + +#define NAME_SIZE 32 + +int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct skl_hda_hdmi_pcm *pcm; + char dai_name[NAME_SIZE]; + + pcm = devm_kzalloc(card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + snprintf(dai_name, sizeof(dai_name), "intel-hdmi-hifi%d", + ctx->dai_index); + pcm->codec_dai = snd_soc_card_get_codec_dai(card, dai_name); + if (!pcm->codec_dai) + return -EINVAL; + + pcm->device = device; + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +/* skl_hda_digital audio interface glue - connects codec <--> CPU */ +struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS] = { + /* Back End DAI links */ + { + .name = "iDisp1", + .id = 1, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 2, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 3, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +int skl_hda_hdmi_jack_init(struct snd_soc_card *card) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + struct skl_hda_hdmi_pcm *pcm; + char jack_name[NAME_SIZE]; + int err; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &pcm->hdmi_jack, + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &pcm->hdmi_jack); + if (err < 0) + return err; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h new file mode 100644 index 00000000000000..b6c79696bfba1b --- /dev/null +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2015-18 Intel Corporation. + */ + +/* + * This file defines data structures used in Machine Driver for Intel + * platforms with HDA Codecs. + */ + +#ifndef __SOUND_SOC_HDA_DSP_COMMON_H +#define __SOUND_SOC_HDA_DSP_COMMON_H +#include +#include +#include +#include + +#define HDA_DSP_MAX_BE_DAI_LINKS 3 + +struct skl_hda_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + struct snd_soc_jack hdmi_jack; + int device; +}; + +struct skl_hda_private { + struct list_head hdmi_pcm_list; + int pcm_count; + int dai_index; + const char *platform_name; +}; + +extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS]; +int skl_hda_hdmi_jack_init(struct snd_soc_card *card); +int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device); + +#endif /* __SOUND_SOC_HDA_DSP_COMMON_H */ diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c new file mode 100644 index 00000000000000..920bc2ce22aa37 --- /dev/null +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-18 Intel Corporation. + +/* + * Machine Driver for SKL+ platforms with DSP and iDisp, HDA Codecs + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" +#include "skl_hda_dsp_common.h" + +static const struct snd_soc_dapm_route skl_hda_map[] = { + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, +}; + +static int skl_hda_card_late_probe(struct snd_soc_card *card) +{ + return skl_hda_hdmi_jack_init(card); +} + +static int +skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + int ret = 0; + + dev_dbg(card->dev, "%s: dai link name - %s\n", __func__, link->name); + link->platform_name = ctx->platform_name; + link->nonatomic = 1; + + if (strstr(link->name, "HDMI")) { + ret = skl_hda_hdmi_add_pcm(card, ctx->pcm_count); + + if (ret < 0) + return ret; + + ctx->dai_index++; + } + + ctx->pcm_count++; + return ret; +} + +static struct snd_soc_card hda_soc_card = { + .name = "skl_hda_card", + .owner = THIS_MODULE, + .dai_link = skl_hda_be_dai_links, + .dapm_routes = skl_hda_map, + .add_dai_link = skl_hda_add_dai_link, + .fully_routed = true, + .late_probe = skl_hda_card_late_probe, +}; + +#define IDISP_DAI_COUNT 3 +/* there are two routes per iDisp output */ +#define IDISP_ROUTE_COUNT (IDISP_DAI_COUNT * 2) +#define IDISP_CODEC_MASK 0x4 + +static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) +{ + struct snd_soc_card *card = &hda_soc_card; + u32 codec_count, codec_mask; + int i, num_links, num_route; + + codec_mask = pdata->codec_mask; + codec_count = hweight_long(codec_mask); + + if (codec_count == 1 && pdata->codec_mask & IDISP_CODEC_MASK) { + num_links = IDISP_DAI_COUNT; + num_route = IDISP_ROUTE_COUNT; + } else { + return -EINVAL; + } + + card->num_links = num_links; + card->num_dapm_routes = num_route; + + for (i = 0; i < num_links; i++) + skl_hda_be_dai_links[i].platform_name = pdata->platform; + + return 0; +} + +static int skl_hda_audio_probe(struct platform_device *pdev) +{ + struct skl_machine_pdata *pdata; + struct skl_hda_private *ctx; + int ret; + + dev_dbg(&pdev->dev, "%s: entry\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + pdata = dev_get_drvdata(&pdev->dev); + if (!pdata) + return -EINVAL; + + ret = skl_hda_fill_card_info(pdata); + if (ret < 0) { + dev_err(&pdev->dev, "Unsupported HDAudio/iDisp configuration found\n"); + return ret; + } + + ctx->pcm_count = hda_soc_card.num_links; + ctx->dai_index = 1; /* hdmi codec dai name starts from index 1 */ + ctx->platform_name = pdata->platform; + + hda_soc_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&hda_soc_card, ctx); + + return devm_snd_soc_register_card(&pdev->dev, &hda_soc_card); +} + +static struct platform_driver skl_hda_audio = { + .probe = skl_hda_audio_probe, + .driver = { + .name = "skl_hda_dsp_generic", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(skl_hda_audio) + +/* Module information */ +MODULE_DESCRIPTION("SKL/KBL/BXT/APL HDA Generic Machine driver"); +MODULE_AUTHOR("Rakesh Ughreja "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_hda_dsp_generic"); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 78aa8bdcb61929..4105a9371b6425 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -117,6 +117,8 @@ struct skl_dma_params { struct skl_machine_pdata { u32 dmic_num; bool use_tplg_pcm; /* use dais and dai links from topology */ + const char *platform; + u32 codec_mask; }; struct skl_dsp_ops { From 9dcd5d56aef272a01ce5382a37531340c354814f Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Wed, 22 Aug 2018 15:25:00 -0500 Subject: [PATCH 154/298] ASoC: Intel: Skylake: use HDAudio if ACPI enumeration fails When no I2S based codec entries are found in the BIOS, check if there are any HDA codecs detected on the bus. Based on the number of codecs found take appropriate action in machine driver. If there are two HDA codecs i.e. iDisp + HDA found on the bus, register DAIs and DAI links for both. If only one codec i.e. iDisp is found then load only iDisp machine driver. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 38 ++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index dce64948564940..c83194de4aed82 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -472,6 +472,25 @@ static struct skl_ssp_clk skl_ssp_clks[] = { {.name = "ssp5_sclkfs"}, }; +static struct snd_soc_acpi_mach *skl_find_hda_machine(struct skl *skl, + struct snd_soc_acpi_mach *machines) +{ + struct hdac_bus *bus = skl_to_bus(skl); + struct snd_soc_acpi_mach *mach; + + /* check if we have any codecs detected on bus */ + if (bus->codec_mask == 0) + return NULL; + + /* point to common table */ + mach = snd_soc_acpi_intel_hda_machines; + + /* all entries in the machine table use the same firmware */ + mach->fw_filename = machines->fw_filename; + + return mach; +} + static int skl_find_machine(struct skl *skl, void *driver_data) { struct hdac_bus *bus = skl_to_bus(skl); @@ -479,9 +498,13 @@ static int skl_find_machine(struct skl *skl, void *driver_data) 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; + if (!mach) { + dev_dbg(bus->dev, "No matching I2S machine driver found\n"); + mach = skl_find_hda_machine(skl, driver_data); + if (!mach) { + dev_err(bus->dev, "No matching machine driver found\n"); + return -ENODEV; + } } skl->mach = mach; @@ -498,8 +521,9 @@ static int skl_find_machine(struct skl *skl, void *driver_data) static int skl_machine_device_register(struct skl *skl) { - struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach = skl->mach; + struct hdac_bus *bus = skl_to_bus(skl); + struct skl_machine_pdata *pdata; struct platform_device *pdev; int ret; @@ -516,8 +540,12 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } - if (mach->pdata) + if (mach->pdata) { + pdata = (struct skl_machine_pdata *)mach->pdata; + pdata->platform = dev_name(bus->dev); + pdata->codec_mask = bus->codec_mask; dev_set_drvdata(&pdev->dev, mach->pdata); + } skl->i2s_dev = pdev; From 1df31d7020d30ee402bc08df799832598b80bf14 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Wed, 22 Aug 2018 15:25:01 -0500 Subject: [PATCH 155/298] ASoC: Intel: Skylake: add HDA BE DAIs Add support for HDA BE DAIs in SKL platform driver. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 70 +++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 823e39103edd30..00b7a91b18c9a5 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -32,6 +32,7 @@ #define HDA_MONO 1 #define HDA_STEREO 2 #define HDA_QUAD 4 +#define HDA_MAX 8 static const struct snd_pcm_hardware azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | @@ -569,7 +570,10 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, stream_tag = hdac_stream(link_dev)->stream_tag; /* set the stream tag in the codec dai dma params */ - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); + else + snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -995,21 +999,63 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { - .name = "HD-Codec Pin", + .name = "Analog CPU DAI", .ops = &skl_link_dai_ops, .playback = { - .stream_name = "HD-Codec Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .stream_name = "Analog CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { - .stream_name = "HD-Codec Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .stream_name = "Analog CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "Alt Analog CPU DAI", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "Alt Analog CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Alt Analog CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "Digital CPU DAI", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "Digital CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Digital CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, }; From 2b8efe4aa6061f761985bc12e59d4fb48b707d9a Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Wed, 22 Aug 2018 15:25:02 -0500 Subject: [PATCH 156/298] ASoC: Intel: Skylake: use hda_bus instead of hdac_bus Use hda_bus instead of hdac_bus in the SKL ASoC platform driver to enable reuse of legacy HDA codec drivers. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 11 +++++++++-- sound/soc/intel/skylake/skl.h | 10 +++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index c83194de4aed82..fe261aad622759 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -673,7 +674,7 @@ static int probe_codec(struct hdac_bus *bus, int addr) mutex_unlock(&bus->cmd_mutex); if (res == -1) return -EIO; - dev_dbg(bus->dev, "codec #%d probed OK\n", addr); + dev_dbg(bus->dev, "codec #%d probed OK: %x\n", addr, res); hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL); if (!hdev) @@ -816,7 +817,7 @@ static int skl_create(struct pci_dev *pci, { struct skl *skl; struct hdac_bus *bus; - + struct hda_bus *hbus; int err; *rskl = NULL; @@ -831,6 +832,7 @@ static int skl_create(struct pci_dev *pci, return -ENOMEM; } + hbus = skl_to_hbus(skl); bus = skl_to_bus(skl); snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, NULL); bus->use_posbuf = 1; @@ -838,6 +840,11 @@ static int skl_create(struct pci_dev *pci, INIT_WORK(&skl->probe_work, skl_probe_work); bus->bdl_pos_adj = 0; + mutex_init(&hbus->prepare_mutex); + hbus->pci = pci; + hbus->mixer_assigned = -1; + hbus->modelname = "sklbus"; + *rskl = skl; return 0; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4105a9371b6425..8d48cd7c56c835 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -23,6 +23,7 @@ #include #include +#include #include #include "skl-nhlt.h" #include "skl-ssp-clk.h" @@ -71,7 +72,7 @@ struct skl_fw_config { }; struct skl { - struct hdac_bus hbus; + struct hda_bus hbus; struct pci_dev *pci; unsigned int init_done:1; /* delayed init status */ @@ -105,8 +106,11 @@ struct skl { struct snd_soc_acpi_mach *mach; }; -#define skl_to_bus(s) (&(s)->hbus) -#define bus_to_skl(bus) container_of(bus, struct skl, hbus) +#define skl_to_bus(s) (&(s)->hbus.core) +#define bus_to_skl(bus) container_of(bus, struct skl, hbus.core) + +#define skl_to_hbus(s) (&(s)->hbus) +#define hbus_to_skl(hbus) container_of((hbus), struct skl, (hbus)) /* to pass dai dma data */ struct skl_dma_params { From 38acdc5e125b9311ddc576878d29a1361225920d Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Wed, 22 Aug 2018 15:25:03 -0500 Subject: [PATCH 157/298] ASoC: hdac_hda: add asoc extension for legacy HDA codec drivers This patch adds a kernel module which is used by the legacy HDA codec drivers as library. This implements hdac_ext_bus_ops to enable the reuse of legacy HDA codec drivers with ASoC platform drivers. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/pci/hda/hda_bind.c | 12 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/hdac_hda.c | 484 +++++++++++++++++++ sound/soc/codecs/hdac_hda.h | 24 + sound/soc/intel/boards/Kconfig | 1 + sound/soc/intel/boards/skl_hda_dsp_common.c | 24 + sound/soc/intel/boards/skl_hda_dsp_common.h | 2 +- sound/soc/intel/boards/skl_hda_dsp_generic.c | 38 ++ sound/soc/intel/skylake/skl.c | 47 +- 10 files changed, 634 insertions(+), 5 deletions(-) create mode 100644 sound/soc/codecs/hdac_hda.c create mode 100644 sound/soc/codecs/hdac_hda.h diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 2222b47d4ec4fb..9174f1b3a987f3 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -81,6 +81,12 @@ static int hda_codec_driver_probe(struct device *dev) hda_codec_patch_t patch; int err; + if (codec->bus->core.ext_ops) { + if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach)) + return -EINVAL; + return codec->bus->core.ext_ops->hdev_attach(&codec->core); + } + if (WARN_ON(!codec->preset)) return -EINVAL; @@ -134,6 +140,12 @@ static int hda_codec_driver_remove(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); + if (codec->bus->core.ext_ops) { + if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) + return -EINVAL; + return codec->bus->core.ext_ops->hdev_detach(&codec->core); + } + if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f38a62178cfa76..e997881db3c1b0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ES7134 select SND_SOC_GTM601 select SND_SOC_HDAC_HDMI + select SND_SOC_HDAC_HDA select SND_SOC_ICS43432 select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C @@ -615,6 +616,10 @@ config SND_SOC_HDAC_HDMI select SND_PCM_ELD select HDMI +config SND_SOC_HDAC_HDA + tristate + select SND_HDA + config SND_SOC_ICS43432 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4f4033353633c4..d5bbdf7c92dbeb 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -77,6 +77,7 @@ snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o snd-soc-gtm601-objs := gtm601.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o +snd-soc-hdac-hda-objs := hdac_hda.o snd-soc-ics43432-objs := ics43432.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o @@ -336,6 +337,7 @@ obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o +obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c new file mode 100644 index 00000000000000..8c25a1332fa7ee --- /dev/null +++ b/sound/soc/codecs/hdac_hda.c @@ -0,0 +1,484 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "hdac_hda.h" + +#define HDAC_ANALOG_DAI_ID 0 +#define HDAC_DIGITAL_DAI_ID 1 +#define HDAC_ALT_ANALOG_DAI_ID 2 + +#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) + +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_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width); +static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, + struct snd_soc_dai *dai); + +static 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_free = hdac_hda_dai_hw_free, + .set_tdm_slot = hdac_hda_dai_set_tdm_slot, +}; + +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 int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hdac_hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = &hda_pvt->pcm[dai->id]; + if (tx_mask) + pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; + else + pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + + 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 hdac_hda_priv *hda_pvt; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_device *hdev; + struct hda_pcm_stream *hda_stream; + 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]; + + format_val = snd_hdac_calc_stream_format(runtime->rate, + runtime->channels, + runtime->format, + hda_stream->maxbps, + 0); + if (!format_val) { + dev_err(&hdev->dev, + "invalid format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + return -EINVAL; + } + + stream = hda_pvt->pcm[dai->id].stream_tag[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; + int ret; + + 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]; + + ret = hda_stream->ops.open(hda_stream, &hda_pvt->codec, substream); + if (ret < 0) + snd_hda_codec_pcm_put(pcm); + + return ret; +} + +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; + + 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; + 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 (strpbrk(cpcm->name, pcm_name)) + return cpcm; + } + + dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name); + return NULL; +} + +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_link(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); + + ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card, + hdev->addr, hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "failed to create hda codec %d\n", ret); + goto error_no_pm; + } + + /* + * 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; + } + + ret = snd_hdac_regmap_init(&hcodec->core); + if (ret < 0) { + dev_err(&hdev->dev, "regmap init failed\n"); + goto error; + } + + 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; + } + } 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; + } + + ret = snd_hda_codec_build_controls(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to create controls %d\n", ret); + goto error; + } + + hcodec->core.lazy_cache = true; + + /* + * 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: + 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 hdac_ext_link *hlink = NULL; + + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return; + } + + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + pm_runtime_disable(&hdev->dev); +} + +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, + .idle_bias_on = false, + .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), +}; + +static int hdac_hda_dev_probe(struct hdac_device *hdev) +{ + struct hdac_ext_link *hlink; + struct hdac_hda_priv *hda_pvt; + int ret; + + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_link(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); + + hda_pvt = hdac_to_hda_priv(hdev); + if (!hda_pvt) + return -ENOMEM; + + /* ASoC specific initialization */ + ret = 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; + } + + dev_set_drvdata(&hdev->dev, hda_pvt); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + + return ret; +} + +static int hdac_hda_dev_remove(struct hdac_device *hdev) +{ + snd_soc_unregister_component(&hdev->dev); + 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"); diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h new file mode 100644 index 00000000000000..e444ef5933606c --- /dev/null +++ b/sound/soc/codecs/hdac_hda.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2015-18 Intel Corporation. + */ + +#ifndef __HDAC_HDA_H__ +#define __HDAC_HDA_H__ + +struct hdac_hda_pcm { + int stream_tag[2]; +}; + +struct hdac_hda_priv { + struct hda_codec codec; + struct hdac_hda_pcm pcm[2]; +}; + +#define hdac_to_hda_priv(_hdac) \ + container_of(_hdac, struct hdac_hda_priv, codec.core) +#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core) + +struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void); + +#endif /* __HDAC_HDA_H__ */ diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 468588f683b4a6..ce36a535ac0408 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -329,6 +329,7 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH tristate "SKL/KBL/BXT/APL with HDA Codecs" select SND_SOC_HDAC_HDMI + select SND_SOC_HDAC_HDA help This adds support for ASoC machine driver for Intel platforms SKL/KBL/BXT/APL with iDisp, HDA audio codecs. diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c index f9917e0f2ba8c3..3fdbf239da741b 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.c +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -69,6 +69,30 @@ struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS] = { .dpcm_playback = 1, .no_pcm = 1, }, + { + .name = "Analog Playback and Capture", + .id = 4, + .cpu_dai_name = "Analog CPU DAI", + .codec_name = "ehdaudio0D0", + .codec_dai_name = "Analog Codec DAI", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = NULL, + .no_pcm = 1, + }, + { + .name = "Digital Playback and Capture", + .id = 5, + .cpu_dai_name = "Digital CPU DAI", + .codec_name = "ehdaudio0D0", + .codec_dai_name = "Digital Codec DAI", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = NULL, + .no_pcm = 1, + }, }; int skl_hda_hdmi_jack_init(struct snd_soc_card *card) diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h index b6c79696bfba1b..87c50aff56cdd7 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.h +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -15,7 +15,7 @@ #include #include -#define HDA_DSP_MAX_BE_DAI_LINKS 3 +#define HDA_DSP_MAX_BE_DAI_LINKS 5 struct skl_hda_hdmi_pcm { struct list_head head; diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 920bc2ce22aa37..b213e9b47505fb 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -16,6 +16,15 @@ #include "../skylake/skl.h" #include "skl_hda_dsp_common.h" +static const struct snd_soc_dapm_widget skl_hda_widgets[] = { + SND_SOC_DAPM_HP("Analog Out", NULL), + SND_SOC_DAPM_MIC("Analog In", NULL), + SND_SOC_DAPM_HP("Alt Analog Out", NULL), + SND_SOC_DAPM_MIC("Alt Analog In", NULL), + SND_SOC_DAPM_SPK("Digital Out", NULL), + SND_SOC_DAPM_MIC("Digital In", NULL), +}; + static const struct snd_soc_dapm_route skl_hda_map[] = { { "hifi3", NULL, "iDisp3 Tx"}, { "iDisp3 Tx", NULL, "iDisp3_out"}, @@ -23,6 +32,29 @@ static const struct snd_soc_dapm_route skl_hda_map[] = { { "iDisp2 Tx", NULL, "iDisp2_out"}, { "hifi1", NULL, "iDisp1 Tx"}, { "iDisp1 Tx", NULL, "iDisp1_out"}, + + { "Analog Out", NULL, "Codec Output Pin1" }, + { "Digital Out", NULL, "Codec Output Pin2" }, + { "Alt Analog Out", NULL, "Codec Output Pin3" }, + + { "Codec Input Pin1", NULL, "Analog In" }, + { "Codec Input Pin2", NULL, "Digital In" }, + { "Codec Input Pin3", NULL, "Alt Analog In" }, + + /* CODEC BE connections */ + { "Analog Codec Playback", NULL, "Analog CPU Playback" }, + { "Analog CPU Playback", NULL, "codec0_out" }, + { "Digital Codec Playback", NULL, "Digital CPU Playback" }, + { "Digital CPU Playback", NULL, "codec1_out" }, + { "Alt Analog Codec Playback", NULL, "Alt Analog CPU Playback" }, + { "Alt Analog CPU Playback", NULL, "codec2_out" }, + + { "codec0_in", NULL, "Analog CPU Capture" }, + { "Analog CPU Capture", NULL, "Analog Codec Capture" }, + { "codec1_in", NULL, "Digital CPU Capture" }, + { "Digital CPU Capture", NULL, "Digital Codec Capture" }, + { "codec2_in", NULL, "Alt Analog CPU Capture" }, + { "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" }, }; static int skl_hda_card_late_probe(struct snd_soc_card *card) @@ -57,6 +89,7 @@ static struct snd_soc_card hda_soc_card = { .name = "skl_hda_card", .owner = THIS_MODULE, .dai_link = skl_hda_be_dai_links, + .dapm_widgets = skl_hda_widgets, .dapm_routes = skl_hda_map, .add_dai_link = skl_hda_add_dai_link, .fully_routed = true, @@ -80,6 +113,11 @@ static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) if (codec_count == 1 && pdata->codec_mask & IDISP_CODEC_MASK) { num_links = IDISP_DAI_COUNT; num_route = IDISP_ROUTE_COUNT; + } else if (codec_count == 2 && codec_mask & IDISP_CODEC_MASK) { + num_links = ARRAY_SIZE(skl_hda_be_dai_links); + num_route = ARRAY_SIZE(skl_hda_map), + card->dapm_widgets = skl_hda_widgets; + card->num_dapm_widgets = ARRAY_SIZE(skl_hda_widgets); } else { return -EINVAL; } diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index fe261aad622759..d174cbe35f7a20 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -37,6 +37,7 @@ #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "../../../soc/codecs/hdac_hda.h" /* * initialize the PCI registers @@ -657,6 +658,24 @@ static void skl_clock_device_unregister(struct skl *skl) platform_device_unregister(skl->clk_dev); } +#define IDISP_INTEL_VENDOR_ID 0x80860000 + +/* + * load the legacy codec driver + */ +static void load_codec_module(struct hda_codec *codec) +{ +#ifdef MODULE + char modalias[MODULE_NAME_LEN]; + const char *mod = NULL; + + snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); + mod = modalias; + dev_dbg(&codec->core.dev, "loading %s codec module\n", mod); + request_module(mod); +#endif +} + /* * Probe the given codec address */ @@ -666,7 +685,9 @@ static int probe_codec(struct hdac_bus *bus, int addr) (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; struct skl *skl = bus_to_skl(bus); + struct hdac_hda_priv *hda_codec; struct hdac_device *hdev; + int err; mutex_lock(&bus->cmd_mutex); snd_hdac_bus_send_cmd(bus, cmd); @@ -676,11 +697,24 @@ static int probe_codec(struct hdac_bus *bus, int addr) return -EIO; dev_dbg(bus->dev, "codec #%d probed OK: %x\n", addr, res); - hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL); - if (!hdev) + hda_codec = devm_kzalloc(&skl->pci->dev, sizeof(*hda_codec), + GFP_KERNEL); + if (!hda_codec) return -ENOMEM; - return snd_hdac_ext_bus_device_init(bus, addr, hdev); + hda_codec->codec.bus = skl_to_hbus(skl); + hdev = &hda_codec->codec.core; + + err = snd_hdac_ext_bus_device_init(bus, addr, hdev); + if (err < 0) + return err; + + /* use legacy bus only for HDA codecs, idisp uses ext bus */ + if ((res & 0xFFFF0000) != IDISP_INTEL_VENDOR_ID) { + hdev->type = HDA_DEV_LEGACY; + load_codec_module(&hda_codec->codec); + } + return 0; } /* Codec initialization */ @@ -815,6 +849,7 @@ static int skl_create(struct pci_dev *pci, const struct hdac_io_ops *io_ops, struct skl **rskl) { + struct hdac_ext_bus_ops *ext_ops = NULL; struct skl *skl; struct hdac_bus *bus; struct hda_bus *hbus; @@ -834,7 +869,11 @@ static int skl_create(struct pci_dev *pci, hbus = skl_to_hbus(skl); bus = skl_to_bus(skl); - snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, NULL); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) + ext_ops = snd_soc_hdac_hda_get_ops(); +#endif + snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, ext_ops); bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); From bafb59032343459cc4019cb65e515595451357e7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 29 Aug 2018 17:25:01 +0100 Subject: [PATCH 158/298] ASoC: SOF: debug: Use signed type for PM return value. Signed-off-by: Liam Girdwood --- sound/soc/sof/debug.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 74d6f66eef782d..67c195573d795c 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -37,7 +37,7 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, { struct snd_sof_dfsentry_io *dfse = file->private_data; struct snd_sof_dev *sdev = dfse->sdev; - int size; + int size, err; u32 *buf; loff_t pos = *ppos; size_t ret; @@ -59,11 +59,11 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, return -ENOMEM; /* copy from DSP MMIO */ - ret = pm_runtime_get_sync(sdev->dev); - if (ret < 0) { - dev_err(sdev->dev, "error: debugFS failed to resume %zd\n", - ret); - return ret; + err = pm_runtime_get_sync(sdev->dev); + if (err < 0) { + dev_err(sdev->dev, "error: debugFS failed to resume %d\n", + err); + return err; } memcpy_fromio(buf, dfse->buf + pos, size); From 50599f9930695ef6626ac589b4710c41e89a8dd5 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 23 Aug 2018 13:25:32 +0800 Subject: [PATCH 159/298] ALSA: HDA: Fix several mismatch for register mask and value E.g. for snd_hdac_ext_link_clear_stream_id(), we should set (1 << stream) as mask, and 0 as value, here correct it and several similar mismatches. And, here also remove unreadable register_mask usage for those mask value updating. Signed-off-by: Keyon Jie --- sound/hda/ext/hdac_ext_stream.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 1bd27576db98d5..a835558ddbc9b5 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -146,7 +146,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); */ void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) { - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_RUN); + snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); @@ -171,7 +172,8 @@ void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) snd_hdac_ext_link_stream_clear(stream); - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_STRST); + snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); udelay(3); timeout = 50; do { @@ -242,7 +244,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id); void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, int stream) { - snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, 0, (1 << stream)); + snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); @@ -415,7 +417,6 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, bool enable, int index) { u32 mask = 0; - u32 register_mask = 0; if (!bus->spbcap) { dev_err(bus->dev, "Address of SPB capability is NULL\n"); @@ -424,12 +425,8 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, mask |= (1 << index); - register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL); - - mask |= register_mask; - if (enable) - snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask); + snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask); else snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); } @@ -503,7 +500,6 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, bool enable, int index) { u32 mask = 0; - u32 register_mask = 0; if (!bus->drsmcap) { dev_err(bus->dev, "Address of DRSM capability is NULL\n"); @@ -512,12 +508,8 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, mask |= (1 << index); - register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL); - - mask |= register_mask; - if (enable) - snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, 0, mask); + snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask); else snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); } From 481177e616143a767e76c28c4cceafc24b594808 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 27 Aug 2018 22:19:32 +0800 Subject: [PATCH 160/298] ASoC: SOF: Define sof_create_platform_device for platform device creation Define a function sof_create_platform_device() to register the platform device "sof-audio". Signed-off-by: Mengdong Lin Signed-off-by: Keyon Jie --- sound/soc/sof/sof-priv.h | 5 +++++ sound/soc/sof/utils.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bd33e9437d0222..fe3da70b1ec1ea 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -516,4 +516,9 @@ static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) } extern const struct sof_arch_ops sof_xtensa_arch_ops; + +/* + * Utilities + */ +int sof_create_platform_device(struct sof_platform_priv *priv); #endif diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c index a38cd0c01f80e8..23597064b8e33b 100644 --- a/sound/soc/sof/utils.c +++ b/sound/soc/sof/utils.c @@ -9,6 +9,7 @@ // #include +#include #include #include #include "sof-priv.h" @@ -47,3 +48,21 @@ int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, } EXPORT_SYMBOL(sof_bes_setup); +/* register sof platform device */ +int sof_create_platform_device(struct sof_platform_priv *priv) +{ + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + struct device *dev = sof_pdata->dev; + + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + return PTR_ERR(priv->pdev_pcm); + } + + return 0; +} +EXPORT_SYMBOL(sof_create_platform_device); From a9b06dd724a4e4080b72cbdf0aed57ee24016373 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 9 Aug 2018 13:16:42 +0800 Subject: [PATCH 161/298] ASoC: SOF: refine and cleanup for request_firmware 1. unify to request firmware in ops.load_firmware(). 2. fix request twice issue for skl+ platforms. Signed-off-by: Keyon Jie --- sound/soc/sof/core.c | 2 +- sound/soc/sof/intel/hda-loader.c | 19 +++-------------- sound/soc/sof/intel/hda.h | 3 +-- sound/soc/sof/loader.c | 35 ++++++++++++++++++++++++-------- sound/soc/sof/pm.c | 2 +- sound/soc/sof/sof-acpi-dev.c | 34 ++++--------------------------- sound/soc/sof/sof-pci-dev.c | 33 +++--------------------------- sound/soc/sof/sof-priv.h | 6 +++--- sound/soc/sof/sof-spi-dev.c | 34 ++++++------------------------- 9 files changed, 49 insertions(+), 119 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 070a8d3799a8d7..437ecdde749fa8 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -289,7 +289,7 @@ static int sof_probe(struct platform_device *pdev) } /* load the firmware */ - ret = snd_sof_load_firmware(sdev, plat_data->fw, true); + ret = snd_sof_load_firmware(sdev, true); if (ret < 0) { dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", ret); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 5dc24b9067d278..f7c58755dcfc94 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -287,28 +287,15 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) return status; } -int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw, - bool first_boot) +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, bool first_boot) { struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); - int ret; /* set code loading condition to true */ sdev->code_loading = 1; - ret = request_firmware(&plat_data->fw, - plat_data->machine->sof_fw_filename, sdev->dev); - - if (ret < 0) { - dev_err(sdev->dev, "error: request firmware failed err: %d\n", - ret); - return -EINVAL; - } - - if (!plat_data->fw) - return -EINVAL; - - return ret; + return request_firmware(&plat_data->fw, + plat_data->machine->sof_fw_filename, sdev->dev); } int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 2f3bc45efb10d0..4d7e0a40aa33fe 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -483,8 +483,7 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); /* * DSP Code loader. */ -int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw, - bool first_boot); +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, bool first_boot); int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); /* diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 03bbdc342b8ee4..d5839c0f3356ce 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "sof-priv.h" #include "ops.h" @@ -208,42 +209,60 @@ static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw) } int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, - const struct firmware *fw, bool first_boot) + bool first_boot) { + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); int ret; + /* set code loading condition to true */ + sdev->code_loading = 1; + + ret = request_firmware(&plat_data->fw, + plat_data->machine->sof_fw_filename, sdev->dev); + + if (ret < 0) { + dev_err(sdev->dev, "error: request firmware failed err: %d\n", + ret); + return ret; + } + /* make sure the FW header and file is valid */ - ret = check_header(sdev, fw); + ret = check_header(sdev, plat_data->fw); if (ret < 0) { dev_err(sdev->dev, "error: invalid FW header\n"); - return ret; + goto error; } /* prepare the DSP for FW loading */ ret = snd_sof_dsp_reset(sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to reset DSP\n"); - return ret; + goto error; } /* parse and load firmware modules to DSP */ - ret = load_modules(sdev, fw); + ret = load_modules(sdev, plat_data->fw); if (ret < 0) { dev_err(sdev->dev, "error: invalid FW modules\n"); - return ret; + goto error; } + return 0; + +error: + release_firmware(plat_data->fw); return ret; + } EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); int snd_sof_load_firmware(struct snd_sof_dev *sdev, - const struct firmware *fw, bool first_boot) + bool first_boot) { dev_dbg(sdev->dev, "loading firmware\n"); if (sdev->ops->load_firmware) - return sdev->ops->load_firmware(sdev, fw, first_boot); + return sdev->ops->load_firmware(sdev, first_boot); return 0; } EXPORT_SYMBOL(snd_sof_load_firmware); diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index bdbd7084e8895a..0ebc3fc8cd9d6a 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -224,7 +224,7 @@ static int sof_resume(struct device *dev) } /* load the firmware */ - ret = snd_sof_load_firmware(sdev, sdev->pdata->fw, false); + ret = snd_sof_load_firmware(sdev, false); if (ret < 0) { dev_err(sdev->dev, "error: failed to load DSP firmware after resume %d\n", diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index b1bf0ee9c9b427..3d6750526860bf 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -134,30 +134,6 @@ static struct platform_device * return pdev; } -static void sof_acpi_fw_cb(const struct firmware *fw, void *context) -{ - struct sof_platform_priv *priv = context; - struct snd_sof_pdata *sof_pdata = priv->sof_pdata; - const struct snd_soc_acpi_mach *mach = sof_pdata->machine; - struct device *dev = sof_pdata->dev; - - sof_pdata->fw = fw; - if (!fw) { - dev_err(dev, "Cannot load firmware %s\n", - mach->sof_fw_filename); - return; - } - - /* register PCM and DAI driver */ - priv->pdev_pcm = - platform_device_register_data(dev, "sof-audio", -1, - sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(priv->pdev_pcm)) { - dev_err(dev, "Cannot register device sof-audio. Error %d\n", - (int)PTR_ERR(priv->pdev_pcm)); - } -} - static const struct dev_pm_ops sof_acpi_pm = { SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, @@ -291,14 +267,12 @@ static int sof_acpi_probe(struct platform_device *pdev) dev_dbg(dev, "created machine %s\n", dev_name(&sof_pdata->pdev_mach->dev)); - /* continue SST probing after firmware is loaded */ - dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); - ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, - dev, GFP_KERNEL, priv, sof_acpi_fw_cb); + /* register sof-audio platform driver */ + ret = sof_create_platform_device(priv); if (ret) { platform_device_unregister(sof_pdata->pdev_mach); - dev_err(dev, "error: failed to load firmware %s\n", - mach->sof_fw_filename); + dev_err(dev, "error: failed to create platform device!\n"); + return ret; } /* allow runtime_pm */ diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index c5371faa08e641..d2ec640306f305 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -127,30 +127,6 @@ static const struct sof_dev_desc icl_desc = { }; #endif -static void sof_pci_fw_cb(const struct firmware *fw, void *context) -{ - struct sof_platform_priv *priv = context; - struct snd_sof_pdata *sof_pdata = priv->sof_pdata; - const struct snd_soc_acpi_mach *mach = sof_pdata->machine; - struct device *dev = sof_pdata->dev; - - sof_pdata->fw = fw; - if (!fw) { - dev_err(dev, "Cannot load firmware %s\n", - mach->sof_fw_filename); - return; - } - - /* register PCM and DAI driver */ - priv->pdev_pcm = - platform_device_register_data(dev, "sof-audio", -1, - sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(priv->pdev_pcm)) { - dev_err(dev, "Cannot register device sof-audio. Error %d\n", - (int)PTR_ERR(priv->pdev_pcm)); - } -} - static const struct dev_pm_ops sof_pci_pm = { SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, @@ -282,14 +258,11 @@ static int sof_pci_probe(struct pci_dev *pci, dev_dbg(dev, "created machine %s\n", dev_name(&sof_pdata->pdev_mach->dev)); - /* continue probing after firmware is loaded */ - dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); - ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, - dev, GFP_KERNEL, priv, sof_pci_fw_cb); + /* register sof-audio platform driver */ + ret = sof_create_platform_device(priv); if (ret) { platform_device_unregister(sof_pdata->pdev_mach); - dev_err(dev, "error: failed to load firmware %s\n", - mach->sof_fw_filename); + dev_err(dev, "error: failed to create platform device!\n"); goto release_regions; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index fe3da70b1ec1ea..ea9aca9de96a52 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -140,7 +140,7 @@ struct snd_sof_dsp_ops { /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, - const struct firmware *fw, bool first_boot); + bool first_boot); int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); @@ -391,9 +391,9 @@ int snd_sof_create_page_table(struct snd_sof_dev *sdev, * Firmware loading. */ int snd_sof_load_firmware(struct snd_sof_dev *sdev, - const struct firmware *fw, bool first_boot); + bool first_boot); int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, - const struct firmware *fw, bool first_boot); + bool first_boot); int snd_sof_run_firmware(struct snd_sof_dev *sdev); int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, struct snd_sof_mod_hdr *module); diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index c0e651ce48376c..c55be66267f6a4 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -19,30 +19,6 @@ #include #include "sof-priv.h" -static void sof_spi_fw_cb(const struct firmware *fw, void *context) -{ - struct sof_platform_priv *priv = context; - struct snd_sof_pdata *sof_pdata = priv->sof_pdata; - const struct snd_sof_machine *mach = sof_pdata->machine; - struct device *dev = sof_pdata->dev; - - sof_pdata->fw = fw; - if (!fw) { - dev_err(dev, "Cannot load firmware %s\n", - mach->sof_fw_filename); - return; - } - - /* register PCM and DAI driver */ - priv->pdev_pcm = - platform_device_register_data(dev, "sof-audio", -1, - sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(priv->pdev_pcm)) { - dev_err(dev, "Cannot register device sof-audio. Error %d\n", - (int)PTR_ERR(priv->pdev_pcm)); - } -} - static const struct dev_pm_ops sof_spi_pm = { SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, @@ -101,11 +77,13 @@ static int sof_spi_probe(struct spi_device *spi) dev_dbg(dev, "created machine %s\n", dev_name(&sof_pdata->pdev_mach->dev)); - /* continue probing after firmware is loaded */ - ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, - dev, GFP_KERNEL, priv, sof_spi_fw_cb); - if (ret) + /* register sof-audio platform driver */ + ret = sof_create_platform_device(priv); + if (ret) { platform_device_unregister(sof_pdata->pdev_mach); + dev_err(dev, "error: failed to create platform device!\n"); + return ret; + } /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY); From 0a688443f1f1a4b67e1e5179e386c5aa79ee011d Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 20 Jul 2018 20:17:46 +0800 Subject: [PATCH 162/298] ASoC: SOF: add hda_bus to struct sof_intel_hda_dev to handle hda with sof Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda.h | 4 ++++ sound/soc/sof/sof-priv.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 4d7e0a40aa33fe..acb6fe4dcebf10 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -11,6 +11,8 @@ #ifndef __SOF_INTEL_HDA_H #define __SOF_INTEL_HDA_H +#include + /* PCI registers */ #define PCI_TCSEL 0x44 #define PCI_CGCTL 0x48 @@ -370,6 +372,8 @@ struct sof_intel_hda_stream { /* represents DSP HDA controller frontend - i.e. host facing control */ struct sof_intel_hda_dev { + struct hda_bus hbus; + /* hw config */ const struct sof_intel_dsp_desc *desc; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ea9aca9de96a52..e8b0c65b1b4a3e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -362,6 +362,9 @@ struct snd_sof_dev { void *private; /* core does not touch this */ }; +#define sof_to_bus(s) (&(s)->hda->hbus.core) +#define sof_to_hbus(s) (&(s)->hda->hbus) + /* * SOF platform private struct used as drvdata of * platform dev (e.g. pci/acpi/spi...) drvdata. From e66de013c60f42979179aa57b33d7097aa948a31 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 20 Jul 2018 20:17:47 +0800 Subject: [PATCH 163/298] ASoC: SOF: hda-stream: add CORB/RIRB ringbuffers init for HDA. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-stream.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 620a023c17384a..e3a3220c14261d 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -525,6 +525,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) return -ENOMEM; } + /* mem alloc for the CORB/RIRB ringbuffers */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + PAGE_SIZE, &sdev->hda->hbus.core.rb); + if (ret < 0) { + dev_err(sdev->dev, "error: RB alloc failed\n"); + return -ENOMEM; + } + /* create capture streams */ for (i = 0; i < num_capture; i++) { stream = &hdev->cstream[i]; From 4677968266d3b9d90dedabf54eefdea84faff289 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 20 Jul 2018 20:17:45 +0800 Subject: [PATCH 164/298] ASoC: SOF: Add Kconfig item for SOF HDA. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 85750054a5c90c..a50bb59139c843 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -101,4 +101,15 @@ config SND_SOC_SOF_HDA_COMMON select SND_SOC_SOF_PCI select SND_SOC_ACPI_INTEL_MATCH +config SND_SOC_SOF_HDA + tristate "SOF support for HDA Links(HDA/HDMI)" + depends on SND_SOC_SOF_HDA_COMMON + select SND_SOC_HDAC_HDA + select SND_SOC_HDAC_HDMI + help + This adds support for HDA links(HDA/HDMI) with Sound Open Firmware + for Intel(R) platforms. + Say Y if you want to enble HDA links with SOF. + If unsure select "N". + endif ## SND_SOC_SOF_INTEL From ad91b061548554e6cf5153812bf544ac0819e232 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 31 Aug 2018 15:00:22 +0800 Subject: [PATCH 165/298] ALSA: HDA: export process_unsol_events() which can be used without hdac_bus library. In some cases, we want to initialize hda bus without this hdac_bus library(e.g. for non-HDA I2S only usuage on Intel Skylake+ platforms), where we will do hdac bus non-HDA-only implementation in BSD license. When we want to add hda link support(GPL) in this implementation, we need reuse this process_unsol_events(), so export it is better solution comparing to implement it again. Signed-off-by: Keyon Jie --- include/sound/hdaudio.h | 1 + sound/hda/hdac_bus.c | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index f1baaa88e766df..f3e33a619508d0 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -361,6 +361,7 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); +void snd_hdac_bus_process_unsol_events(struct work_struct *work); static inline void snd_hdac_codec_link_up(struct hdac_device *codec) { diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 714a51721a313c..012305177f6822 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -9,8 +9,6 @@ #include #include "trace.h" -static void process_unsol_events(struct work_struct *work); - static const struct hdac_bus_ops default_ops = { .command = snd_hdac_bus_send_cmd, .get_response = snd_hdac_bus_get_response, @@ -37,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, bus->io_ops = io_ops; INIT_LIST_HEAD(&bus->stream_list); INIT_LIST_HEAD(&bus->codec_list); - INIT_WORK(&bus->unsol_work, process_unsol_events); + INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events); spin_lock_init(&bus->reg_lock); mutex_init(&bus->cmd_mutex); bus->irq = -1; @@ -148,7 +146,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); /* * process queued unsolicited events */ -static void process_unsol_events(struct work_struct *work) +void snd_hdac_bus_process_unsol_events(struct work_struct *work) { struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work); struct hdac_device *codec; @@ -171,6 +169,7 @@ static void process_unsol_events(struct work_struct *work) drv->unsol_event(codec, res); } } +EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events); /** * snd_hdac_bus_add_device - Add a codec to bus From 744a59ec89ca389f3d1386ba3bdeb2a7761b62e1 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 29 Aug 2018 18:23:42 +0800 Subject: [PATCH 166/298] ASoC: SOF: HDA: add hda codec probing APIs Signed-off-by: Keyon Jie --- sound/soc/sof/intel/Makefile | 3 + sound/soc/sof/intel/hda-codec.c | 133 ++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 8 ++ 3 files changed, 144 insertions(+) create mode 100644 sound/soc/sof/intel/hda-codec.c diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 7b5784d88fd154..8d6935c779d994 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -9,7 +9,10 @@ snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o hda-dai.o\ skl.o apl.o cnl.o +snd-sof-intel-hda-objs := hda-codec.o + obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-intel-byt.o obj-$(CONFIG_SND_SOC_SOF_HASWELL) += snd-sof-intel-hsw.o obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-intel-bdw.o obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o +obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c new file mode 100644 index 00000000000000..6971e0e2d3adc7 --- /dev/null +++ b/sound/soc/sof/intel/hda-codec.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Jeeja KP +// Rakesh Ughreja +// Keyon Jie +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/hdac_hda.h" + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +#define IDISP_VID_INTEL 0x80860000 + +/* load the legacy HDA codec driver */ +#ifdef MODULE +static void hda_codec_load_module(struct hda_codec *codec) +{ + char alias[MODULE_NAME_LEN]; + const char *module = alias; + + snd_hdac_codec_modalias(&codec->core, alias, sizeof(alias)); + dev_dbg(&codec->core.dev, "loading codec module: %s\n", module); + request_module(module); +} +#else +static void hda_codec_load_module(struct hda_codec *codec) {} +#endif + +/* probe individual codec */ +static int hda_codec_probe(struct snd_sof_dev *sdev, int address) +{ + struct hda_bus *hbus = sof_to_hbus(sdev); + struct hdac_device *hdev; + struct hdac_hda_priv *hda_priv; + u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | + (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + u32 resp = -1; + int ret; + + mutex_lock(&hbus->core.cmd_mutex); + snd_hdac_bus_send_cmd(&hbus->core, hda_cmd); + snd_hdac_bus_get_response(&hbus->core, address, &resp); + mutex_unlock(&hbus->core.cmd_mutex); + if (resp == -1) + return -EIO; + dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n", + address, resp); + + hda_priv = devm_kzalloc(&hbus->pci->dev, sizeof(*hda_priv), + GFP_KERNEL); + if (!hda_priv) + return -ENOMEM; + + hda_priv->codec.bus = hbus; + hdev = &hda_priv->codec.core; + + ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev); + if (ret < 0) + return ret; + + /* use legacy bus only for HDA codecs, idisp uses ext bus */ + if ((resp & 0xFFFF0000) != IDISP_VID_INTEL) { + hdev->type = HDA_DEV_LEGACY; + hda_codec_load_module(&hda_priv->codec); + } + + return 0; +} + +/* Codec initialization */ +int hda_codec_probe_bus(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + int i, ret = 0; + + /* probe codecs in avail slots */ + for (i = 0; i < HDA_MAX_CODECS; i++) { + + if (!(bus->codec_mask & (1 << i))) + continue; + + ret = hda_codec_probe(sdev, i); + if (ret < 0) { + dev_err(bus->dev, "codec #%d probe error, ret: %d\n", + i, ret); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL(hda_codec_probe_bus); + +int hda_codec_i915_init(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + int ret; + + /* i915 exposes a HDA codec for HDMI audio */ + ret = snd_hdac_i915_init(bus); + if (ret < 0) + return ret; + + ret = snd_hdac_display_power(bus, true); + if (ret < 0) + dev_err(bus->dev, "i915 HDAC power on failed %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(hda_codec_i915_init); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index acb6fe4dcebf10..4a430ba5a309c5 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -496,6 +496,14 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev); int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +/* + * HDA Codec operations. + */ +int hda_codec_probe_bus(struct snd_sof_dev *sdev); +int hda_codec_i915_init(struct snd_sof_dev *sdev); +#endif + /* * Trace Control. */ From be2a38a16e75839e265d5da7d6c9ac874fb3f629 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 31 Aug 2018 14:14:32 +0800 Subject: [PATCH 167/298] ASoC: SOF: HDA: add hda bus initialization API Signed-off-by: Keyon Jie --- sound/soc/sof/intel/Makefile | 3 +- sound/soc/sof/intel/hda-bus.c | 109 ++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 6 ++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/intel/hda-bus.c diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 8d6935c779d994..accb3d4d209d21 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -6,7 +6,8 @@ snd-sof-intel-byt-objs := byt.o snd-sof-intel-hsw-objs := hsw.o snd-sof-intel-bdw-objs := bdw.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ - hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o hda-dai.o\ + hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ + hda-dai.o hda-bus.o \ skl.o apl.o cnl.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c new file mode 100644 index 00000000000000..0518c0d09cc664 --- /dev/null +++ b/sound/soc/sof/intel/hda-bus.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Authors: Takashi Iwai + * Jeeja KP + * Keyon Jie + */ + +#include + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + +static const struct hdac_bus_ops bus_ops = { + .command = snd_hdac_bus_send_cmd, + .get_response = snd_hdac_bus_get_response, +}; + +#endif + +static void sof_hda_writel(u32 value, u32 __iomem *addr) +{ + writel(value, addr); +} + +static u32 sof_hda_readl(u32 __iomem *addr) +{ + return readl(addr); +} + +static void sof_hda_writew(u16 value, u16 __iomem *addr) +{ + writew(value, addr); +} + +static u16 sof_hda_readw(u16 __iomem *addr) +{ + return readw(addr); +} + +static void sof_hda_writeb(u8 value, u8 __iomem *addr) +{ + writeb(value, addr); +} + +static u8 sof_hda_readb(u8 __iomem *addr) +{ + return readb(addr); +} + +static int sof_hda_dma_alloc_pages(struct hdac_bus *bus, int type, + size_t size, struct snd_dma_buffer *buf) +{ + return snd_dma_alloc_pages(type, bus->dev, size, buf); +} + +static void sof_hda_dma_free_pages(struct hdac_bus *bus, + struct snd_dma_buffer *buf) +{ + snd_dma_free_pages(buf); +} + +static const struct hdac_io_ops io_ops = { + .reg_writel = sof_hda_writel, + .reg_readl = sof_hda_readl, + .reg_writew = sof_hda_writew, + .reg_readw = sof_hda_readw, + .reg_writeb = sof_hda_writeb, + .reg_readb = sof_hda_readb, + .dma_alloc_pages = sof_hda_dma_alloc_pages, + .dma_free_pages = sof_hda_dma_free_pages, +}; + +/* + * This can be used for both with/without hda link support. + * Returns 0 if successful, or a negative error code. + */ +int sof_hda_bus_init(struct hdac_bus *bus, struct device *dev, + const struct hdac_ext_bus_ops *ext_ops) +{ + static int idx; + + memset(bus, 0, sizeof(*bus)); + bus->dev = dev; + bus->io_ops = &io_ops; + + INIT_LIST_HEAD(&bus->codec_list); + INIT_LIST_HEAD(&bus->stream_list); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + bus->ops = &bus_ops; + INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events); +#endif + spin_lock_init(&bus->reg_lock); + mutex_init(&bus->cmd_mutex); + bus->irq = -1; + + bus->ext_ops = ext_ops; + INIT_LIST_HEAD(&bus->hlink_list); + bus->idx = idx++; + + mutex_init(&bus->lock); + bus->cmd_dma_state = true; + + return 0; +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 4a430ba5a309c5..1b5cd5d99227ba 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -496,6 +496,12 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev); int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev); +/* + * HDA bus operations. + */ +int sof_hda_bus_init(struct hdac_bus *bus, struct device *dev, + const struct hdac_ext_bus_ops *ext_ops); + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* * HDA Codec operations. From cc34b2281f95c708d24b84878aa8caed9744a06c Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Wed, 29 Aug 2018 19:03:30 +0800 Subject: [PATCH 168/298] ASoC: SOF: HDA: introduce hdac_bus for probing and stream management 1. add hda probing during initialization. 2. use hdac_bus to manage streams 3. replace sof_intel_hda_stream with hdac_ext_stream 4. refine makefile for with/without HDA support Signed-off-by: Mengdong Lin Signed-off-by: Keyon Jie --- sound/soc/sof/intel/Kconfig | 1 + sound/soc/sof/intel/hda-bus.c | 7 +- sound/soc/sof/intel/hda-ctrl.c | 52 +++- sound/soc/sof/intel/hda-loader.c | 59 +++-- sound/soc/sof/intel/hda-pcm.c | 24 +- sound/soc/sof/intel/hda-stream.c | 442 +++++++++++++++++-------------- sound/soc/sof/intel/hda-trace.c | 14 +- sound/soc/sof/intel/hda.c | 307 ++++++++++++++------- sound/soc/sof/intel/hda.h | 75 ++---- 9 files changed, 585 insertions(+), 396 deletions(-) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index a50bb59139c843..d777ddd80d893e 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -104,6 +104,7 @@ config SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA tristate "SOF support for HDA Links(HDA/HDMI)" depends on SND_SOC_SOF_HDA_COMMON + select SND_HDA_EXT_CORE select SND_SOC_HDAC_HDA select SND_SOC_HDAC_HDMI help diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c index 0518c0d09cc664..0942ffb9110fa4 100644 --- a/sound/soc/sof/intel/hda-bus.c +++ b/sound/soc/sof/intel/hda-bus.c @@ -5,8 +5,7 @@ * * Copyright(c) 2018 Intel Corporation. All rights reserved. * - * Authors: Takashi Iwai - * Jeeja KP + * Authors: Jeeja KP * Keyon Jie */ @@ -85,10 +84,10 @@ int sof_hda_bus_init(struct hdac_bus *bus, struct device *dev, memset(bus, 0, sizeof(*bus)); bus->dev = dev; - bus->io_ops = &io_ops; - INIT_LIST_HEAD(&bus->codec_list); + bus->io_ops = &io_ops; INIT_LIST_HEAD(&bus->stream_list); + INIT_LIST_HEAD(&bus->codec_list); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) bus->ops = &bus_ops; diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 825deb59527dfa..867bc3c4c7d3e9 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -86,8 +86,9 @@ int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev) int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) { + struct hdac_bus *bus = sof_to_bus(sdev); u32 cap, offset, feature; - int ret = -ENODEV, count = 0; + int count = 0; offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); @@ -103,21 +104,30 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) case SOF_HDA_PP_CAP_ID: dev_dbg(sdev->dev, "found DSP capability at 0x%x\n", offset); - sdev->bar[HDA_DSP_PP_BAR] = sdev->bar[HDA_DSP_HDA_BAR] + - offset; - ret = 0; + bus->ppcap = bus->remap_addr + offset; + sdev->bar[HDA_DSP_PP_BAR] = bus->ppcap; break; case SOF_HDA_SPIB_CAP_ID: dev_dbg(sdev->dev, "found SPIB capability at 0x%x\n", offset); - sdev->bar[HDA_DSP_SPIB_BAR] = - sdev->bar[HDA_DSP_HDA_BAR] + offset; + bus->spbcap = bus->remap_addr + offset; + sdev->bar[HDA_DSP_SPIB_BAR] = bus->spbcap; break; case SOF_HDA_DRSM_CAP_ID: dev_dbg(sdev->dev, "found DRSM capability at 0x%x\n", offset); - sdev->bar[HDA_DSP_DRSM_BAR] = - sdev->bar[HDA_DSP_HDA_BAR] + offset; + bus->drsmcap = bus->remap_addr + offset; + sdev->bar[HDA_DSP_DRSM_BAR] = bus->drsmcap; + break; + case SOF_HDA_GTS_CAP_ID: + dev_dbg(sdev->dev, "found GTS capability at 0x%x\n", + offset); + bus->gtscap = bus->remap_addr + offset; + break; + case SOF_HDA_ML_CAP_ID: + dev_dbg(sdev->dev, "found ML capability at 0x%x\n", + offset); + bus->mlcap = bus->remap_addr + offset; break; default: dev_vdbg(sdev->dev, "found capability %d at 0x%x\n", @@ -128,6 +138,32 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) offset = cap & SOF_HDA_CAP_NEXT_MASK; } while (count++ <= SOF_HDA_MAX_CAPS && offset); + return 0; +} + +void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable) +{ + u32 val = enable ? PCI_CGCTL_MISCBDCGE_MASK : 0; + + snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_MISCBDCGE_MASK, val); +} + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +/* + * While performing reset, controller may not come back properly and causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset + * (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ +int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + int ret; + + hda_dsp_ctrl_misc_clock_gating(sdev, false); + ret = snd_hdac_bus_init_chip(bus, full_reset); + hda_dsp_ctrl_misc_clock_gating(sdev, true); + return ret; } +#endif diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index f7c58755dcfc94..72df362b86360b 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -38,7 +38,8 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab, int direction) { - struct sof_intel_hda_stream *stream = NULL; + struct hdac_ext_stream *stream = NULL; + struct hdac_stream *hstream; struct pci_dev *pci = sdev->pci; int ret; @@ -53,6 +54,7 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, dev_err(sdev->dev, "error: no stream available\n"); return -ENODEV; } + hstream = &stream->hstream; /* allocate DMA buffer */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); @@ -61,8 +63,8 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, goto error; } - stream->config = format; - stream->bufsize = size; + hstream->format_val = format; + hstream->bufsize = size; ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); if (ret < 0) { @@ -72,10 +74,10 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, size); - return stream->tag; + return hstream->stream_tag; error: - hda_dsp_stream_put_pstream(sdev, stream->tag); + hda_dsp_stream_put_pstream(sdev, hstream->stream_tag); snd_dma_free_pages(dmab); return ret; } @@ -188,8 +190,11 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, } static int cl_trigger(struct snd_sof_dev *sdev, - struct sof_intel_hda_stream *stream, int cmd) + struct hdac_ext_stream *stream, int cmd) { + struct hdac_stream *hstream = &stream->hstream; + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + /* code loader is special case that reuses stream ops */ switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -197,16 +202,17 @@ static int cl_trigger(struct snd_sof_dev *sdev, HDA_DSP_CL_TRIGGER_TIMEOUT); snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - 1 << stream->index, 1 << stream->index); + 1 << hstream->index, + 1 << hstream->index); snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset, + sd_offset, SOF_HDA_SD_CTL_DMA_START | SOF_HDA_CL_DMA_SD_INT_MASK, SOF_HDA_SD_CTL_DMA_START | SOF_HDA_CL_DMA_SD_INT_MASK); - stream->running = true; + hstream->running = true; return 0; default: return hda_dsp_stream_trigger(sdev, stream, cmd); @@ -214,45 +220,50 @@ static int cl_trigger(struct snd_sof_dev *sdev, } static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct sof_intel_hda_stream *stream) + struct hdac_ext_stream *stream) { + struct hdac_stream *hstream = &stream->hstream; + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret; ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); /* TODO: spin lock ?*/ - stream->open = 0; - stream->running = 0; - stream->substream = NULL; + hstream->opened = 0; + hstream->running = 0; + hstream->substream = NULL; /* reset BDL address */ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0); + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0); + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0); - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); snd_dma_free_pages(dmab); dmab->area = NULL; - stream->bufsize = 0; - stream->config = 0; + hstream->bufsize = 0; + hstream->format_val = 0; return ret; } static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) { - struct sof_intel_hda_stream *stream = NULL; - struct sof_intel_hda_dev *hdev = sdev->hda; - int ret, status, i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_stream *stream = NULL; + struct hdac_stream *s; + int ret, status; /* get stream with tag */ - for (i = 0; i < hdev->num_playback; i++) { - if (hdev->pstream[i].tag == tag) { - stream = &hdev->pstream[i]; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK + && s->stream_tag == tag) { + stream = stream_to_hdac_ext_stream(s); break; } } + if (!stream) { dev_err(sdev->dev, "error: could not get stream with stream tag%d\n", diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index fb256a12a8fd5e..45338f78df9d9e 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -98,7 +98,8 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct sof_intel_hda_stream *stream = substream->runtime->private_data; + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream); struct snd_dma_buffer *dmab; int ret; u32 size, rate, bits; @@ -107,12 +108,12 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, rate = get_mult_div(sdev, params_rate(params)); bits = get_bits(sdev, params_width(params)); - stream->substream = substream; + hstream->substream = substream; dmab = substream->runtime->dma_buffer_p; - stream->config = rate | bits | (params_channels(params) - 1); - stream->bufsize = size; + hstream->format_val = rate | bits | (params_channels(params) - 1); + hstream->bufsize = size; ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params); if (ret < 0) { @@ -123,13 +124,14 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* disable SPIB, to enable buffer wrap for stream */ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); - return stream->tag; + return hstream->stream_tag; } int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd) { - struct sof_intel_hda_stream *stream = substream->runtime->private_data; + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream); return hda_dsp_stream_trigger(sdev, stream, cmd); } @@ -137,7 +139,7 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, int hda_dsp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { - struct sof_intel_hda_stream *stream; + struct hdac_ext_stream *stream; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) stream = hda_dsp_stream_get_pstream(sdev); @@ -150,20 +152,20 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, } /* binding pcm substream to hda stream */ - substream->runtime->private_data = stream; + substream->runtime->private_data = &stream->hstream; return 0; } int hda_dsp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { - struct sof_intel_hda_stream *stream = substream->runtime->private_data; + struct hdac_stream *hstream = substream->runtime->private_data; int ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ret = hda_dsp_stream_put_pstream(sdev, stream->tag); + ret = hda_dsp_stream_put_pstream(sdev, hstream->stream_tag); else - ret = hda_dsp_stream_put_cstream(sdev, stream->tag); + ret = hda_dsp_stream_put_cstream(sdev, hstream->stream_tag); if (ret) { dev_dbg(sdev->dev, "stream %s not opened!\n", substream->name); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index e3a3220c14261d..60e3844f72457b 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,7 @@ */ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct sof_intel_hda_stream *stream, + struct hdac_stream *stream, struct sof_intel_dsp_bdl *bdl, int size, struct snd_pcm_hw_params *params) { @@ -92,9 +93,10 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, } int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, - struct sof_intel_hda_stream *stream, + struct hdac_ext_stream *stream, int enable, u32 size) { + struct hdac_stream *hstream = &stream->hstream; u32 mask = 0; if (!sdev->bar[HDA_DSP_SPIB_BAR]) { @@ -102,12 +104,12 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, return -EINVAL; } - mask |= (1 << stream->index); + mask |= (1 << hstream->index); /* enable/disable SPIB for the stream */ snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, - enable << stream->index); + enable << hstream->index); /* set the SPIB value */ hda_dsp_write(sdev, stream->spib_addr, size); @@ -115,19 +117,47 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, return 0; } + +/* get next unused stream */ +struct hdac_ext_stream * +hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_stream *stream = NULL; + struct hdac_stream *s; + + /* get an unused playback stream */ + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == direction && !s->opened) { + s->opened = true; + stream = stream_to_hdac_ext_stream(s); + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free %s streams\n", + direction == SNDRV_PCM_STREAM_PLAYBACK ? + "playback" : "capture"); + + return stream; +} + /* get next unused playback stream */ -struct sof_intel_hda_stream * +struct hdac_ext_stream * hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; - struct sof_intel_hda_stream *stream = NULL; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_stream *stream = NULL; + struct hdac_stream *s; /* get an unused playback stream */ - for (i = 0; i < hdev->num_playback; i++) { - if (!hdev->pstream[i].open) { - hdev->pstream[i].open = true; - stream = &hdev->pstream[i]; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK + && !s->opened) { + s->opened = true; + stream = stream_to_hdac_ext_stream(s); break; } } @@ -140,18 +170,19 @@ hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) } /* get next unused capture stream */ -struct sof_intel_hda_stream * +struct hdac_ext_stream * hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; - struct sof_intel_hda_stream *stream = NULL; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_stream *stream = NULL; + struct hdac_stream *s; /* get an unused capture stream */ - for (i = 0; i < hdev->num_capture; i++) { - if (!hdev->cstream[i].open) { - hdev->cstream[i].open = true; - stream = &hdev->cstream[i]; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_CAPTURE + && !s->opened) { + s->opened = true; + stream = stream_to_hdac_ext_stream(s); break; } } @@ -163,17 +194,36 @@ hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) return stream; } +/* free a stream */ +int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int tag) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *s; + + /* find used stream */ + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == direction + && s->opened && s->stream_tag == tag) { + s->opened = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + /* free playback stream */ int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) { - struct sof_intel_hda_dev *hdev = sdev->hda; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *s; /* find used playback stream */ - for (i = 0; i < hdev->num_playback; i++) { - if (hdev->pstream[i].open && - hdev->pstream[i].tag == tag) { - hdev->pstream[i].open = false; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK + && s->opened && s->stream_tag == tag) { + s->opened = false; return 0; } } @@ -185,14 +235,14 @@ int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) /* free capture stream */ int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int tag) { - struct sof_intel_hda_dev *hdev = sdev->hda; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *s; /* find used capture stream */ - for (i = 0; i < hdev->num_capture; i++) { - if (hdev->cstream[i].open && - hdev->cstream[i].tag == tag) { - hdev->cstream[i].open = false; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_CAPTURE + && s->opened && s->stream_tag == tag) { + s->opened = false; return 0; } } @@ -202,41 +252,44 @@ int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int tag) } int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, - struct sof_intel_hda_stream *stream, int cmd) + struct hdac_ext_stream *stream, int cmd) { + struct hdac_stream *hstream = &stream->hstream; + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + /* cmd must be for audio stream */ switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_START: snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - 1 << stream->index, - 1 << stream->index); + 1 << hstream->index, + 1 << hstream->index); snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset, + sd_offset, SOF_HDA_SD_CTL_DMA_START | SOF_HDA_CL_DMA_SD_INT_MASK, SOF_HDA_SD_CTL_DMA_START | SOF_HDA_CL_DMA_SD_INT_MASK); - stream->running = true; + hstream->running = true; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset, + sd_offset, SOF_HDA_SD_CTL_DMA_START | SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK); - stream->running = false; + hstream->running = false; snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - 1 << stream->index, 0x0); + 1 << hstream->index, 0x0); break; default: dev_err(sdev->dev, "error: unknown command: %d\n", cmd); @@ -251,12 +304,14 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, * and normal stream. */ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, - struct sof_intel_hda_stream *stream, + struct hdac_ext_stream *stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { - struct sof_intel_hda_dev *hdev = sdev->hda; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *hstream = &stream->hstream; struct sof_intel_dsp_bdl *bdl; + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; u32 val, mask; @@ -266,31 +321,35 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, } /* decouple host and link DMA */ - mask = 0x1 << stream->index; + mask = 0x1 << hstream->index; snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, +#ifndef CONFIG_SND_SOC_SOF_FORCE_LEGACY_HDA mask, mask); - +#else + /* temporary using coupled mode */ + mask, 0); +#endif if (!dmab) { dev_err(sdev->dev, "error: no dma buffer allocated!\n"); return -ENODEV; } /* clear stream status */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_CL_DMA_SD_INT_MASK | SOF_HDA_SD_CTL_DMA_START, 0); snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, SOF_HDA_CL_DMA_SD_INT_MASK); /* stream reset */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, 0x1); udelay(3); do { val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset); + sd_offset); if (val & 0x1) break; } while (--timeout); @@ -300,14 +359,14 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, } timeout = HDA_DSP_STREAM_RESET_TIMEOUT; - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, 0x0); /* wait for hardware to report that stream is out of reset */ udelay(3); do { val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset); + sd_offset); if ((val & 0x1) == 0) break; } while (--timeout); @@ -316,31 +375,31 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, return -ETIMEDOUT; } - if (stream->posbuf) - *stream->posbuf = 0; + if (hstream->posbuf) + *hstream->posbuf = 0; /* reset BDL address */ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0x0); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0x0); /* clear stream status */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_CL_DMA_SD_INT_MASK | SOF_HDA_SD_CTL_DMA_START, 0); snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, SOF_HDA_CL_DMA_SD_INT_MASK); - stream->frags = 0; + hstream->frags = 0; - bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; - ret = hda_dsp_stream_setup_bdl(sdev, dmab, stream, bdl, - stream->bufsize, params); + bdl = (struct sof_intel_dsp_bdl *)hstream->bdl.area; + ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream, bdl, + hstream->bufsize, params); if (ret < 0) { dev_err(sdev->dev, "error: set up of BDL failed\n"); return ret; @@ -348,57 +407,57 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, /* set up stream descriptor for DMA */ /* program stream tag */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK, - stream->tag << + hstream->stream_tag << SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT); /* program cyclic buffer length */ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, - stream->bufsize); + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, + hstream->bufsize); /* program stream format */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + + sd_offset + SOF_HDA_ADSP_REG_CL_SD_FORMAT, - 0xffff, stream->config); + 0xffff, hstream->format_val); /* program last valid index */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, - 0xffff, (stream->frags - 1)); + sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, + 0xffff, (hstream->frags - 1)); /* program BDL address */ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, - (u32)stream->bdl.addr); + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + (u32)hstream->bdl.addr); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, - upper_32_bits(stream->bdl.addr)); + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + upper_32_bits(hstream->bdl.addr)); /* enable position buffer */ if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) & SOF_HDA_ADSP_DPLBASE_ENABLE)) snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, - (u32)hdev->posbuffer.addr | + (u32)bus->posbuf.addr | SOF_HDA_ADSP_DPLBASE_ENABLE); /* set interrupt enable bits */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_CL_DMA_SD_INT_MASK, SOF_HDA_CL_DMA_SD_INT_MASK); /* read FIFO size */ - if (stream->direction == SNDRV_PCM_STREAM_PLAYBACK) { - stream->fifo_size = + if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) { + hstream->fifo_size = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + + sd_offset + SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE); - stream->fifo_size &= 0xffff; - stream->fifo_size += 1; + hstream->fifo_size &= 0xffff; + hstream->fifo_size += 1; } else { - stream->fifo_size = 0; + hstream->fifo_size = 0; } return ret; @@ -406,77 +465,61 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) { - struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + struct hdac_bus *bus = (struct hdac_bus *)context; u32 status; - if (!pm_runtime_active(sdev->dev)) + if (!pm_runtime_active(bus->dev)) return IRQ_NONE; - status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + spin_lock(&bus->reg_lock); - if (status == 0 || status == 0xffffffff) + status = snd_hdac_chip_readl(bus, INTSTS); + if (status == 0 || status == 0xffffffff) { + spin_unlock(&bus->reg_lock); return IRQ_NONE; + } - return status ? IRQ_WAKE_THREAD : IRQ_HANDLED; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + /* clear rirb int */ + status = snd_hdac_chip_readb(bus, RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) + snd_hdac_bus_update_rirb(bus); + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); + } +#endif + + spin_unlock(&bus->reg_lock); + + return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; } irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) { - struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; - struct sof_intel_hda_dev *hdev = sdev->hda; - u32 status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + struct hdac_bus *bus = (struct hdac_bus *)context; + struct hdac_stream *s; + u32 status = snd_hdac_chip_readl(bus, INTSTS); u32 sd_status; - int i; - - /* check playback streams */ - for (i = 0; i < hdev->num_playback; i++) { - /* is IRQ for this stream ? */ - if (status & (1 << hdev->pstream[i].index)) { - sd_status = - snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - hdev->pstream[i].sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS) & - 0xff; - - dev_dbg(sdev->dev, "pstream %d status 0x%x\n", - i, sd_status); - - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - hdev->pstream[i].sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); - - if (!hdev->pstream[i].substream || - !hdev->pstream[i].running || - (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) - continue; - } - } - /* check capture streams */ - for (i = 0; i < hdev->num_capture; i++) { - /* is IRQ for this stream ? */ - if (status & (1 << hdev->cstream[i].index)) { - sd_status = - snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - hdev->cstream[i].sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS) & - 0xff; - - dev_dbg(sdev->dev, "cstream %d status 0x%x\n", - i, sd_status); - - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - hdev->cstream[i].sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); - - if (!hdev->cstream[i].substream || - !hdev->cstream[i].running || - (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) + /* check streams */ + list_for_each_entry(s, &bus->stream_list, list) { + if (status & (1 << s->index) + && s->opened) { + sd_status = snd_hdac_stream_readb(s, SD_STS); + + dev_dbg(bus->dev, "stream %d status 0x%x\n", + s->index, sd_status); + + snd_hdac_stream_writeb(s, SD_STS, SD_INT_MASK); + + if (!s->substream || + !s->running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) continue; +#ifdef USE_POS_BUF + snd_pcm_period_elapsed(s->substream); +#endif + } } @@ -485,9 +528,11 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) int hda_dsp_stream_init(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; - struct sof_intel_hda_stream *stream; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_stream *stream; + struct hdac_stream *hstream; struct pci_dev *pci = sdev->pci; + int sd_offset; int i, num_playback, num_capture, num_total, ret; u32 gcap; @@ -499,9 +544,6 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) num_playback = (gcap >> 12) & 0x0f; num_total = num_playback + num_capture; - hdev->num_capture = num_capture; - hdev->num_playback = num_playback; - dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", num_playback, num_capture); @@ -518,8 +560,10 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) } /* mem alloc for the position buffer */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 8, - &hdev->posbuffer); + /* TODO: check position buffer update */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + SOF_HDA_DPIB_ENTRY_SIZE * num_total, + &bus->posbuf); if (ret < 0) { dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); return -ENOMEM; @@ -527,7 +571,7 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) /* mem alloc for the CORB/RIRB ringbuffers */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, - PAGE_SIZE, &sdev->hda->hbus.core.rb); + PAGE_SIZE, &bus->rb); if (ret < 0) { dev_err(sdev->dev, "error: RB alloc failed\n"); return -ENOMEM; @@ -535,7 +579,10 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) /* create capture streams */ for (i = 0; i < num_capture; i++) { - stream = &hdev->cstream[i]; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; @@ -555,35 +602,45 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) SOF_HDA_SPIB_MAXFIFO; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DRSM) + /* FIXME: Remove? HDAC doesn't use DRSM so has no drsm_addr */ /* do we support DRSM */ if (sdev->bar[HDA_DSP_DRSM_BAR]) stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; - - stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; - stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + - stream->sd_offset; - - stream->tag = i + 1; - stream->open = false; - stream->running = false; - stream->direction = SNDRV_PCM_STREAM_CAPTURE; - stream->index = i; +#endif + + hstream = &stream->hstream; + hstream->bus = bus; + hstream->sd_int_sta_mask = 1 << i; + hstream->index = i; + sd_offset = SOF_STREAM_SD_OFFSET(hstream); + hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; + hstream->stream_tag = i + 1; + hstream->opened = false; + hstream->running = false; + hstream->direction = SNDRV_PCM_STREAM_CAPTURE; /* memory alloc for stream BDL */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, - HDA_DSP_BDL_SIZE, &stream->bdl); + HDA_DSP_BDL_SIZE, &hstream->bdl); if (ret < 0) { dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + kfree(stream); return -ENOMEM; } - stream->posbuf = (__le32 *)(hdev->posbuffer.area + - (stream->index) * 8); + hstream->posbuf = (__le32 *)(bus->posbuf.area + + (hstream->index) * 8); + + list_add_tail(&hstream->list, &bus->stream_list); } /* create playback streams */ for (i = num_capture; i < num_total; i++) { - stream = &hdev->pstream[i - num_capture]; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; /* we always have DSP support */ stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + @@ -604,30 +661,38 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) SOF_HDA_SPIB_MAXFIFO; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DRSM) + /* FIXME: Remove? HDAC doesn't use DRSM so has no drsm_addr */ /* do we support DRSM */ if (sdev->bar[HDA_DSP_DRSM_BAR]) stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; - - stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; - stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + - stream->sd_offset; - stream->tag = i - num_capture + 1; - stream->open = false; - stream->running = false; - stream->direction = SNDRV_PCM_STREAM_PLAYBACK; - stream->index = i; +#endif + + hstream = &stream->hstream; + hstream->bus = bus; + hstream->sd_int_sta_mask = 1 << i; + hstream->index = i; + sd_offset = SOF_STREAM_SD_OFFSET(hstream); + hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; + hstream->stream_tag = i - num_capture + 1; + hstream->opened = false; + hstream->running = false; + hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; /* mem alloc for stream BDL */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, - HDA_DSP_BDL_SIZE, &stream->bdl); + HDA_DSP_BDL_SIZE, &hstream->bdl); if (ret < 0) { dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + kfree(stream); return -ENOMEM; } - stream->posbuf = (__le32 *)(hdev->posbuffer.area + - (stream->index) * 8); + hstream->posbuf = (__le32 *)(bus->posbuf.area + + (hstream->index) * 8); + + list_add_tail(&hstream->list, &bus->stream_list); } return 0; @@ -635,30 +700,23 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) void hda_dsp_stream_free(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; - struct sof_intel_hda_stream *stream; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *s, *_s; + struct hdac_ext_stream *stream; /* free position buffer */ - if (hdev->posbuffer.area) - snd_dma_free_pages(&hdev->posbuffer); - - /* free capture streams */ - for (i = 0; i < hdev->num_capture; i++) { - stream = &hdev->cstream[i]; - - /* free bdl buffer */ - if (stream->bdl.area) - snd_dma_free_pages(&stream->bdl); - } + if (bus->posbuf.area) + snd_dma_free_pages(&bus->posbuf); - /* free playback streams */ - for (i = 0; i < hdev->num_playback; i++) { - stream = &hdev->pstream[i]; + list_for_each_entry_safe(s, _s, &bus->stream_list, list) { + /* TODO: decouple */ /* free bdl buffer */ - if (stream->bdl.area) - snd_dma_free_pages(&stream->bdl); + if (s->bdl.area) + snd_dma_free_pages(&s->bdl); + list_del(&s->list); + stream = stream_to_hdac_ext_stream(s); + kfree(stream); } } diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 273ac6d1a78ec3..a1c9f1b45dca14 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -36,11 +36,12 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) { - struct sof_intel_hda_stream *stream = sdev->hda->dtrace_stream; + struct hdac_ext_stream *stream = sdev->hda->dtrace_stream; + struct hdac_stream *hstream = &stream->hstream; struct snd_dma_buffer *dmab = &sdev->dmatb; int ret; - stream->bufsize = sdev->dmatb.bytes; + hstream->bufsize = sdev->dmatb.bytes; ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); if (ret < 0) @@ -59,7 +60,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *tag) return -ENODEV; } - *tag = sdev->hda->dtrace_stream->tag; + *tag = sdev->hda->dtrace_stream->hstream.stream_tag; /* * initialize capture stream, set BDL address and return corresponding @@ -70,10 +71,13 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *tag) int hda_dsp_trace_release(struct snd_sof_dev *sdev) { + struct hdac_stream *hstream; + if (sdev->hda->dtrace_stream) { - sdev->hda->dtrace_stream->open = false; + hstream = &sdev->hda->dtrace_stream->hstream; + hstream->opened = false; hda_dsp_stream_put_cstream(sdev, - sdev->hda->dtrace_stream->tag); + hstream->stream_tag); sdev->hda->dtrace_stream = NULL; return 0; } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index f9f1720c4c3150..21803e11228c9c 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -29,10 +29,18 @@ #include #include #include +#include +#include #include "../sof-priv.h" #include "../ops.h" #include "hda.h" +#include "../../codecs/hdac_hda.h" + +/* platform specific devices */ +#include "shim.h" + + /* * Register IO @@ -321,6 +329,133 @@ static const struct sof_intel_dsp_desc *get_chip_info(int pci_id) return NULL; } +static int hda_init(struct snd_sof_dev *sdev) +{ + struct hda_bus *hbus; + struct hdac_bus *bus; + struct hdac_ext_bus_ops *ext_ops = NULL; + struct pci_dev *pci = sdev->pci; + int ret; + + hbus = sof_to_hbus(sdev); + bus = sof_to_bus(sdev); + + /* HDA bus init */ +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) + ext_ops = snd_soc_hdac_hda_get_ops(); +#endif + sof_hda_bus_init(bus, &pci->dev, ext_ops); + bus->use_posbuf = 1; + bus->bdl_pos_adj = 0; + + mutex_init(&hbus->prepare_mutex); + hbus->pci = pci; + hbus->mixer_assigned = -1; + hbus->modelname = "sofbus"; + + /* initialise hdac bus */ + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (bus->remap_addr == NULL) { + dev_err(bus->dev, "ioremap error\n"); + return -ENXIO; + } + + /* HDA base */ + sdev->bar[HDA_DSP_HDA_BAR] = bus->remap_addr; + + /* get controller capabilities */ + ret = hda_dsp_ctrl_get_caps(sdev); + if (ret < 0) + dev_err(&pci->dev, "error: get caps error\n"); + + return ret; +} + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + +static int hda_init_caps(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct pci_dev *pci = sdev->pci; + struct hdac_ext_link *hlink = NULL; + int ret = 0; + + // FIXME: we do this a lot ! + hda_dsp_ctrl_init_chip(sdev, true); + + device_disable_async_suspend(bus->dev); + + /* check if dsp is there */ + if (bus->ppcap) + dev_dbg(&pci->dev, "PP capbility, will probe DSP later.\n"); + + if (bus->mlcap) + snd_hdac_ext_bus_get_ml_capabilities(bus); + + snd_hdac_bus_stop_chip(bus); + + /* probe i915 and HDA codecs */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = hda_codec_i915_init(sdev); + if (ret < 0) + return ret; + } + + // FIXME: we do this a lot ! + ret = hda_dsp_ctrl_init_chip(sdev, true); + if (ret < 0) { + dev_err(bus->dev, "Init chip failed with ret: %d\n", ret); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, false); + return ret; + } + + /* codec detection */ + if (!bus->codec_mask) + dev_info(bus->dev, "no hda codecs found!\n"); + + /* create codec instances */ + hda_codec_probe_bus(sdev); + + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, false); + if (ret < 0) { + dev_err(bus->dev, "Cannot turn off display power on i915\n"); + return ret; + } + } + + /* + * we are done probing so decrement link counts + */ + list_for_each_entry(hlink, &bus->hlink_list, list) + snd_hdac_ext_bus_link_put(bus, hlink); + + return 0; +} + +#else + +static int hda_init_caps(struct snd_sof_dev *sdev) +{ + /* + * set CGCTL.MISCBDCGE to 0 during reset and set back to 1 + * when reset finished. + * TODO: maybe no need for init_caps? + */ + hda_dsp_ctrl_misc_clock_gating(sdev, 0); + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + return 0; +} + +#endif + /* * We don't need to do a full HDA codec probe as external HDA codec mode is * considered legacy and will not be supported under SOF. HDMI/DP HDA will @@ -330,10 +465,10 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = sdev->pci; struct sof_intel_hda_dev *hdev; - struct sof_intel_hda_stream *stream; + struct hdac_bus *bus; + struct hdac_stream *stream; const struct sof_intel_dsp_desc *chip; - int i; - int ret = 0; + int sd_offset, ret = 0; /* set DSP arch ops */ sdev->arch_ops = &sof_xtensa_arch_ops; @@ -352,33 +487,21 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) sdev->hda = hdev; hdev->desc = chip; - /* HDA base */ - sdev->bar[HDA_DSP_HDA_BAR] = pci_ioremap_bar(pci, HDA_DSP_HDA_BAR); - if (!sdev->bar[HDA_DSP_HDA_BAR]) { - dev_err(&pci->dev, "error: ioremap error\n"); - /* - * FIXME: why do we return directly, - * should we have a goto err here? - * or should all these gotos be replaced - * by a return? - */ - return -ENXIO; - } + /* set up HDA base */ + ret = hda_init(sdev); + if (ret < 0) + return ret; /* DSP base */ sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR); if (!sdev->bar[HDA_DSP_BAR]) { dev_err(&pci->dev, "error: ioremap error\n"); - ret = -ENXIO; - goto err; + return -ENXIO; } sdev->mmio_bar = HDA_DSP_BAR; sdev->mailbox_bar = HDA_DSP_BAR; - pci_set_master(pci); - synchronize_irq(pci->irq); - /* allow 64bit DMA address if supported by H/W */ if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(64))) { dev_dbg(&pci->dev, "DMA mask is 64 bit\n"); @@ -389,13 +512,6 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); } - /* get controller capabilities */ - ret = hda_dsp_ctrl_get_caps(sdev); - if (ret < 0) { - dev_err(&pci->dev, "error: failed to find DSP capability\n"); - goto err; - } - /* init streams */ ret = hda_dsp_stream_init(sdev); if (ret < 0) { @@ -404,72 +520,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) * not all errors are due to memory issues, but trying * to free everything does not harm */ - goto stream_err; - } - - /* - * clear bits 0-2 of PCI register TCSEL (at offset 0x44) - * TCSEL == Traffic Class Select Register, which sets PCI express QOS - * Ensuring these bits are 0 clears playback static on some HD Audio - * codecs. PCI register TCSEL is defined in the Intel manuals. - */ - snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); - - /* - * while performing reset, controller may not come back properly causing - * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do - * reset (init chip) and then again set CGCTL.MISCBDCGE to 1 - */ - snd_sof_pci_update_bits(sdev, PCI_CGCTL, - PCI_CGCTL_MISCBDCGE_MASK, 0); - - /* clear WAKESTS */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, - SOF_HDA_WAKESTS_INT_MASK, - SOF_HDA_WAKESTS_INT_MASK); - - /* reset HDA controller */ - ret = hda_dsp_ctrl_link_reset(sdev); - if (ret < 0) { - dev_err(&pci->dev, "error: failed to reset HDA controller\n"); - goto stream_err; - } - - /* clear stream status */ - for (i = 0 ; i < hdev->num_capture ; i++) { - stream = &hdev->cstream[i]; - if (stream) - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); - } - - for (i = 0 ; i < hdev->num_playback ; i++) { - stream = &hdev->pstream[i]; - if (stream) - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); + goto err; } - /* clear WAKESTS */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, - SOF_HDA_WAKESTS_INT_MASK, - SOF_HDA_WAKESTS_INT_MASK); - - /* clear interrupt status register */ - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, - SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); - - /* enable CIE and GIE interrupts */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, - SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); - /* * register our IRQ * let's try to enable msi firstly @@ -477,7 +530,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) * TODO: support interrupt mode selection with kernel parameter * support msi multiple vectors */ - ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI); +// ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI); + /* todo: MSI mode doesn't work for HDMI yet, debug it later */ + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_LEGACY); if (ret < 0) { dev_info(sdev->dev, "use legacy interrupt mode\n"); sdev->hda->irq = pci->irq; @@ -489,14 +544,15 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) sdev->ipc_irq = sdev->hda->irq; } + bus = sof_to_bus(sdev); dev_dbg(sdev->dev, "using HDA IRQ %d\n", sdev->hda->irq); ret = request_threaded_irq(sdev->hda->irq, hda_dsp_stream_interrupt, hda_dsp_stream_threaded_handler, - IRQF_SHARED, "AudioHDA", sdev); + IRQF_SHARED, "AudioHDA", bus); if (ret < 0) { dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", sdev->hda->irq); - goto stream_err; + goto free_streams; } dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); @@ -506,13 +562,57 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", sdev->ipc_irq); - goto irq_err; + goto free_hda_irq; + } + + pci_set_master(pci); + synchronize_irq(pci->irq); + + /* + * clear TCSEL to clear playback on some HD Audio + * codecs. PCI TCSEL is defined in the Intel manuals. + */ + snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); + + + /* init HDA capabilities */ + ret = hda_init_caps(sdev); + if (ret < 0) + goto free_ipc_irq; + + /* reset HDA controller */ + ret = hda_dsp_ctrl_link_reset(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to reset HDA controller\n"); + goto free_ipc_irq; + } + + /* clear stream status */ + list_for_each_entry(stream, &bus->stream_list, list) { + sd_offset = SOF_STREAM_SD_OFFSET(stream); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); } + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + /* clear interrupt status register */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); + + /* enable CIE and GIE interrupts */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); + /* re-enable CGCTL.MISCBDCGE after reset */ - snd_sof_pci_update_bits(sdev, PCI_CGCTL, - PCI_CGCTL_MISCBDCGE_MASK, - PCI_CGCTL_MISCBDCGE_MASK); + hda_dsp_ctrl_misc_clock_gating(sdev, true); device_disable_async_suspend(&pci->dev); @@ -532,10 +632,12 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) return 0; -irq_err: - free_irq(sdev->hda->irq, sdev); -stream_err: +free_ipc_irq: + free_irq(sdev->ipc_irq, sdev); +free_hda_irq: + free_irq(sdev->hda->irq, bus); pci_free_irq_vectors(pci); +free_streams: hda_dsp_stream_free(sdev); err: /* disable DSP */ @@ -546,6 +648,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) int hda_dsp_remove(struct snd_sof_dev *sdev) { + struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = sdev->pci; const struct sof_intel_dsp_desc *chip = sdev->hda->desc; @@ -566,7 +669,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) SOF_HDA_PPCTL_GPROCEN, 0); free_irq(sdev->ipc_irq, sdev); - free_irq(sdev->pci->irq, sdev); + free_irq(sdev->pci->irq, bus); pci_free_irq_vectors(pci); hda_dsp_stream_free(sdev); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1b5cd5d99227ba..e8919b635a9c14 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -30,6 +30,7 @@ #define SOF_HDA_INTSTS 0x24 #define SOF_HDA_WAKESTS 0x0E #define SOF_HDA_WAKESTS_INT_MASK ((1 << 8) - 1) +#define SOF_HDA_RIRBSTS 0x5d /* SOF_HDA_GCTL register bist */ #define SOF_HDA_GCTL_RESET BIT(0) @@ -44,12 +45,18 @@ #define SOF_HDA_CAP_ID_MASK (0xFFF << SOF_HDA_CAP_ID_OFF) #define SOF_HDA_CAP_NEXT_MASK 0xFFFF +#define SOF_HDA_GTS_CAP_ID 0x1 +#define SOF_HDA_ML_CAP_ID 0x2 + #define SOF_HDA_PP_CAP_ID 0x3 #define SOF_HDA_REG_PP_PPCH 0x10 #define SOF_HDA_REG_PP_PPCTL 0x04 #define SOF_HDA_PPCTL_PIE BIT(31) #define SOF_HDA_PPCTL_GPROCEN BIT(30) +/* DPIB entry size: 8 Bytes = 2 DWords */ +#define SOF_HDA_DPIB_ENTRY_SIZE 0x8 + #define SOF_HDA_SPIB_CAP_ID 0x4 #define SOF_HDA_DRSM_CAP_ID 0x5 @@ -101,6 +108,7 @@ #define SOF_HDA_ADSP_REG_CL_SD_FIFOL 0x14 #define SOF_HDA_ADSP_REG_CL_SD_BDLPL 0x18 #define SOF_HDA_ADSP_REG_CL_SD_BDLPU 0x1C +#define SOF_HDA_ADSP_SD_ENTRY_SIZE 0x20 /* CL: Software Position Based FIFO Capability Registers */ #define SOF_DSP_REG_CL_SPBFIFO \ @@ -330,40 +338,6 @@ struct sof_intel_dsp_desc { struct snd_sof_dsp_ops *ops; }; -/* per stream data for HDA DSP Frontend */ -struct sof_intel_hda_stream { - - /* addresses for stream HDA functions */ - void __iomem *pphc_addr; - void __iomem *pplc_addr; - void __iomem *spib_addr; - void __iomem *fifo_addr; - void __iomem *drsm_addr; - - /* runtime state */ - u32 dpib; - u32 lpib; - int tag; - int direction; - bool open; - bool running; - u32 index; - - /* buffer & descriptors */ - struct snd_dma_buffer bdl; - void __iomem *sd_addr; /* stream descriptor pointer */ - int sd_offset; /* Stream descriptor offset */ - unsigned int bufsize; /* size of the play buffer in bytes */ - unsigned int fifo_size; /* FIFO size */ - - __le32 *posbuf; /* position buffer pointer */ - unsigned int frags; /* number for period in the play buffer */ - unsigned int config; /* format config value */ - - /* PCM */ - struct snd_pcm_substream *substream; -}; - #define SOF_HDA_PLAYBACK_STREAMS 16 #define SOF_HDA_CAPTURE_STREAMS 16 #define SOF_HDA_PLAYBACK 0 @@ -377,21 +351,16 @@ struct sof_intel_hda_dev { /* hw config */ const struct sof_intel_dsp_desc *desc; - /* streams */ - struct sof_intel_hda_stream pstream[SOF_HDA_PLAYBACK_STREAMS]; - struct sof_intel_hda_stream cstream[SOF_HDA_CAPTURE_STREAMS]; - int num_capture; - int num_playback; - - /* position buffers */ - struct snd_dma_buffer posbuffer; - /*trace */ - struct sof_intel_hda_stream *dtrace_stream; + struct hdac_ext_stream *dtrace_stream; int irq; }; +#define SOF_STREAM_SD_OFFSET(s) \ + (SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \ + + SOF_HDA_ADSP_LOADER_BASE) + /* * DSP Core services. */ @@ -449,26 +418,30 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, int hda_dsp_stream_init(struct snd_sof_dev *sdev); void hda_dsp_stream_free(struct snd_sof_dev *sdev); int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, - struct sof_intel_hda_stream *stream, + struct hdac_ext_stream *stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params); int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, - struct sof_intel_hda_stream *stream, int cmd); + struct hdac_ext_stream *stream, int cmd); irqreturn_t hda_dsp_stream_interrupt(int irq, void *context); irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct sof_intel_hda_stream *stream, + struct hdac_stream *stream, struct sof_intel_dsp_bdl *bdl, int size, struct snd_pcm_hw_params *params); -struct sof_intel_hda_stream * + +struct hdac_ext_stream * + hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction); +struct hdac_ext_stream * hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev); -struct sof_intel_hda_stream * +struct hdac_ext_stream * hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev); +int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int stream_tag); int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int stream_tag); int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, - struct sof_intel_hda_stream *stream, + struct hdac_ext_stream *stream, int enable, u32 size); /* @@ -495,6 +468,8 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); */ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev); int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev); +void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable); +int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset); /* * HDA bus operations. From f89c39dda110f73a22e8ba39d80c2e47377765dd Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 10 Aug 2018 19:30:30 +0800 Subject: [PATCH 169/298] ASoC: SOF: topology: continue parsing when num_hw_configs is not 1 Only give warning and continue parsing when num_hw_configs is not 1. this is needed for HDA dai parsing. Signed-off-by: Mengdong Lin Signed-off-by: Keyon Jie --- sound/soc/sof/topology.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 32bef181dde9f7..e4b7cc0a96f937 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1786,12 +1786,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, if (!link->no_pcm) return 0; - /* only support 1 config atm */ - if (le32_to_cpu(cfg->num_hw_configs) != 1) { - dev_err(sdev->dev, "error: unexpected DAI config count %d\n", + /* usually we use 1 config, but for HDA it may be 0 ATM */ + if (le32_to_cpu(cfg->num_hw_configs) != 1) + dev_warn(sdev->dev, "warn: unexpected DAI config count %d!\n", le32_to_cpu(cfg->num_hw_configs)); - return -EINVAL; - } /* check we have some tokens - we need at least DAI type */ if (le32_to_cpu(private->size) == 0) { From 2d3478a6087dd99d926930cf7f2c91f2d23cd8e5 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 17 Aug 2018 15:23:54 +0800 Subject: [PATCH 170/298] ASoC: SOF: PCM: do nothing for HDA dai_link fixup() Signed-off-by: Keyon Jie --- sound/soc/sof/pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index c34e4af1abaaa9..d746cdf4d14eaa 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -665,7 +665,8 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, /* TODO: add any other DMIC specific fixups */ break; case SOF_DAI_INTEL_HDA: - /* fallthrough */ + /* do nothing for HDA dai_link */ + break; default: dev_err(sdev->dev, "error: invalid DAI type %d\n", dai->dai_config.type); From 7889a7ade4e20a81e297e3f5a5aea3b5d7fa55ef Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 27 Aug 2018 22:30:06 +0800 Subject: [PATCH 171/298] ASoC: SOF: hda-dai: add ops for hda link dais. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-dai.c | 185 ++++++++++++++++++++++++++++++++-- sound/soc/sof/intel/hda.h | 4 + 2 files changed, 180 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 4cb67bc836657a..dd68cbf00014dd 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -9,12 +9,171 @@ // #include +#include +#include #include "../sof-priv.h" #include "hda.h" #define SKL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + +struct hda_pipe_params { + u8 host_dma_id; + u8 link_dma_id; + u32 ch; + u32 s_freq; + u32 s_fmt; + u8 linktype; + snd_pcm_format_t format; + int link_index; + int stream; + unsigned int host_bps; + unsigned int link_bps; +}; + +/* TODO: add hda dai params in tplg, and configure this in topology parsing */ +static int hda_link_dma_params(struct hdac_ext_stream *stream, + struct hda_pipe_params *params) +{ + struct hdac_stream *hstream = &stream->hstream; + struct hdac_bus *bus = hstream->bus; + unsigned int format_val; + struct hdac_ext_link *link; + + snd_hdac_ext_stream_decouple(bus, stream, true); + snd_hdac_ext_link_stream_reset(stream); + + format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, + params->format, + params->link_bps, 0); + + dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", + format_val, params->s_freq, params->ch, params->format); + + snd_hdac_ext_link_stream_setup(stream, format_val); + + list_for_each_entry(link, &bus->hlink_list, list) { + if (link->index == params->link_index) + snd_hdac_ext_link_set_stream_id(link, + hstream->stream_tag); + } + + stream->link_prepared = 1; + + return 0; +} + +static int hda_link_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_bus *bus = hstream->bus; + struct hdac_ext_stream *link_dev; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct hda_pipe_params p_params = {0}; + struct hdac_ext_link *link; + int stream_tag; + + link_dev = snd_hdac_ext_stream_assign(bus, substream, + HDAC_EXT_STREAM_TYPE_LINK); + if (!link_dev) + return -EBUSY; + + snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); + + link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); + if (!link) + return -EINVAL; + + stream_tag = hdac_stream(link_dev)->stream_tag; + + /* set the stream tag in the codec dai dma params */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); + else + snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + p_params.link_dma_id = stream_tag - 1; + p_params.link_index = link->index; + p_params.format = params_format(params); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + p_params.link_bps = codec_dai->driver->playback.sig_bits; + else + p_params.link_bps = codec_dai->driver->capture.sig_bits; + + return hda_link_dma_params(link_dev, &p_params); +} + +static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *link_dev = + snd_soc_dai_get_dma_data(dai, substream); + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_bus *bus = hstream->bus; + struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream); + + dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_hdac_ext_link_stream_start(link_dev); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + snd_hdac_ext_link_stream_clear(link_dev); + if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) + snd_hdac_ext_stream_decouple(bus, stream, false); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int hda_link_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_bus *bus = hstream->bus; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct hdac_ext_stream *link_dev = + snd_soc_dai_get_dma_data(dai, substream); + struct hdac_ext_link *link; + + link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); + if (!link) + return -EINVAL; + + snd_hdac_ext_link_clear_stream_id(link, + hdac_stream(link_dev)->stream_tag); + snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); + + link_dev->link_prepared = 0; + + return 0; +} + +static const struct snd_soc_dai_ops hda_link_dai_ops = { + .hw_params = hda_link_hw_params, + .hw_free = hda_link_hw_free, + .trigger = hda_link_pcm_trigger, +}; +#endif + /* * common dai driver for skl+ platforms. * some products who use this DAI array only physically have a subset of @@ -73,40 +232,48 @@ struct snd_soc_dai_driver skl_dai[] = { .capture = SOF_DAI_STREAM("DMIC16k Rx", 1, 4, SNDRV_PCM_RATE_16000, SKL_FORMATS), }, +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) { .name = "iDisp1 Pin", + .ops = &hda_link_dai_ops, .playback = SOF_DAI_STREAM("iDisp1 Tx", 1, 8, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), }, { .name = "iDisp2 Pin", + .ops = &hda_link_dai_ops, .playback = SOF_DAI_STREAM("iDisp2 Tx", 1, 8, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), }, { .name = "iDisp3 Pin", + .ops = &hda_link_dai_ops, .playback = SOF_DAI_STREAM("iDisp3 Tx", 1, 8, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), }, { - .name = "Analog Codec DAI", - .playback = SOF_DAI_STREAM("Analog Codec Playback", 1, 16, + .name = "Analog CPU DAI", + .ops = &hda_link_dai_ops, + .playback = SOF_DAI_STREAM("Analog CPU Playback", 1, 16, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), - .capture = SOF_DAI_STREAM("Analog Codec Capture", 1, 16, + .capture = SOF_DAI_STREAM("Analog CPU Capture", 1, 16, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), }, { - .name = "Digital Codec DAI", - .playback = SOF_DAI_STREAM("Digital Codec Playback", 1, 16, + .name = "Digital CPU DAI", + .ops = &hda_link_dai_ops, + .playback = SOF_DAI_STREAM("Digital CPU Playback", 1, 16, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), - .capture = SOF_DAI_STREAM("Digital Codec Capture", 1, 16, + .capture = SOF_DAI_STREAM("Digital CPU Capture", 1, 16, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), }, { - .name = "Alt Analog Codec DAI", - .playback = SOF_DAI_STREAM("Alt Analog Codec Playback", 1, 16, + .name = "Alt Analog CPU DAI", + .ops = &hda_link_dai_ops, + .playback = SOF_DAI_STREAM("Alt Analog CPU Playback", 1, 16, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), - .capture = SOF_DAI_STREAM("Alt Analog Codec Capture", 1, 16, + .capture = SOF_DAI_STREAM("Alt Analog CPU Capture", 1, 16, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), }, +#endif }; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index e8919b635a9c14..d44b2550e87fcd 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -316,7 +316,11 @@ (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) /* Number of DAIs */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) #define SOF_SKL_NUM_DAIS 14 +#else +#define SOF_SKL_NUM_DAIS 8 +#endif struct sof_intel_dsp_bdl { u32 addr_l; From 41b8f5d8b870fb6f8bc6694248998a849156b354 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 5 Sep 2018 09:43:26 +0200 Subject: [PATCH 172/298] sof: fix compilation on non X86 platforms SND_DMA_SGBUF is defined to depend on X86 snd_sgbuf_aligned_pages() is only defined if SND_DMA_SGBUF iss set. Use PAGE_ALIGN() instead to avoid the dependency. Signed-off-by: Guennadi Liakhovetski --- sound/soc/sof/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 437ecdde749fa8..a822be0f8d3945 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -197,7 +197,7 @@ int snd_sof_create_page_table(struct snd_sof_dev *sdev, { int i, pages; - pages = snd_sgbuf_aligned_pages(size); + pages = PAGE_ALIGN(size); dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", dmab->area, size, pages); From e04cc9c91499f0dc906d67b92a973f97055d9218 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 5 Sep 2018 10:47:55 +0200 Subject: [PATCH 173/298] sof: add support for SPI based configurations to the core Currently only PCI and ACPI SOF devices are supported by the SOF core. This patch adds a machine description struct for non ACPI- based configurations and adds code to select the correct machine data block, depending on the device type. Signed-off-by: Guennadi Liakhovetski --- include/sound/sof.h | 19 +++++++++++++++++-- sound/soc/sof/intel/hda-loader.c | 12 ++++++++++-- sound/soc/sof/loader.c | 12 ++++++++++-- sound/soc/sof/pcm.c | 29 ++++++++++++++++++++++++----- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 6285c6f99ea0b2..f1b99f2e4b303b 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -33,6 +33,15 @@ enum sof_device_type { /* * SOF Platform data. */ +struct snd_sof_machine { + const u8 id[ACPI_ID_LEN]; + const char *drv_name; + const char *sof_fw_filename; + const char *sof_tplg_filename; + const char *asoc_plat_name; + const struct snd_sof_dsp_ops *ops; +}; + struct snd_sof_pdata { u32 id; /* PCI/ACPI ID */ const struct firmware *fw; @@ -48,7 +57,10 @@ struct snd_sof_pdata { /* machine */ struct platform_device *pdev_mach; - const struct snd_soc_acpi_mach *machine; + union { + const struct snd_soc_acpi_mach *machine; + const struct snd_sof_machine *sof_machine; + }; }; /* @@ -57,7 +69,10 @@ struct snd_sof_pdata { */ struct sof_dev_desc { /* list of machines using this configuration */ - struct snd_soc_acpi_mach *machines; + union { + struct snd_soc_acpi_mach *machines; + struct snd_sof_machine *sof_machines; + }; /* Platform resource indexes in BAR / ACPI resources. */ /* Must set to -1 if not used - add new items to end */ diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 72df362b86360b..796d772a6a8d30 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -301,12 +301,20 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, bool first_boot) { struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + const char *fw_filename; /* set code loading condition to true */ sdev->code_loading = 1; - return request_firmware(&plat_data->fw, - plat_data->machine->sof_fw_filename, sdev->dev); + switch (plat_data->type) { + case SOF_DEVICE_SPI: + fw_filename = plat_data->sof_machine->sof_fw_filename; + break; + default: + fw_filename = plat_data->machine->sof_fw_filename; + } + + return request_firmware(&plat_data->fw, fw_filename, sdev->dev); } int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index d5839c0f3356ce..9a90045fceef66 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -212,13 +212,21 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, bool first_boot) { struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + const char *fw_filename; int ret; /* set code loading condition to true */ sdev->code_loading = 1; - ret = request_firmware(&plat_data->fw, - plat_data->machine->sof_fw_filename, sdev->dev); + switch (plat_data->type) { + case SOF_DEVICE_SPI: + fw_filename = plat_data->sof_machine->sof_fw_filename; + break; + default: + fw_filename = plat_data->machine->sof_fw_filename; + } + + ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); if (ret < 0) { dev_err(sdev->dev, "error: request firmware failed err: %d\n", diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d746cdf4d14eaa..76f801bc93df79 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -681,12 +681,21 @@ static int sof_pcm_probe(struct snd_soc_component *component) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pdata *plat_data = dev_get_platdata(component->dev); + const char *tplg_filename; int ret, err; /* load the default topology */ sdev->component = component; - ret = snd_sof_load_topology(sdev, - plat_data->machine->sof_tplg_filename); + + switch (plat_data->type) { + case SOF_DEVICE_SPI: + tplg_filename = plat_data->sof_machine->sof_tplg_filename; + break; + default: + tplg_filename = plat_data->machine->sof_tplg_filename; + } + + ret = snd_sof_load_topology(sdev, tplg_filename); if (ret < 0) { dev_err(sdev->dev, "error: failed to load DSP topology %d\n", ret); @@ -715,9 +724,19 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) { struct snd_soc_component_driver *pd = &sdev->plat_drv; struct snd_sof_pdata *plat_data = sdev->pdata; + const char *plat_name, *drv_name; + + switch (plat_data->type) { + case SOF_DEVICE_SPI: + plat_name = plat_data->sof_machine->asoc_plat_name; + drv_name = plat_data->sof_machine->drv_name; + break; + default: + plat_name = plat_data->machine->asoc_plat_name; + drv_name = plat_data->machine->drv_name; + } - dev_dbg(sdev->dev, "using platform alias %s\n", - plat_data->machine->asoc_plat_name); + dev_dbg(sdev->dev, "using platform alias %s\n", plat_name); pd->name = "sof-audio"; pd->probe = sof_pcm_probe; @@ -726,7 +745,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->compr_ops = &sof_compressed_ops; pd->pcm_new = sof_pcm_new; pd->pcm_free = sof_pcm_free; - pd->ignore_machine = plat_data->machine->drv_name; + pd->ignore_machine = drv_name; pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; pd->be_pcm_base = SOF_BE_PCM_BASE; pd->use_dai_pcm_id = true; From b647da25fe0ab5f635eae22df1137835471e9da8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 5 Sep 2018 14:19:47 +0200 Subject: [PATCH 174/298] sof: fix and enable compilation of SPI, add DT support This patch extends the SOF SPI implementation with some rather basic DT support, adds SPI IO functions, fixes compilation issues and enables compilation in Kconfig files. Signed-off-by: Guennadi Liakhovetski --- sound/soc/sof/Kconfig | 3 + sound/soc/sof/hw-spi.c | 134 +++++++++++++++++++++++++----------- sound/soc/sof/intel/Kconfig | 11 +++ sound/soc/sof/sof-spi-dev.c | 117 +++++++++++++++++++++++-------- 4 files changed, 194 insertions(+), 71 deletions(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 0036f1c85ba72c..f143a651b6b757 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -4,6 +4,9 @@ config SND_SOC_SOF_PCI config SND_SOC_SOF_ACPI tristate +config SND_SOC_SOF_SPI + tristate + config SND_SOC_SOF tristate "Sound Open Firmware Support" select SND_SOC_TOPOLOGY diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c index db5dcea73dee3e..47066f517e85c2 100644 --- a/sound/soc/sof/hw-spi.c +++ b/sound/soc/sof/hw-spi.c @@ -12,6 +12,8 @@ * Hardware interface for audio DSPs via SPI */ +#include +#include #include #include #include @@ -29,22 +31,69 @@ #include "sof-priv.h" #include "ops.h" -#include "intel.h" /* * Memory copy. */ -static void spi_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, - size_t size) +static void spi_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) { - // use spi_write() to copy data to DSP + u8 *buf; + int ret; + + if (offset) { + buf = kmalloc(size + offset, GFP_KERNEL); + if (!buf) { + dev_err(sdev->dev, "Buffer allocation failed\n"); + return; + } + } else { + buf = dest; + } + + ret = spi_read(to_spi_device(sdev->dev), buf, size + offset); + if (ret < 0) + dev_err(sdev->dev, "SPI read failed: %d\n", ret); + + if (offset) { + memcpy(dest, buf + offset, size); + kfree(buf); + } } -static void spi_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, - size_t size) +static void spi_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) { - // use spi_read() to copy data from DSP + int ret; + u8 *buf; + + if (offset) { + buf = kmalloc(size + offset, GFP_KERNEL); + if (!buf) { + dev_err(sdev->dev, "Buffer allocation failed\n"); + return; + } + + /* Use Read-Modify-Wwrite */ + ret = spi_read(to_spi_device(sdev->dev), buf, size + offset); + if (ret < 0) { + dev_err(sdev->dev, "SPI read failed: %d\n", ret); + goto free; + } + + memcpy(buf + offset, src, size); + } else { + buf = src; + } + + ret = spi_write(to_spi_device(sdev->dev), buf, size + offset); + if (ret < 0) + dev_err(sdev->dev, "SPI write failed: %d\n", ret); + +free: + if (offset) + kfree(buf); } /* @@ -59,7 +108,7 @@ static int spi_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) // read local buffer with SPI data - dev_info(sdev->dev, " Firmware info: version %d:%d-%s build %d on %s:%s\n", + dev_info(sdev->dev, "Firmware info: version %d:%d-%s build %d on %s:%s\n", v->major, v->minor, v->tag, v->build, v->date, v->time); return 0; @@ -69,26 +118,25 @@ static int spi_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) * IPC Mailbox IO */ -static void spi_mailbox_write(struct snd_sof_dev *sdev, u32 offset, - void *message, size_t bytes) +static void spi_mailbox_write(struct snd_sof_dev *sdev __maybe_unused, + u32 offset __maybe_unused, + void *message __maybe_unused, + size_t bytes __maybe_unused) { - void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; - - //memcpy_toio(dest, message, bytes); /* * this will copy to a local memory buffer that will be sent to DSP via * SPI at next IPC */ } -static void spi_mailbox_read(struct snd_sof_dev *sdev, u32 offset, +static void spi_mailbox_read(struct snd_sof_dev *sdev __maybe_unused, + u32 offset __maybe_unused, void *message, size_t bytes) { - void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + memset(message, 0, bytes); - //memcpy_fromio(message, src, bytes); /* - * this will copy from a local memory buffer that will be received from + * this will copy from a local memory buffer that was received from * DSP via SPI at last IPC */ } @@ -97,9 +145,8 @@ static void spi_mailbox_read(struct snd_sof_dev *sdev, u32 offset, * IPC Doorbell IRQ handler and thread. */ -static irqreturn_t spi_irq_handler(int irq, void *context) +static irqreturn_t spi_irq_handler(int irq __maybe_unused, void *context __maybe_unused) { - struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; int ret = IRQ_NONE; // on SPI based devices this will likely come via a SoC GPIO IRQ @@ -109,25 +156,21 @@ static irqreturn_t spi_irq_handler(int irq, void *context) return ret; } -static irqreturn_t spi_irq_thread(int irq, void *context) +static irqreturn_t spi_irq_thread(int irq __maybe_unused, void *context __maybe_unused) { - struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; - // read SPI data into local buffer and determine IPC cmd or reply /* * if reply. Handle Immediate reply from DSP Core and set DSP * state to ready */ - //snd_sof_ipc_reply(sdev, ipcx); /* if cmd, Handle messages from DSP Core */ - //snd_sof_ipc_msgs_rx(sdev); return IRQ_HANDLED; } -static int spi_is_ready(struct snd_sof_dev *sdev) +static int spi_is_ready(struct snd_sof_dev *sdev __maybe_unused) { // use local variable to store DSP command state. either DSP is ready // for new cmd or still processing current cmd. @@ -137,8 +180,6 @@ static int spi_is_ready(struct snd_sof_dev *sdev) static int spi_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - u64 cmd = msg->header; - /* send the message */ spi_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); @@ -160,7 +201,7 @@ static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) } else { /* reply correct size ? */ if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected 0x%lx got 0x%x bytes\n", + dev_err(sdev->dev, "error: reply expected 0x%zu got 0x%x bytes\n", msg->reply_size, reply.hdr.size); size = msg->reply_size; ret = -EINVAL; @@ -183,22 +224,26 @@ static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) static int spi_sof_probe(struct snd_sof_dev *sdev) { - struct snd_sof_pdata *pdata = sdev->pdata; - const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->parent, struct platform_device, dev); - int ret = 0; - /* get IRQ from Device tree or ACPI - register our IRQ */ + struct irq_data *irqd; + struct spi_device *spi = to_spi_device(pdev->dev.parent); + int ret; + + sdev->ipc_irq = spi->irq; dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); - ret = request_threaded_irq(sdev->ipc_irq, spi_irq_handler, - spi_irq_thread, IRQF_SHARED, "AudioDSP", - sdev); - if (ret < 0) { + irqd = irq_get_irq_data(sdev->ipc_irq); + if (!irqd) + return -EINVAL; + + ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq, + spi_irq_handler, spi_irq_thread, + irqd_get_trigger_type(irqd) | IRQF_ONESHOT, + "AudioDSP", sdev); + if (ret < 0) dev_err(sdev->dev, "error: failed to register IRQ %d\n", sdev->ipc_irq); - goto irq_err; - } return ret; } @@ -209,7 +254,12 @@ static int spi_sof_remove(struct snd_sof_dev *sdev) return 0; } -/* baytrail ops */ +static int spi_cmd_done(struct snd_sof_dev *sof_dev __maybe_unused, int dir __maybe_unused) +{ + return 0; +} + +/* SPI SOF ops */ struct snd_sof_dsp_ops snd_sof_spi_ops = { /* device init */ .probe = spi_sof_probe, @@ -235,9 +285,9 @@ struct snd_sof_dsp_ops snd_sof_spi_ops = { .cmd_done = spi_cmd_done, /* debug */ - .debug_map = spi_debugfs, - .debug_map_count = ARRAY_SIZE(spi_debugfs), - .dbg_dump = spi_dump, + .debug_map = NULL/*spi_debugfs*/, + .debug_map_count = 0/*ARRAY_SIZE(spi_debugfs)*/, + .dbg_dump = NULL/*spi_dump*/, /* module loading */ .load_module = snd_sof_parse_module_memcpy, diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index d777ddd80d893e..d41d60b037426e 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -9,6 +9,17 @@ config SND_SOC_SOF_INTEL Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_SPIDSP + tristate "SOF support over the SPI bus" + depends on SND_SOC_SOF + depends on SPI + select SND_SOC_SOF_SPI + help + This adds support for Sound Open Firmware over SPI for Device + Tree based systems. + Say Y if you have such a device. + If unsure select "N". + if SND_SOC_SOF_INTEL config SND_SOC_SOF_BAYTRAIL diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index c55be66267f6a4..bc38e50941a2a9 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -17,7 +17,9 @@ #include #include #include +#include #include "sof-priv.h" +#include "hw-spi.h" static const struct dev_pm_ops sof_spi_pm = { SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) @@ -26,14 +28,47 @@ static const struct dev_pm_ops sof_spi_pm = { .suspend_late = snd_sof_suspend_late, }; +/* FIXME: replace with some meaningful values */ +static struct snd_sof_machine sof_spi_machines[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .sof_fw_filename = "intel/sof-spi.ri", + .sof_tplg_filename = "intel/sof-spi.tplg", + .asoc_plat_name = "0000:00:0e.0", + .ops = &snd_sof_spi_ops, + }, +}; + +static const struct sof_dev_desc spi_desc = { + .sof_machines = sof_spi_machines, + .nocodec_fw_filename = "intel/sof-spi.ri", + .nocodec_tplg_filename = "intel/sof-spi.tplg", + .resindex_lpe_base = -1, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, +}; + static int sof_spi_probe(struct spi_device *spi) { struct device *dev = &spi->dev; - const struct snd_sof_machine *mach; - struct snd_sof_machine *m; + const struct sof_dev_desc *desc = of_device_get_match_data(dev); + struct snd_sof_machine *machines; + struct snd_sof_machine *mach; struct snd_sof_pdata *sof_pdata; struct sof_platform_priv *priv; - int ret = 0; + const char *tplg, *fw; + struct gpio_desc *gpiod; + int ret, irq; + + if (!dev->of_node || !desc) + return -ENODEV; + + machines = desc->sof_machines; + if (!machines) + return -ENODEV; dev_dbg(&spi->dev, "SPI DSP detected"); @@ -46,24 +81,49 @@ static int sof_spi_probe(struct spi_device *spi) if (!sof_pdata) return -ENOMEM; + ret = of_property_read_string(dev->of_node, "tplg_filename", &tplg); + if (ret < 0 || !tplg) + return -EINVAL; + + ret = of_property_read_string(dev->of_node, "fw_filename", &fw); + if (ret < 0 || !fw) + return -EINVAL; + + /* + * Get an IRQ GPIO descriptor from an "irq-gpios" property + * If the IRQ is optional, use devm_gpiod_get_optional() + */ + gpiod = devm_gpiod_get(dev, "irq", GPIOD_IN); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + + irq = gpiod_to_irq(gpiod); + if (irq < 0) + return irq; + + /* TODO: add any required regulators */ + /* use nocodec machine atm */ dev_err(dev, "No matching ASoC machine driver found - using nocodec\n"); sof_pdata->drv_name = "sof-nocodec"; - m = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - if (!m) + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + if (!mach) return -ENOMEM; - m->drv_name = "sof-nocodec"; - m->sof_fw_filename = desc->nocodec_fw_filename; - m->sof_tplg_filename = desc->nocodec_tplg_filename; - m->ops = desc->machines[0].ops; - m->asoc_plat_name = "sof-platform"; - mach = m; - - sof_pdata->id = pci_id->device; - sof_pdata->name = spi_name(spi); - sof_pdata->machine = mach; - sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + mach->drv_name = "sof-nocodec"; + /* + * desc->nocodec_*_filename are selected as long as nocodec is used. + * Later machine->*_filename will have to be used. + */ + mach->sof_fw_filename = desc->nocodec_fw_filename; + mach->sof_tplg_filename = desc->nocodec_tplg_filename; + mach->ops = machines[0].ops; + mach->asoc_plat_name = "sof-platform"; + + sof_pdata->id = -1; + sof_pdata->name = dev_name(&spi->dev); + sof_pdata->sof_machine = mach; + sof_pdata->desc = desc; priv->sof_pdata = sof_pdata; sof_pdata->dev = dev; sof_pdata->type = SOF_DEVICE_SPI; @@ -74,6 +134,7 @@ static int sof_spi_probe(struct spi_device *spi) sof_pdata, sizeof(*sof_pdata)); if (IS_ERR(sof_pdata->pdev_mach)) return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", dev_name(&sof_pdata->pdev_mach->dev)); @@ -85,6 +146,8 @@ static int sof_spi_probe(struct spi_device *spi) return ret; } + spi->irq = irq; + /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY); pm_runtime_use_autosuspend(dev); @@ -102,28 +165,24 @@ static int sof_spi_remove(struct spi_device *spi) if (!IS_ERR_OR_NULL(priv->pdev_pcm)) platform_device_unregister(priv->pdev_pcm); release_firmware(sof_pdata->fw); + + return 0; } -static struct spi_driver wm8731_spi_driver = { +const struct of_device_id sof_of_match[] = { + { .compatible = "sof,spi-sue-creek", .data = &spi_desc }, + { } +}; + +static struct spi_driver sof_spi_driver = { .driver = { - .name = "sof-spi-dev", +o .name = "sof-spi-dev", .of_match_table = sof_of_match, }, .probe = sof_spi_probe, .remove = sof_spi_remove, }; -static const struct snd_sof_machine sof_spi_machines[] = { - { "INT343A", "bxt_alc298s_i2s", "intel/sof-spi.ri", - "intel/sof-spi.tplg", "0000:00:0e.0", &snd_sof_spi_ops }, -}; - -static const struct sof_dev_desc spi_desc = { - .machines = sof_spi_machines, - .nocodec_fw_filename = "intel/sof-spi.ri", - .nocodec_tplg_filename = "intel/sof-spi.tplg" -}; - static int __init sof_spi_modinit(void) { int ret; From 3c289608d160b45a7a123e269ec9303555b71425 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 5 Sep 2018 15:15:24 +0200 Subject: [PATCH 175/298] rpi: add a sample DT node for SPI SOF This sample SPI SOF DT node is solely an illustration to accompany respective SOF changes, all values are completely fictitious. Signed-off-by: Guennadi Liakhovetski --- arch/arm/boot/dts/bcm283x-rpi-sof.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 arch/arm/boot/dts/bcm283x-rpi-sof.dtsi diff --git a/arch/arm/boot/dts/bcm283x-rpi-sof.dtsi b/arch/arm/boot/dts/bcm283x-rpi-sof.dtsi new file mode 100644 index 00000000000000..1b69ac517f95bb --- /dev/null +++ b/arch/arm/boot/dts/bcm283x-rpi-sof.dtsi @@ -0,0 +1,17 @@ +// FIXME: which SPI bus is the card connected to? +@spi { + status = "okay"; + + sue-creek: sof-sue-creek@0 { + reg = <0>; + compatible = "sof,spi-sue-creek"; + // FIXME: frequency value + spi-max-frequency = <54000000>; + fw_filename = "intel/sof-spi.ri"; + tplg_filename = "intel/sof-spi.tplg"; + // FIXME: GPIO controller and IRQ number and sense + interrupt-parent = <&gpio1>; + interrupts = <9 IRQ_TYPE_EDGE_FALLING>; + irq-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; + }; +}; From 6f71197ef642e375c8b663529175cd55b062322d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 5 Sep 2018 19:27:16 +0200 Subject: [PATCH 176/298] rpi: include SOF DT nodes Add SOF DT nodes to Raspberry Pi 2-B. Signed-off-by: Guennadi Liakhovetski --- arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts index 2fef70a099535d..490efdcebefbe7 100644 --- a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts @@ -4,6 +4,7 @@ #include "bcm2835-rpi.dtsi" #include "bcm283x-rpi-smsc9514.dtsi" #include "bcm283x-rpi-usb-host.dtsi" +#include "bcm283x-rpi-sof.dtsi" / { compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; From 128ff8c47592155d7ae4e80bd1468d080dfe5b96 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 6 Sep 2018 15:38:35 +0800 Subject: [PATCH 177/298] ASoC: SOF: uapi: sof-ipc: add SOF_IPC_PANIC_WFI Signed-off-by: Pan Xiuli --- include/uapi/sound/sof-ipc.h | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 985919995b5f31..969e30b7b42af1 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -110,16 +110,17 @@ #define SOF_IPC_PANIC_MAGIC 0x0dead000 #define SOF_IPC_PANIC_MAGIC_MASK 0x0ffff000 #define SOF_IPC_PANIC_CODE_MASK 0x00000fff -#define SOF_IPC_PANIC_MEM (SOF_IPC_PANIC_MAGIC | 0) -#define SOF_IPC_PANIC_WORK (SOF_IPC_PANIC_MAGIC | 1) -#define SOF_IPC_PANIC_IPC (SOF_IPC_PANIC_MAGIC | 2) -#define SOF_IPC_PANIC_ARCH (SOF_IPC_PANIC_MAGIC | 3) -#define SOF_IPC_PANIC_PLATFORM (SOF_IPC_PANIC_MAGIC | 4) -#define SOF_IPC_PANIC_TASK (SOF_IPC_PANIC_MAGIC | 5) -#define SOF_IPC_PANIC_EXCEPTION (SOF_IPC_PANIC_MAGIC | 6) -#define SOF_IPC_PANIC_DEADLOCK (SOF_IPC_PANIC_MAGIC | 7) -#define SOF_IPC_PANIC_STACK (SOF_IPC_PANIC_MAGIC | 8) -#define SOF_IPC_PANIC_IDLE (SOF_IPC_PANIC_MAGIC | 9) +#define SOF_IPC_PANIC_MEM (SOF_IPC_PANIC_MAGIC | 0x0) +#define SOF_IPC_PANIC_WORK (SOF_IPC_PANIC_MAGIC | 0x1) +#define SOF_IPC_PANIC_IPC (SOF_IPC_PANIC_MAGIC | 0x2) +#define SOF_IPC_PANIC_ARCH (SOF_IPC_PANIC_MAGIC | 0x3) +#define SOF_IPC_PANIC_PLATFORM (SOF_IPC_PANIC_MAGIC | 0x4) +#define SOF_IPC_PANIC_TASK (SOF_IPC_PANIC_MAGIC | 0x5) +#define SOF_IPC_PANIC_EXCEPTION (SOF_IPC_PANIC_MAGIC | 0x6) +#define SOF_IPC_PANIC_DEADLOCK (SOF_IPC_PANIC_MAGIC | 0x7) +#define SOF_IPC_PANIC_STACK (SOF_IPC_PANIC_MAGIC | 0x8) +#define SOF_IPC_PANIC_IDLE (SOF_IPC_PANIC_MAGIC | 0x9) +#define SOF_IPC_PANIC_WFI (SOF_IPC_PANIC_MAGIC | 0xa) /* * SOF memory capabilities, add new ones at the end From eeb4f6cef30d90ceb6b9106adc489990d867b730 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 6 Sep 2018 15:39:42 +0800 Subject: [PATCH 178/298] ASoC: SOF: add SOF_IPC_PANIC_WFI error info Add verbose info for the panic code Signed-off-by: Pan Xiuli --- sound/soc/sof/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index a822be0f8d3945..c16bf46c4e2db8 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -145,6 +145,7 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_DEADLOCK, "deadlock"}, {SOF_IPC_PANIC_STACK, "stack overflow"}, {SOF_IPC_PANIC_IDLE, "can't enter idle"}, + {SOF_IPC_PANIC_WFI, "invalid wait state"}, }; int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, From aa9b27645c99c2b3ad97b99066100cce8db82f6a Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 6 Sep 2018 20:16:01 +0300 Subject: [PATCH 179/298] ASoC: dapm: fix kcontrols for effect widgets This patch adds the handling of snd_soc_dapm_effect that was missing. Signed-off-by: Ranjani Sridharan Signed-off-by: Seppo Ingalsuo --- sound/soc/soc-dapm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a099c3e4550478..7fc7c7374ff061 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -856,6 +856,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_pga: + case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: wname_in_long_name = true; kcname_in_long_name = true; @@ -2337,6 +2338,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, case snd_soc_dapm_dac: case snd_soc_dapm_adc: case snd_soc_dapm_pga: + case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: @@ -3075,6 +3077,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_new_mux(w); break; case snd_soc_dapm_pga: + case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: dapm_new_pga(w); break; From fcb87ed60e51f672c9037f0805fe1aa784b3fa4e Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 6 Sep 2018 20:19:16 +0300 Subject: [PATCH 180/298] SOF: Increase DSP maximum IPC size to 384 This patch updates the maximum size into the same as used currently in DSP side. Signed-off-by: Seppo Ingalsuo --- include/uapi/sound/sof-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 969e30b7b42af1..29cf46da5f3103 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -102,7 +102,7 @@ #define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff) /* maximum message size for mailbox Tx/Rx */ -#define SOF_IPC_MSG_MAX_SIZE 128 +#define SOF_IPC_MSG_MAX_SIZE 384 /* * SOF panic codes From f0cc5eab44f1e9106f8708cfa2962307c982e977 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 6 Sep 2018 20:22:02 +0300 Subject: [PATCH 181/298] SOF: Update EQ IPC header file This patch updates the definitions and structs to indentical as used on DSP side. It will help using the equalizer setup blobs in user space. Signed-off-by: Seppo Ingalsuo --- include/uapi/sound/sof-eq.h | 70 +++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/include/uapi/sound/sof-eq.h b/include/uapi/sound/sof-eq.h index f94712667ecee3..081a68a512e696 100644 --- a/include/uapi/sound/sof-eq.h +++ b/include/uapi/sound/sof-eq.h @@ -22,42 +22,70 @@ #define SOF_EQ_FIR_MAX_SIZE 4096 /* Max size allowed for coef data in bytes */ +#define SOF_EQ_FIR_MAX_LENGTH 192 /* Max length for individual filter */ + /* * eq_fir_configuration data structure contains this information + * uint32_t size + * This is the number of bytes need to store the received EQ + * configuration. * uint16_t channels_in_config * This describes the number of channels in this EQ config data. It * can be different from PLATFORM_MAX_CHANNELS. * uint16_t number_of_responses * 0=no responses, 1=one response defined, 2=two responses defined, etc. * int16_t data[] - * assign_response[STREAM_MAX_CHANNELS] - * -1 = not defined, 0 = use first response, 1 = use 2nd response, etc. - * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the - * same first defined response and leave channels 4-7 unequalized. + * assign_response[channels_in_config] + * 0 = use first response, 1 = use 2nd response, etc. + * E.g. {0, 0, 0, 0, 1, 1, 1, 1} would apply to channels 0-3 the + * same first defined response and for to channels 4-7 the second. * coef_data[] - * Repeated data { filter_length, input_shift, output_shift, h[] } + * Repeated data + * { filter_length, output_shift, h[] } * for every EQ response defined where vector h has filter_length * number of coefficients. Coefficients in h[] are in Q1.15 format. * E.g. 16384 (Q1.15) = 0.5. The shifts are number of right shifts. + * + * NOTE: The channels_in_config must be even to have coef_data aligned to + * 32 bit word in RAM. Therefore a mono EQ assign must be duplicated to 2ch + * even if it would never used. Similarly a 5ch EQ assign must be increased + * to 6ch. EQ init will return an error if this is not met. + * + * NOTE: The filter_length must be multiple of four. Therefore the filter must + * be padded from the end with zeros have this condition met. */ struct sof_eq_fir_config { + uint32_t size; uint16_t channels_in_config; uint16_t number_of_responses; int16_t data[]; }; +struct sof_eq_fir_coef_data { + int16_t length; /* Number of FIR taps */ + int16_t out_shift; /* Amount of right shifts at output */ + int16_t coef[]; /* FIR coefficients */ +}; + +/* In the struct above there's two words (length, shift) before the actual + * FIR coefficients. This information is used in parsing of the config blob. + */ +#define SOF_EQ_FIR_COEF_NHEADER 2 + /* IIR EQ type */ /* Component will reject non-matching configuration. The version number need * to be incremented with any ABI changes in function fir_cmd(). */ -#define SOF_EQ_FIR_ABI_VERSION 1 +#define SOF_EQ_IIR_ABI_VERSION 1 #define SOF_EQ_IIR_IDX_SWITCH 0 #define SOF_EQ_IIR_MAX_SIZE 1024 /* Max size allowed for coef data in bytes */ +#define SOF_EQ_IIR_MAX_RESPONSES 8 /* A blob can define max 8 IIR EQs */ + /* eq_iir_configuration * uint32_t channels_in_config * This describes the number of channels in this EQ config data. It @@ -94,9 +122,37 @@ struct sof_eq_fir_config { */ struct sof_eq_iir_config { + uint32_t size; uint32_t channels_in_config; uint32_t number_of_responses; - int32_t data[]; + int32_t data[]; /* eq_assign[channels], eq 0, eq 1, ... */ +}; + +struct sof_eq_iir_header_df2t { + uint32_t num_sections; + uint32_t num_sections_in_series; + int32_t biquads[]; /* Repeated biquad coefficients */ +}; + +struct sof_eq_iir_biquad_df2t { + int32_t a2; /* Q2.30 */ + int32_t a1; /* Q2.30 */ + int32_t b2; /* Q2.30 */ + int32_t b1; /* Q2.30 */ + int32_t b0; /* Q2.30 */ + int32_t output_shift; /* Number of right shifts */ + int32_t output_gain; /* Q2.14 */ }; +/* A full 22th order equalizer with 11 biquads cover octave bands 1-11 in + * in the 0 - 20 kHz bandwidth. + */ +#define SOF_EQ_IIR_DF2T_BIQUADS_MAX 11 + +/* The number of int32_t words in sof_eq_iir_header_df2t */ +#define SOF_EQ_IIR_NHEADER_DF2T 2 + +/* The number of int32_t words in sof_eq_iir_biquad_df2t */ +#define SOF_EQ_IIR_NBIQUAD_DF2T 7 + #endif /* EQ_H */ From 7cf3630456b12e5b73a21719bebfb8a505847a8c Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 6 Sep 2018 20:26:36 +0300 Subject: [PATCH 182/298] SOF Topology: Add ext bytes put and get feature This patch adds the binary control into topology parsing and provides for ASoC the SOF ext bytes put and get functions. Signed-off-by: Ranjani Sridharan Signed-off-by: Seppo Ingalsuo --- sound/soc/sof/control.c | 95 ++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 6 +++ sound/soc/sof/topology.c | 107 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 205 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 03c1a980f141b2..71914d0898dadb 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -268,3 +268,98 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, err); return ret; } + +int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + u32 header[2]; + int ret; + int err; + int length; + const int max_length = SOF_IPC_MSG_MAX_SIZE - + sizeof(struct sof_ipc_ctrl_data) - + sizeof(struct sof_abi_hdr); + + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: bytes put failed to resume %d\n", + ret); + return ret; + } + + /* the first two ints of the bytes data contain a dummy tag + * and the size, so copy from the 3rd int + * TODO: or should we send the size as well so the firmware + * can allocate memory accordingly? + */ + if (copy_from_user(&header, binary_data, 2*sizeof(u32))) + return -EFAULT; + + length = header[1]; + dev_dbg(sdev->dev, "size of bytes put data is %d\n", length); + if (length > max_length) { + dev_err(sdev->dev, "error: size is too large, bytes max is %d\n", + max_length); + ret = -EINVAL; + goto out; + } + + if (copy_from_user(cdata->data->data, binary_data + 2, length)) + return -EFAULT; + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->comp_abi = SOF_ABI_VERSION; + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_DATA, + SOF_CTRL_TYPE_DATA_SET, scontrol->cmd); + + +out: + pm_runtime_mark_last_busy(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err(sdev->dev, "error: failed to idle %d\n", err); + + return ret; +} + +int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, + unsigned int __user *binary_data, + unsigned int size) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int tag = 0; + + pm_runtime_get_sync(sdev->dev); + + dev_dbg(sdev->dev, "getting data and command is %d\n", scontrol->cmd); + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_DATA, + SOF_CTRL_TYPE_DATA_GET, scontrol->cmd); + + /* TODO: replace 252 with actual size */ + if (copy_to_user(binary_data, &tag, sizeof(u32))) + return -EFAULT; + if (copy_to_user(binary_data + 1, &size, sizeof(u32))) + return -EFAULT; + if (copy_to_user(binary_data + 2, cdata->data->data, + SOF_IPC_MSG_MAX_SIZE - sizeof(struct sof_ipc_ctrl_data))) + return -EFAULT; + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e8b0c65b1b4a3e..802bf1bc7812ef 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -501,6 +501,12 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *bytes, + unsigned int size); +int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, + unsigned int __user *bytes, + unsigned int size); /* * DSP Architectures. diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e4b7cc0a96f937..fab12dc3d1de95 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -269,6 +269,30 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, return 0; } +static int sof_control_load_bytes(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_ctrl_data *cdata; + + /* init the get/put bytes data */ + scontrol->size = SOF_IPC_MSG_MAX_SIZE; + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + cdata = scontrol->control_data; + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->cmd = SOF_CTRL_CMD_BINARY; + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + return 0; +} + /* * Topology Token Parsing. * New tokens should be added to headers and parsing tables below. @@ -381,6 +405,16 @@ static const struct sof_topology_token src_tokens[] = { static const struct sof_topology_token tone_tokens[] = { }; +/* EQ FIR */ +/* +static const struct sof_topology_token eq_fir_tokens[] = { +}; +*/ + +/* EQ IIR */ +static const struct sof_topology_token eq_iir_tokens[] = { +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -707,6 +741,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_ctl_hdr *hdr) { struct soc_mixer_control *sm; + struct soc_bytes_ext *sbe; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_dobj *dobj = NULL; struct snd_sof_control *scontrol; @@ -730,8 +765,12 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, dobj = &sm->dobj; ret = sof_control_load_volume(scomp, scontrol, kc, hdr); break; - case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_BYTES: + sbe = (struct soc_bytes_ext *)kc->private_value; + dobj = &sbe->dobj; + ret = sof_control_load_bytes(scomp, scontrol, kc, hdr); + break; + case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_CTL_STROBE: @@ -1306,6 +1345,66 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, kfree(tone); return ret; } +/* + * Effect Topology. Only IIR equalizer is supported at this moment. + * TODO: Need to add also FIR support and have a way to add other + * effects and enhancements. + */ + +static int sof_widget_load_effect(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_eq_iir *eq; + int ret; + + eq = kzalloc(sizeof(*eq), GFP_KERNEL); + if (!eq) + return -ENOMEM; + + /* configure IIR EQ IPC message */ + eq->comp.hdr.size = sizeof(*eq); + eq->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + eq->comp.id = swidget->comp_id; + eq->comp.pipeline_id = index; + + eq->comp.type = SOF_COMP_EQ_IIR; + ret = sof_parse_tokens(scomp, eq, eq_iir_tokens, + ARRAY_SIZE(eq_iir_tokens), private->array, + le32_to_cpu(private->size)); + if (ret) { + dev_err(sdev->dev, "error: parse EQ tokens failed %d\n", + private->size); + goto err; + } + + ret = sof_parse_tokens(scomp, &eq->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret) { + dev_err(sdev->dev, "error: parse EQ.cfg tokens failed %d\n", + le32_to_cpu(private->size)); + goto err; + } + + dev_dbg(sdev->dev, "eq iir %s created\n", swidget->widget->name); + + sof_dbg_comp_config(scomp, &eq->config); + + swidget->private = (void *)eq; + + ret = sof_ipc_tx_message(sdev->ipc, eq->comp.hdr.cmd, eq, + sizeof(*eq), r, sizeof(*r)); + + if (ret >= 0) + return ret; +err: + kfree(eq); + return ret; +} /* * Generic widget loader. @@ -1403,12 +1502,14 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_siggen: ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); break; + case snd_soc_dapm_effect: + ret = sof_widget_load_effect(scomp, index, swidget, tw, &reply); + break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: case snd_soc_dapm_kcontrol: - case snd_soc_dapm_effect: default: dev_warn(sdev->dev, "warning: widget type %d name %s not handled\n", swidget->id, tw->name); @@ -2110,7 +2211,7 @@ static const struct snd_soc_tplg_kcontrol_ops sof_io_ops[] = { /* vendor specific bytes ext handlers available for binding */ static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { -{}, + {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_ext_get, snd_sof_bytes_ext_put}, }; static struct snd_soc_tplg_ops sof_tplg_ops = { From c9d77b1a3da4ded8dc18ef5de1109e5eb401f624 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Sep 2018 11:14:21 +0200 Subject: [PATCH 183/298] SOF: fix compilation and a superfluous IRQ release Remove a stray character and add a missing header. The GPIO IRQ is requested as a managed resource during probing, therefore there's no need to explicitly release it during driver unbinding. Signed-off-by: Guennadi Liakhovetski --- sound/soc/sof/hw-spi.c | 1 - sound/soc/sof/hw-spi.h | 6 ++++++ sound/soc/sof/sof-spi-dev.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sof/hw-spi.h diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c index 47066f517e85c2..6e783c9fed8761 100644 --- a/sound/soc/sof/hw-spi.c +++ b/sound/soc/sof/hw-spi.c @@ -250,7 +250,6 @@ static int spi_sof_probe(struct snd_sof_dev *sdev) static int spi_sof_remove(struct snd_sof_dev *sdev) { - free_irq(sdev->ipc_irq, sdev); return 0; } diff --git a/sound/soc/sof/hw-spi.h b/sound/soc/sof/hw-spi.h new file mode 100644 index 00000000000000..1929856eabec90 --- /dev/null +++ b/sound/soc/sof/hw-spi.h @@ -0,0 +1,6 @@ +#ifndef HW_SPI_H +#define HW_SPI_H + +extern struct snd_sof_dsp_ops snd_sof_spi_ops; + +#endif diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index bc38e50941a2a9..442ae5f9b67590 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -176,7 +176,7 @@ const struct of_device_id sof_of_match[] = { static struct spi_driver sof_spi_driver = { .driver = { -o .name = "sof-spi-dev", + .name = "sof-spi-dev", .of_match_table = sof_of_match, }, .probe = sof_spi_probe, From 0f9af236dbcbeed27df44e846f2b32db422b2bc8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Sep 2018 12:06:10 +0200 Subject: [PATCH 184/298] sof: add a check for the IRQ GPIO level Checking for the IRQ GPIO level once an IRQ has been triggered shouldn't be necessary, once an IRQ has been triggered, so this might end up being superfluous. Signed-off-by: Guennadi Liakhovetski --- include/sound/sof.h | 4 ++++ sound/soc/sof/hw-spi.c | 30 ++++++++++++++++++++++++------ sound/soc/sof/sof-spi-dev.c | 2 ++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index f1b99f2e4b303b..bc87e8800ae718 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -55,6 +55,10 @@ struct snd_sof_pdata { /* descriptor */ const struct sof_dev_desc *desc; + /* SPI data */ + unsigned int gpio; + unsigned int active; + /* machine */ struct platform_device *pdev_mach; union { diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c index 6e783c9fed8761..71325c44031156 100644 --- a/sound/soc/sof/hw-spi.c +++ b/sound/soc/sof/hw-spi.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -145,15 +146,25 @@ static void spi_mailbox_read(struct snd_sof_dev *sdev __maybe_unused, * IPC Doorbell IRQ handler and thread. */ -static irqreturn_t spi_irq_handler(int irq __maybe_unused, void *context __maybe_unused) +/* + * If the handler only has to wake up the thread, we might use the standard one + * as well + */ +static irqreturn_t spi_irq_handler(int irq __maybe_unused, void *context) { - int ret = IRQ_NONE; + const struct snd_sof_dev *sdev = context; + const struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct snd_sof_pdata *sof_pdata = dev_get_platdata(&pdev->dev); // on SPI based devices this will likely come via a SoC GPIO IRQ // check if GPIO is assetred and if so run thread. + if (sof_pdata->gpio >= 0 && + gpio_get_value(sof_pdata->gpio) == sof_pdata->active) + return IRQ_WAKE_THREAD; - return ret; + return IRQ_NONE; } static irqreturn_t spi_irq_thread(int irq __maybe_unused, void *context __maybe_unused) @@ -226,9 +237,11 @@ static int spi_sof_probe(struct snd_sof_dev *sdev) { struct platform_device *pdev = container_of(sdev->parent, struct platform_device, dev); + struct snd_sof_pdata *sof_pdata = dev_get_platdata(&pdev->dev); /* get IRQ from Device tree or ACPI - register our IRQ */ struct irq_data *irqd; struct spi_device *spi = to_spi_device(pdev->dev.parent); + unsigned int irq_trigger, irq_sense; int ret; sdev->ipc_irq = spi->irq; @@ -237,10 +250,15 @@ static int spi_sof_probe(struct snd_sof_dev *sdev) if (!irqd) return -EINVAL; + irq_trigger = irqd_get_trigger_type(irqd); + irq_sense = irq_trigger & IRQ_TYPE_SENSE_MASK; + sof_pdata->active = irq_sense == IRQ_TYPE_EDGE_RISING || + irq_sense == IRQ_TYPE_LEVEL_HIGH; + ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq, - spi_irq_handler, spi_irq_thread, - irqd_get_trigger_type(irqd) | IRQF_ONESHOT, - "AudioDSP", sdev); + spi_irq_handler, spi_irq_thread, + irq_trigger | IRQF_ONESHOT, + "AudioDSP", sdev); if (ret < 0) dev_err(sdev->dev, "error: failed to register IRQ %d\n", sdev->ipc_irq); diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index 442ae5f9b67590..c5b3befa19e37d 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -97,6 +97,8 @@ static int sof_spi_probe(struct spi_device *spi) if (IS_ERR(gpiod)) return PTR_ERR(gpiod); + sof_pdata->gpio = desc_to_gpio(gpiod); + irq = gpiod_to_irq(gpiod); if (irq < 0) return irq; From 9f93a3a0275c5c34353a0e1908e2f5cde80c7657 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Sep 2018 17:30:28 +0200 Subject: [PATCH 185/298] rpi: temporarily disable SOF DT The SOF DT doesn't compile yet, it's only provided as an example, disable it for now to prevent automated builds from failing. Signed-off-by: Guennadi Liakhovetski --- arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts index 490efdcebefbe7..f0b2b4117e7b72 100644 --- a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts @@ -4,7 +4,7 @@ #include "bcm2835-rpi.dtsi" #include "bcm283x-rpi-smsc9514.dtsi" #include "bcm283x-rpi-usb-host.dtsi" -#include "bcm283x-rpi-sof.dtsi" +//#include "bcm283x-rpi-sof.dtsi" / { compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; From 2170a5621d13661d224a3edc8c622ae4b52ad09e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 7 Sep 2018 14:36:13 +0200 Subject: [PATCH 186/298] Revert "sof: fix compilation on non X86 platforms" This reverts commit 41b8f5d8b870fb6f8bc6694248998a849156b354 and fixes #113. Signed-off-by: Guennadi Liakhovetski --- sound/soc/sof/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index c16bf46c4e2db8..ac20b901f32369 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -198,7 +198,7 @@ int snd_sof_create_page_table(struct snd_sof_dev *sdev, { int i, pages; - pages = PAGE_ALIGN(size); + pages = snd_sgbuf_aligned_pages(size); dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", dmab->area, size, pages); From 66dc735d45f64fcfc868c3772bb2c4c52446c000 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 7 Sep 2018 14:58:50 +0200 Subject: [PATCH 187/298] sound: snd_sgbuf_aligned_pages() doesn't need CONFIG_SND_DMA_SGBUF The snd_sgbuf_aligned_pages() inline function is currently only defined if CONFIG_SND_DMA_SGBUF is enabled. This isn't needed, the function doesn't need SG DMA to be valid. Move it outside of #ifdef. Signed-off-by: Guennadi Liakhovetski --- include/sound/memalloc.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 9c3db3dce32b4a..3f8e6eade1ef52 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -90,14 +90,6 @@ struct snd_sg_buf { struct device *dev; }; -/* - * return the pages matching with the given byte size - */ -static inline unsigned int snd_sgbuf_aligned_pages(size_t size) -{ - return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; -} - /* * return the physical address at the corresponding offset */ @@ -140,6 +132,14 @@ static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab, #endif /* CONFIG_SND_DMA_SGBUF */ +/* + * return the pages matching with the given byte size + */ +static inline unsigned int snd_sgbuf_aligned_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + /* allocate/release a buffer */ int snd_dma_alloc_pages(int type, struct device *dev, size_t size, struct snd_dma_buffer *dmab); From 679b291733c2bcfabdb145ed699a5638b32d794e Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Tue, 4 Sep 2018 21:32:49 +0800 Subject: [PATCH 188/298] ASoC:core:bug fix: oops caused by pointer dereference. oops case: the snd_card{} would be freed ahead of dynamic kcontrol remove process, in this process, the snd_card{} will be used, then the oops will be hit. the solution: the operation of removing the dynamic kcontrol should be ahead of snd_card{} free. Signed-off-by: Wu Zhigang --- sound/soc/soc-core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 71fdac067a47bd..49e82131141f0a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2168,11 +2168,21 @@ static int soc_probe(struct platform_device *pdev) static int soc_cleanup_card_resources(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; + struct snd_soc_component *component; + int ret; /* make sure any delayed work runs */ list_for_each_entry(rtd, &card->rtd_list, list) flush_delayed_work(&rtd->delayed_work); + /* remove dynamic controls for all component driver */ + list_for_each_entry(component, &card->component_dev_list, card_list) { + ret = snd_soc_tplg_component_remove(component, SND_SOC_TPLG_INDEX_ALL); + if (ret < 0) + dev_err(component->dev, + "error: component free failed %d\n", ret); + } + /* free the ALSA card at first; this syncs with pending operations */ snd_card_free(card->snd_card); From 64cf3e31650738d7e455880a2173223e847ff406 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Mon, 10 Sep 2018 17:26:49 +0800 Subject: [PATCH 189/298] ASoC:topology:delete dynamic object during widget remove remove the dynamic object during the topology free stage. to avoid multi remove operation. Signed-off-by: Wu Zhigang --- sound/soc/soc-topology.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 05d177d689e264..00ab616f1bfc67 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -496,6 +496,7 @@ static void remove_widget(struct snd_soc_component *comp, } free_news: + list_del(&dobj->list); kfree(w->kcontrol_news); /* widget w is freed by soc-dapm.c */ From ae5f868eeac7af221197dc1c5e4449dbd4b8813c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 8 Sep 2018 22:00:38 -0700 Subject: [PATCH 190/298] ASoC: SOF: restore byte control data after resume This patch adds support for retreiving byte control data after the DSP comes out of D3. Signed-off-by: Ranjani Sridharan Signed-off-by: Seppo Ingalsuo --- sound/soc/sof/pm.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 0ebc3fc8cd9d6a..8bef8d67ca991c 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -29,6 +29,7 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) struct sof_ipc_comp_dai *comp_dai; struct sof_ipc_hdr *hdr; struct snd_sof_control *scontrol = NULL; + int ipc_cmd, ctrl_type, sof_abi; int ret = 0; /* restore pipeline components */ @@ -129,10 +130,33 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { /* notify DSP of kcontrol values */ - ret = snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_SET, - scontrol->cmd); + switch (scontrol->cmd) { + case SOF_CTRL_CMD_VOLUME: + ipc_cmd = SOF_IPC_COMP_SET_VALUE; + ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; + ret = snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + ipc_cmd, ctrl_type, + scontrol->cmd); + break; + case SOF_CTRL_CMD_BINARY: + + /* Check if control data contains valid data. + * SOF_ABI_MAGIC will not match if there is no data. + */ + ipc_cmd = SOF_IPC_COMP_SET_DATA; + ctrl_type = SOF_CTRL_TYPE_DATA_SET; + sof_abi = scontrol->control_data->data->magic; + if (sof_abi == SOF_ABI_MAGIC) + ret = snd_sof_ipc_set_comp_data(sdev->ipc, + scontrol, + ipc_cmd, + ctrl_type, + scontrol->cmd); + break; + + default: + break; + } if (ret < 0) { dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", From b0266deb5cd4e9910350feacf1be4262f7c602ee Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 11 Sep 2018 15:40:55 +0800 Subject: [PATCH 191/298] ASoC: SOF: Kconfig: add an item to force IPC position update for debug Signed-off-by: Keyon Jie --- sound/soc/sof/Kconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index f143a651b6b757..aab154cbf1a6f7 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -68,5 +68,19 @@ config SND_SOC_SOF_DEBUG_VERBOSE_IPC if you are trying to debug IPC with the DSP firmware. If unsure select "N". +config SND_SOC_SOF_DEBUG_FORCE_IPC_POSITION + bool "SOF force to use IPC for position update on SKL+" + depends on SND_SOC_SOF_DEBUG + default y + help + This option force to handle stream position update IPCs and run pcm + elapse to inform ALSA about that, on platforms (e.g. Intel SKL+) that + with other approach (e.g. HDAC DPIB/posbuf) to elapse PCM. + On platforms (e.g. Intel SKL-) where position update IPC is the only + one choice, this setting won't impact anything. + if you are trying to debug pointer update with position IPCs or where + DPIB/posbuf is not ready, select "Y". + If unsure select "N". + source "sound/soc/sof/intel/Kconfig" source "sound/soc/sof/xtensa/Kconfig" From 09bb0065ed29da4d5ebcdbea50496f6974a48cb5 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 13 Sep 2018 18:35:59 +0800 Subject: [PATCH 192/298] ASoC: SOF: Intel: add no_ipc_position flag for position update Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda.c | 10 ++++++++++ sound/soc/sof/intel/hda.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 21803e11228c9c..ba492ac3756658 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -487,6 +487,16 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) sdev->hda = hdev; hdev->desc = chip; + /* + * use position update IPC if either it is forced + * or we don't have other choice + */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_FORCE_IPC_POSITION) + hdev->no_ipc_position = 0; +#else + hdev->no_ipc_position = sdev->ops->pcm_pointer ? 1 : 0; +#endif + /* set up HDA base */ ret = hda_init(sdev); if (ret < 0) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index d44b2550e87fcd..f51dde950cad82 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -358,6 +358,9 @@ struct sof_intel_hda_dev { /*trace */ struct hdac_ext_stream *dtrace_stream; + /* if position update IPC needed */ + bool no_ipc_position; + int irq; }; From d582fbecefe38b7f611329cc01faa314becec7de Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 10 Aug 2018 19:29:08 +0800 Subject: [PATCH 193/298] ASoC: SOF: add pointer callback for dsp_ops Signed-off-by: Keyon Jie --- sound/soc/sof/ops.h | 12 ++++++++++++ sound/soc/sof/sof-priv.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 6574707cd69b11..202900a5598ffe 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "sof-priv.h" @@ -265,6 +266,17 @@ snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, return 0; } +/* host stream pointer */ +static inline snd_pcm_uframes_t +snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sdev->ops && sdev->ops->pcm_pointer) + return sdev->ops->pcm_pointer(sdev, substream); + else + return 0; +} + int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 802bf1bc7812ef..6388ff91f190f4 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -138,6 +138,10 @@ struct snd_sof_dsp_ops { int (*pcm_trigger)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); + /* host stream pointer */ + snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); + /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, bool first_boot); From 5e643146711b7ec1293bee6d67720544951394eb Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 9 Aug 2018 18:11:01 +0800 Subject: [PATCH 194/298] ASoC: SOF: HDA: add pcm_pointer callback If use IPC poistion update, read position from IPC position. For Playback, Use DPIB register from HDA space which reflects the actual data transferred. For Capture, Use the position buffer for pointer, as DPIB is not accurate enough, its update may be completed earlier than the data written to DDR. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-pcm.c | 60 ++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda-stream.c | 4 +-- sound/soc/sof/intel/hda.h | 2 ++ sound/soc/sof/pcm.c | 4 +++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 45338f78df9d9e..af3ce223883394 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,65 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, return hda_dsp_stream_trigger(sdev, stream, cmd); } +snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_pcm *spcm = rtd->private; + snd_pcm_uframes_t pos = 0; + + if (!sdev->hda->no_ipc_position) { + /* read position from IPC position */ + pos = spcm->stream[substream->stream].posn.host_posn; + goto found; + } + + /* + * DPIB/posbuf position mode: + * For Playback, Use DPIB register from HDA space which + * reflects the actual data transferred. + * For Capture, Use the position buffer for pointer, as DPIB + * is not accurate enough, its update may be completed + * earlier than the data written to DDR. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + } else { + /* + * For capture stream, we need more workaround to fix the + * position incorrect issue: + * + * 1. Wait at least 20us before reading position buffer after + * the interrupt generated(IOC), to make sure position update + * happens on frame boundary i.e. 20.833uSec for 48KHz. + * 2. Perform a dummy Read to DPIB register to flush DMA + * position value. + * 3. Read the DMA Position from posbuf. Now the readback + * value should be >= period boundary. + */ + usleep_range(20, 21); + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + pos = snd_hdac_stream_get_pos_posbuf(hstream); + } + + if (pos >= hstream->bufsize) + pos = 0; + +found: + pos = bytes_to_frames(substream->runtime, pos); + + dev_vdbg(sdev->dev, "PCM: stream %d dir %d position %lu\n", + hstream->index, substream->stream, pos); + return pos; +} + int hda_dsp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 60e3844f72457b..5372d77cb9d032 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -507,8 +507,8 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) && s->opened) { sd_status = snd_hdac_stream_readb(s, SD_STS); - dev_dbg(bus->dev, "stream %d status 0x%x\n", - s->index, sd_status); + dev_vdbg(bus->dev, "stream %d status 0x%x\n", + s->index, sd_status); snd_hdac_stream_writeb(s, SD_STS, SD_INT_MASK); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index f51dde950cad82..1071649bd46f02 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -417,6 +417,8 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params); int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); +snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); /* * DSP Stream Operations. diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 76f801bc93df79..0efcd8bc09e5e3 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -342,6 +342,10 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) if (rtd->dai_link->no_pcm) return 0; + /* if have dsp ops pointer callback, use that directly */ + if (sdev->hda && sdev->ops->pcm_pointer) + return sdev->ops->pcm_pointer(sdev, substream); + /* read position from DSP */ host = bytes_to_frames(substream->runtime, spcm->stream[substream->stream].posn.host_posn); From 60d3d04353e86ff9598b4804db3e2e2e2dacf62b Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 4 Sep 2018 12:16:09 +0800 Subject: [PATCH 195/298] ASoC: SOF: PCM: change IPC stream params in platform hw_params(). Which will make it possible to retrieve stream_tag and change host_period_bytes in for HDA hs_params(), the latter will make disable position update IPC for HDA stream possbile. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-pcm.c | 11 +++++++++-- sound/soc/sof/intel/hda-stream.c | 8 +++++--- sound/soc/sof/intel/hda.h | 6 +++++- sound/soc/sof/ops.h | 6 ++++-- sound/soc/sof/pcm.c | 7 ++++--- sound/soc/sof/sof-priv.h | 3 ++- 6 files changed, 29 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index af3ce223883394..9d0215008d29ee 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -97,7 +97,8 @@ static inline u32 get_bits(struct snd_sof_dev *sdev, int sample_bits) int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params, + struct sof_ipc_stream_params *ipc_params) { struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream); @@ -125,7 +126,13 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* disable SPIB, to enable buffer wrap for stream */ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); - return hstream->stream_tag; + /* set host_period_bytes to 0 if no IPC position */ + if (sdev->hda && sdev->hda->no_ipc_position) + ipc_params->host_period_bytes = 0; + + ipc_params->stream_tag = hstream->stream_tag; + + return 0; } int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 5372d77cb9d032..89e4b32a62498d 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -497,6 +497,7 @@ irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) { struct hdac_bus *bus = (struct hdac_bus *)context; + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct hdac_stream *s; u32 status = snd_hdac_chip_readl(bus, INTSTS); u32 sd_status; @@ -516,9 +517,10 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) !s->running || (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) continue; -#ifdef USE_POS_BUF - snd_pcm_period_elapsed(s->substream); -#endif + + /* Inform ALSA only in case not do that with IPC */ + if (sof_hda->no_ipc_position) + snd_pcm_period_elapsed(s->substream); } } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1071649bd46f02..ba03dac0dbf6f8 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -364,6 +364,9 @@ struct sof_intel_hda_dev { int irq; }; +#define bus_to_sof_hda(bus) \ + container_of(bus, struct sof_intel_hda_dev, hbus.core) + #define SOF_STREAM_SD_OFFSET(s) \ (SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \ + SOF_HDA_ADSP_LOADER_BASE) @@ -414,7 +417,8 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params); + struct snd_pcm_hw_params *params, + struct sof_ipc_stream_params *ipc_params); int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 202900a5598ffe..91f30f5e23411e 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -247,10 +247,12 @@ snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, static inline int snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params, + struct sof_ipc_stream_params *ipc_params) { if (sdev->ops && sdev->ops->pcm_hw_params) - return sdev->ops->pcm_hw_params(sdev, substream, params); + return sdev->ops->pcm_hw_params(sdev, substream, + params, ipc_params); else return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 0efcd8bc09e5e3..e1b53594434a0c 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -142,9 +142,10 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, } /* firmware already configured host stream */ - pcm.params.stream_tag = snd_sof_pcm_platform_hw_params(sdev, - substream, - params); + ret = snd_sof_pcm_platform_hw_params(sdev, + substream, + params, + &pcm.params); dev_dbg(sdev->dev, "stream_tag %d", pcm.params.stream_tag); /* send IPC to the DSP */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6388ff91f190f4..ec559669a7f631 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -132,7 +132,8 @@ struct snd_sof_dsp_ops { /* host stream hw params */ int (*pcm_hw_params)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params); + struct snd_pcm_hw_params *params, + struct sof_ipc_stream_params *ipc_params); /* host stream trigger */ int (*pcm_trigger)(struct snd_sof_dev *sdev, From 1fb7cfaa798d82e53e3beaf68de99720fbc86696 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 10 Sep 2018 18:36:40 +0800 Subject: [PATCH 196/298] ASoC: SOF: Intel: correct BDL settings for DPIB/posbuf mode In DPIB/posbuf position pointer mode, we don't have position update IPC messages, we may need(depends on if no_irq is set) set different IOC flags for each BDL entry. Without IOC setting for BDL entries, irq mode won't work as there is no interrupt happens at each period finished. Last, we set BDL IOC at DPIB/pos mode only. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-stream.c | 122 ++++++++++++++++++++----------- sound/soc/sof/intel/hda.h | 4 +- 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 89e4b32a62498d..0572842c2876f4 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -36,57 +36,102 @@ #include "hda.h" /* - * set up Buffer Descriptor List (BDL) for host memory transfer - * BDL describes the location of the individual buffers and is little endian. + * set up one of BDL entries for a stream */ -int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, - struct snd_dma_buffer *dmab, - struct hdac_stream *stream, - struct sof_intel_dsp_bdl *bdl, int size, - struct snd_pcm_hw_params *params) +static int hda_setup_bdle(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct hdac_stream *stream, + struct sof_intel_dsp_bdl **bdlp, + int offset, int size, int ioc) { - int offset = 0; - int chunk = PAGE_SIZE, entry_size; - dma_addr_t addr; - - if (stream->substream && params) { - chunk = params_period_bytes(params); - dev_dbg(sdev->dev, "period_bytes:0x%x\n", chunk); - } + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_dsp_bdl *bdl = *bdlp; while (size > 0) { + dma_addr_t addr; + int chunk; + if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { dev_err(sdev->dev, "error: stream frags exceeded\n"); return -EINVAL; } addr = snd_sgbuf_get_addr(dmab, offset); - /* program BDL addr */ bdl->addr_l = lower_32_bits(addr); bdl->addr_h = upper_32_bits(addr); - - entry_size = size > chunk ? chunk : size; - /* program BDL size */ - bdl->size = snd_sgbuf_get_chunk_size(dmab, offset, entry_size); - - /* program the IOC to enable interrupt - * when the whole fragment is processed - */ - size -= entry_size; - if (size) - bdl->ioc = 0; - else - bdl->ioc = 1; + chunk = snd_sgbuf_get_chunk_size(dmab, offset, size); + /* one BDLE should not cross 4K boundary */ + if (bus->align_bdle_4k) { + u32 remain = 0x1000 - (offset & 0xfff); + if (chunk > remain) + chunk = remain; + } + bdl->size = cpu_to_le32(chunk); + /* only program IOC when the whole segment is processed */ + size -= chunk; + bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01); + bdl++; stream->frags++; - offset += bdl->size; + offset += chunk; + + dev_vdbg(sdev->dev, "bdl, frags:%d, chunk size:0x%x;\n", + stream->frags, chunk); + } - dev_vdbg(sdev->dev, "bdl, frags:%d, entry size:0x%x;\n", - stream->frags, entry_size); + *bdlp = bdl; + return offset; +} - bdl++; +/* + * set up Buffer Descriptor List (BDL) for host memory transfer + * BDL describes the location of the individual buffers and is little endian. + */ +int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct hdac_stream *stream) +{ + struct sof_intel_dsp_bdl *bdl; + int i, offset, period_bytes, periods; + int remain, ioc; + + period_bytes = stream->period_bytes; + dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes); + if (!period_bytes) + period_bytes = stream->bufsize; + + periods = stream->bufsize / period_bytes; + + dev_dbg(sdev->dev, "periods:%d\n", periods); + + remain = stream->bufsize % period_bytes; + if (remain) + periods++; + + /* program the initial BDL entries */ + bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; + offset = 0; + stream->frags = 0; + + /* + * set IOC if don't use position IPC + * and period_wakeup needed. + */ + ioc = sdev->hda->no_ipc_position ? + !stream->no_period_wakeup : 0; + + for (i = 0; i < periods; i++) { + if (i == periods - 1 && remain) + /* set the last small entry */ + offset = hda_setup_bdle(sdev, dmab, + stream, &bdl, offset, + remain, 0); + else + offset = hda_setup_bdle(sdev, dmab, + stream, &bdl, offset, + period_bytes, ioc); } return offset; @@ -310,7 +355,6 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, { struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *hstream = &stream->hstream; - struct sof_intel_dsp_bdl *bdl; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; u32 val, mask; @@ -323,12 +367,8 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, /* decouple host and link DMA */ mask = 0x1 << hstream->index; snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, -#ifndef CONFIG_SND_SOC_SOF_FORCE_LEGACY_HDA mask, mask); -#else - /* temporary using coupled mode */ - mask, 0); -#endif + if (!dmab) { dev_err(sdev->dev, "error: no dma buffer allocated!\n"); return -ENODEV; @@ -397,9 +437,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, hstream->frags = 0; - bdl = (struct sof_intel_dsp_bdl *)hstream->bdl.area; - ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream, bdl, - hstream->bufsize, params); + ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); if (ret < 0) { dev_err(sdev->dev, "error: set up of BDL failed\n"); return ret; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index ba03dac0dbf6f8..cf0781ebcb50fe 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -440,9 +440,7 @@ irqreturn_t hda_dsp_stream_interrupt(int irq, void *context); irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_stream *stream, - struct sof_intel_dsp_bdl *bdl, int size, - struct snd_pcm_hw_params *params); + struct hdac_stream *stream); struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction); From 3e80cfb1baa1650e5bd37df9b113181bd08f02fd Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 11 Sep 2018 14:09:42 +0800 Subject: [PATCH 197/298] ASoC: SOF: Intel: correct period settings for hdac_stream pcm_runtime is not ready yet at hw_params() stage, so we need to retrieve period settings from params here. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-pcm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 9d0215008d29ee..d5a09dcceeee4d 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -116,6 +116,10 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, hstream->format_val = rate | bits | (params_channels(params) - 1); hstream->bufsize = size; + hstream->period_bytes = params_period_size(params); + hstream->no_period_wakeup = + (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && + (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params); if (ret < 0) { From 540aea4a5c1efda7fcd4b9f791c29068f83a44cc Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 9 Aug 2018 18:12:38 +0800 Subject: [PATCH 198/298] ASoC: SOF: apl: add pcm_pointer callback to dsp_ops Signed-off-by: Keyon Jie --- sound/soc/sof/intel/apl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 3ba4ed5e8582c7..851abfb0b0780c 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -81,6 +81,7 @@ struct snd_sof_dsp_ops sof_apl_ops = { .pcm_close = hda_dsp_pcm_close, .pcm_hw_params = hda_dsp_pcm_hw_params, .pcm_trigger = hda_dsp_pcm_trigger, + .pcm_pointer = hda_dsp_pcm_pointer, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, From a224ab20f74b15a18812755ff62271e0f1c69925 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 14 Sep 2018 16:31:40 +0800 Subject: [PATCH 199/298] ASoC: SOF: only elapse/inform ALSA for irq mode Signed-off-by: Keyon Jie --- sound/soc/sof/ipc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 7aa0240ee8491d..4ee63ce4dd2b11 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -534,7 +534,10 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) posn.host_posn, posn.dai_posn, posn.wallclock); memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); - snd_pcm_period_elapsed(spcm->stream[direction].substream); + + /* only inform ALSA for period_wakeup mode */ + if (!spcm->stream[direction].substream->runtime->no_period_wakeup) + snd_pcm_period_elapsed(spcm->stream[direction].substream); } /* DSP notifies host of an XRUN within FW */ From 50792bc2c90ea9300116a8296492da6c26ef12e7 Mon Sep 17 00:00:00 2001 From: Zhu Yingjiang Date: Fri, 14 Sep 2018 15:19:20 +0800 Subject: [PATCH 200/298] ASoC: SOF: skl enable the skl/kbl ops, instead of using apl_ops v2 Signed-off-by: Zhu Yingjiang --- sound/soc/sof/sof-pci-dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index d2ec640306f305..fcef2bb1318215 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -148,10 +148,10 @@ static const struct sof_ops_table mach_ops[] = { {&cnl_desc, &sof_cnl_ops}, #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) - {&skl_desc, &sof_apl_ops}, + {&skl_desc, &sof_skl_ops}, #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) - {&kbl_desc, &sof_apl_ops}, + {&kbl_desc, &sof_skl_ops}, #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) {&icl_desc, &sof_cnl_ops}, From a84d54b2109d23095f06ee3e51c333c7affbf810 Mon Sep 17 00:00:00 2001 From: Zhu Yingjiang Date: Mon, 10 Sep 2018 17:06:29 +0800 Subject: [PATCH 201/298] ASoC:SOF:skl:enable the core and get the ROM init 1. enable the skl core 2. get the ROM int Signed-off-by: Zhu Yingjiang --- sound/soc/sof/intel/hda-dsp.c | 15 +++++ sound/soc/sof/intel/hda-loader.c | 102 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 4 ++ sound/soc/sof/intel/skl.c | 2 +- 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 640bf0692413c0..d8400d85c9ae39 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -208,6 +208,21 @@ bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, return is_enable; } +int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + int ret; + + /* power up */ + ret = hda_dsp_core_power_up(sdev, core_mask); + if (ret < 0) { + dev_err(sdev->dev, "dsp core power up failed: core_mask %x\n", + core_mask); + return ret; + } + + return hda_dsp_core_run(sdev, core_mask); +} + int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) { diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 796d772a6a8d30..38229913b51aad 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -372,3 +372,105 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); return ret; } + +static int cl_dsp_init_skl(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + int ret; + + /* check if the core is already enabled, if yes, reset and make it run, + * if not, powerdown and enable it again. + */ + if (hda_dsp_core_is_enabled(sdev, HDA_DSP_CORE_MASK(0))) { + /* if enabled, reset it, and run the core. */ + ret = hda_dsp_core_stall_reset(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) + goto err; + + ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core start failed %d\n", + ret); + goto err; + } + } else { + /* if not enabled, power down it first and then powerup and run + * the core. + */ + ret = hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "dsp core0 disable fail: %d\n", ret); + goto err; + } + ret = hda_dsp_enable_core(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "dsp core0 enable fail: %d\n", ret); + goto err; + } + } + + /* enable the interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + + /* enable IPC DONE interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + + /* enable IPC BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_BUSY, + HDA_DSP_REG_HIPCCTL_BUSY); + + /* polling the ROM init status information. */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS_SKL, + HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_INIT, + HDA_DSP_INIT_TIMEOUT); + if (ret < 0) + goto err; + + return ret; + +err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); + return ret; +} + +int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) +{ + int ret; + + ret = cl_dsp_init_skl(sdev); + + /* retry enabling core and ROM load. seemed to help */ + if (ret < 0) { + ret = cl_dsp_init_skl(sdev); + if (ret < 0) { + dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); + dev_err(sdev->dev, "Core En/ROM load fail:%d\n", ret); + goto irq_err; + } + } + + /* init for booting wait */ + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + return ret; + +irq_err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + return ret; +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index cf0781ebcb50fe..02499bc5dce27f 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -322,6 +322,8 @@ #define SOF_SKL_NUM_DAIS 8 #endif +#define HDA_DSP_SRAM_REG_ROM_STATUS_SKL 0x8000 + struct sof_intel_dsp_bdl { u32 addr_l; u32 addr_h; @@ -383,6 +385,7 @@ int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask); @@ -473,6 +476,7 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); */ int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, bool first_boot); int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); +int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev); /* * HDA Controller Operations. diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index ab7cd2b689cba9..43b0bc169438fe 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -86,7 +86,7 @@ struct snd_sof_dsp_ops sof_skl_ops = { .load_firmware = hda_dsp_cl_load_fw, /* firmware run */ - .run = hda_dsp_cl_boot_firmware, + .run = hda_dsp_cl_boot_firmware_skl, /* trace callback */ .trace_init = hda_dsp_trace_init, From c3ba91f782707bd3e51c46f37300bd7b3cfe8333 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sun, 16 Sep 2018 20:31:40 -0700 Subject: [PATCH 202/298] ASoC: SOF: enable S2idle entry on SKL+ platforms This patch adds the missing register write sequences to disable/enable the hda controller during suspend/resume. This facilitates the entry of S2idle on SKL+ platforms. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dsp.c | 74 ++++++++++++++++++++++++++++++++++- sound/soc/sof/intel/hda.h | 1 + 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index d8400d85c9ae39..e8291ad64cfd36 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -257,15 +258,84 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) { const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + struct hdac_bus *bus = sof_to_bus(sdev); + int ret = 0; + + /* power down all hda link */ + snd_hdac_ext_bus_link_power_down_all(bus); /* power down DSP */ - return hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + ret = hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to power down core during suspend\n"); + return ret; + } + + /* disable ppcap interrupt */ + snd_hdac_ext_bus_ppcap_int_enable(bus, false); + snd_hdac_ext_bus_ppcap_enable(bus, false); + + /* disable hda bus irw and i/o */ + snd_hdac_bus_stop_chip(bus); + + /* disable LP retention mode */ + snd_sof_pci_update_bits(sdev, PCI_TCSEL, + PCI_CGCTL_LSRMD_MASK, PCI_CGCTL_LSRMD_MASK); + + return 0; } int hda_dsp_resume(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_link *hlink = NULL; + int ret; + + /* + * clear TCSEL to clear playback on some HD Audio + * codecs. PCI TCSEL is defined in the Intel manuals. + */ + snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); + + /* reset and start hda controller */ + ret = hda_dsp_ctrl_init_chip(sdev, true); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to start controller after resume\n"); + return ret; + } + + hda_dsp_ctrl_misc_clock_gating(sdev, false); + + /* Reset stream-to-link mapping */ + list_for_each_entry(hlink, &bus->hlink_list, list) + bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); + + hda_dsp_ctrl_misc_clock_gating(sdev, true); + + /* enable ppcap interrupt */ + snd_hdac_ext_bus_ppcap_enable(bus, true); + snd_hdac_ext_bus_ppcap_int_enable(bus, true); /* power up the DSP */ - return hda_dsp_core_power_up(sdev, chip->cores_mask); + ret = hda_dsp_core_power_up(sdev, chip->cores_mask); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to power up core after resume\n"); + return ret; + } + + /* turn off the links that were off before suspend */ + list_for_each_entry(hlink, &bus->hlink_list, list) { + if (!hlink->ref_count) + snd_hdac_ext_bus_link_power_down(hlink); + } + + /* check dma status and clean up CORB/RIRB buffers */ + if (!bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); + + return 0; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 02499bc5dce27f..c103bc66e8b806 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -19,6 +19,7 @@ /* PCI_CGCTL bits */ #define PCI_CGCTL_MISCBDCGE_MASK BIT(6) +#define PCI_CGCTL_LSRMD_MASK BIT(4) /* Legacy HDA registers and bits used - widths are variable */ #define SOF_HDA_GCAP 0x0 From 71e321cf97ad8b91681f2fe87428b41ca75de6fe Mon Sep 17 00:00:00 2001 From: Bard liao Date: Tue, 18 Sep 2018 09:12:55 +0000 Subject: [PATCH 203/298] ASoC: SOF: debug: fix potential memory leak issue free dynamic allocated buf betore return. Signed-off-by: Bard liao --- sound/soc/sof/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 67c195573d795c..793d6ac3cf0fe5 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -63,6 +63,7 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, if (err < 0) { dev_err(sdev->dev, "error: debugFS failed to resume %d\n", err); + kfree(buf); return err; } From 0ec5600afd68908bc4be40ef998c27cf61db1ce8 Mon Sep 17 00:00:00 2001 From: Bard liao Date: Tue, 18 Sep 2018 09:43:10 +0000 Subject: [PATCH 204/298] ASoC: SOF: intel: use devm_kzalloc use devm_kzalloc to prevent memory leak. Signed-off-by: Bard liao --- sound/soc/sof/intel/hda-stream.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 0572842c2876f4..c18b49a5160224 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -620,7 +620,7 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) /* create capture streams */ for (i = 0; i < num_capture; i++) { - stream = kzalloc(sizeof(*stream), GFP_KERNEL); + stream = devm_kzalloc(sdev->dev, sizeof(*stream), GFP_KERNEL); if (!stream) return -ENOMEM; @@ -666,7 +666,6 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) HDA_DSP_BDL_SIZE, &hstream->bdl); if (ret < 0) { dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); - kfree(stream); return -ENOMEM; } hstream->posbuf = (__le32 *)(bus->posbuf.area + @@ -678,7 +677,7 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) /* create playback streams */ for (i = num_capture; i < num_total; i++) { - stream = kzalloc(sizeof(*stream), GFP_KERNEL); + stream = devm_kzalloc(sdev->dev, sizeof(*stream), GFP_KERNEL); if (!stream) return -ENOMEM; @@ -725,7 +724,6 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) HDA_DSP_BDL_SIZE, &hstream->bdl); if (ret < 0) { dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); - kfree(stream); return -ENOMEM; } From 801d1765db4eec9acaaeb58edc20c64c91c2f20a Mon Sep 17 00:00:00 2001 From: Bard liao Date: Tue, 18 Sep 2018 09:55:27 +0000 Subject: [PATCH 205/298] ASoC: SOF: use devm_kzalloc use devm_kzalloc to prevent memory leak. Signed-off-by: Bard liao --- sound/soc/sof/topology.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index fab12dc3d1de95..4fbeb4c3525308 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2013,15 +2013,14 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, int ret = 0; /* allocate memory for sroute and connect */ - sroute = kzalloc(sizeof(*sroute), GFP_KERNEL); + sroute = devm_kzalloc(sdev->dev, sizeof(*sroute), GFP_KERNEL); if (!sroute) return -ENOMEM; sroute->sdev = sdev; - connect = kzalloc(sizeof(*connect), GFP_KERNEL); + connect = devm_kzalloc(sdev->dev, sizeof(*connect), GFP_KERNEL); if (!connect) { - kfree(sroute); return -ENOMEM; } @@ -2134,11 +2133,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, list_add(&sroute->list, &sdev->route_list); } - return ret; - err: - kfree(connect); - kfree(sroute); return ret; } From bc51a6f6a53ffad10ab7a4d8054e55bc47d59e0d Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Tue, 28 Aug 2018 13:43:11 +0800 Subject: [PATCH 206/298] ASoC:topology:bug fix:check return value avoid oops. check the return value to free the kcontrols instance to avoid oops caused by the pointer dereference. Signed-off-by: Wu Zhigang [guennadi.liakhovetski@intel.com add handling of .autodisable=1 cases] --- sound/soc/soc-dapm.c | 99 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7fc7c7374ff061..df7f64b12e6835 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2410,12 +2410,11 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) kfree(path); } -void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) +static void snd_soc_dapm_free_widget_data(struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_path *p, *next_p; enum snd_soc_dapm_direction dir; - list_del(&w->list); /* * remove source and sink paths associated to this widget. * While removing the path, remove reference to it from both @@ -2428,6 +2427,12 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) kfree(w->kcontrols); kfree_const(w->name); +} + +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) +{ + list_del(&w->list); + snd_soc_dapm_free_widget_data(w); kfree(w); } @@ -3046,11 +3051,16 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); */ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w, *last; unsigned int val; + int ret = 0; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + /* + * widgets with the snd_soc_dapm_kcontrol ID and .num_controls = 0 can + * be appended to the list while scanning it, this is safe. + */ list_for_each_entry(w, &card->widgets, list) { if (w->new) @@ -3061,8 +3071,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) sizeof(struct snd_kcontrol *), GFP_KERNEL); if (!w->kcontrols) { - mutex_unlock(&card->dapm_mutex); - return -ENOMEM; + ret = -ENOMEM; + goto out_free; } } @@ -3070,24 +3080,30 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: - dapm_new_mixer(w); + ret = dapm_new_mixer(w); break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: - dapm_new_mux(w); + ret = dapm_new_mux(w); break; case snd_soc_dapm_pga: case snd_soc_dapm_effect: case snd_soc_dapm_out_drv: - dapm_new_pga(w); + ret = dapm_new_pga(w); break; case snd_soc_dapm_dai_link: - dapm_new_dai_link(w); + ret = dapm_new_dai_link(w); break; default: break; } + if (ret < 0) { + kfree(w->kcontrols); + w->kcontrols = NULL; + goto out_free; + } + /* Read the initial power state from the device */ if (w->reg >= 0) { soc_dapm_read(w->dapm, w->reg, &val); @@ -3096,6 +3112,11 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) if (val == w->on_val) w->power = 1; } + } + + list_for_each_entry(w, &card->widgets, list) { + if (w->new) + continue; w->new = 1; @@ -3106,6 +3127,66 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); mutex_unlock(&card->dapm_mutex); return 0; + +out_free: + last = w; + + /* + * If any new widgets have been created above for .autodisable = 1 + * controls, they are also on this list, but at its very end. We're + * processing an error case, so the loop above was interrupted before + * reaching dynamically added widgets, since the latter cannot fail - + * their .num_controls = 0, so allocation cannot fail, and their type is + * snd_soc_dapm_kcontrol, those cannot fail either. Therefore "last" + * points to a widget before the first dynamic one. + */ + list_for_each_entry(w, &card->widgets, list) { + unsigned int i; + + if (w->new) + continue; + + if (w == last) + break; + + switch (w->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + for (i = 0; i < w->num_kcontrols; i++) { + struct snd_kcontrol *kcontrol = w->kcontrols[i]; + struct dapm_kcontrol_data *data = + kcontrol->private_data; + struct soc_mixer_control *mc = + (struct soc_mixer_control *) + kcontrol->private_value; + + if (mc->autodisable) + snd_soc_dapm_free_widget(data->widget); + } + break; + case snd_soc_dapm_demux: + case snd_soc_dapm_mux: + for (i = 0; i < w->num_kcontrols; i++) { + struct snd_kcontrol *kcontrol = w->kcontrols[i]; + struct dapm_kcontrol_data *data = + kcontrol->private_data; + struct soc_enum *e = (struct soc_enum *) + kcontrol->private_value; + + if (e->autodisable) + snd_soc_dapm_free_widget(data->widget); + } + break; + default: + break; + } + + snd_soc_dapm_free_widget_data(w); + } + + mutex_unlock(&card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); From 9544ae530fc7736c84153e1709fe0d1710fe403c Mon Sep 17 00:00:00 2001 From: Bard liao Date: Wed, 19 Sep 2018 14:16:28 +0000 Subject: [PATCH 207/298] ASoC: SOF: intel: use devm_kfree stream is allocated by devm_kzalloc and it should be freed by devm_kfree. Signed-off-by: Bard liao --- sound/soc/sof/intel/hda-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index c18b49a5160224..cfdc8a629affbe 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -754,7 +754,7 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev) snd_dma_free_pages(&s->bdl); list_del(&s->list); stream = stream_to_hdac_ext_stream(s); - kfree(stream); + devm_kfree(sdev->dev, stream); } } From 108d9cf3daec51bfd01211a7e9585013131f612a Mon Sep 17 00:00:00 2001 From: Bard liao Date: Wed, 19 Sep 2018 14:19:16 +0000 Subject: [PATCH 208/298] ASoC: SOF: use devm_kfree sroute is allocated by devm_kzalloc and it should be freed by devm_kfree. Signed-off-by: Bard liao --- sound/soc/sof/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 4fbeb4c3525308..83090ae0353f2a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2304,7 +2304,7 @@ void snd_sof_free_topology(struct snd_sof_dev *sdev) /* free sroute and its private data */ kfree(sroute->private); - kfree(sroute); + devm_kfree(sdev->dev, sroute); } ret = snd_soc_tplg_component_remove(sdev->component, From 7128681746b90d4c2acad7a0882e8daa9749afe3 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 19 Sep 2018 23:08:20 -0700 Subject: [PATCH 209/298] ASoC: SOF: separate system sleep and runtime PM calls for SKL+ platforms Previously, identical suspend/resume sequences for runtime PM and system suspend/resume caused a significant delay in resume times after system suspend. This patch separates the calls for SKL+ platforms and introduces the display power ON step during resume from system idle that fixes the long resume time issue. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/apl.c | 6 ++++-- sound/soc/sof/intel/hda-dsp.c | 37 +++++++++++++++++++++++++++++++++-- sound/soc/sof/intel/hda.h | 2 ++ sound/soc/sof/ops.h | 17 ++++++++++++++++ sound/soc/sof/pm.c | 22 ++++++++++++++------- sound/soc/sof/sof-priv.h | 2 ++ 6 files changed, 75 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 851abfb0b0780c..82edc77eeac1f7 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -99,7 +99,9 @@ struct snd_sof_dsp_ops sof_apl_ops = { .num_drv = SOF_SKL_NUM_DAIS, /* PM */ - .suspend = hda_dsp_suspend, - .resume = hda_dsp_resume, + .suspend = hda_dsp_suspend, + .resume = hda_dsp_resume, + .runtime_suspend = hda_dsp_runtime_suspend, + .runtime_resume = hda_dsp_runtime_resume, }; EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index e8291ad64cfd36..bb16065b58807e 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -255,7 +256,7 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, return ret; } -int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) +static int hda_suspend(struct snd_sof_dev *sdev, int state) { const struct sof_intel_dsp_desc *chip = sdev->hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); @@ -286,7 +287,7 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) return 0; } -int hda_dsp_resume(struct snd_sof_dev *sdev) +static int hda_resume(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip = sdev->hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); @@ -339,3 +340,35 @@ int hda_dsp_resume(struct snd_sof_dev *sdev) return 0; } + +int hda_dsp_resume(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + int ret; + + /* turn display power on */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, true); + if (ret < 0) { + dev_err(bus->dev, "Cannot turn on display power on i915 after resume\n"); + return ret; + } + } + + return hda_resume(sdev); +} + +int hda_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + return hda_resume(sdev); +} + +int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state) +{ + return hda_suspend(sdev, state); +} + +int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) +{ + return hda_suspend(sdev, state); +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index c103bc66e8b806..cb03bbc5dd1c84 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -394,6 +394,8 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_suspend(struct snd_sof_dev *sdev, int state); int hda_dsp_resume(struct snd_sof_dev *sdev); +int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state); +int hda_dsp_runtime_resume(struct snd_sof_dev *sdev); void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); /* diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 91f30f5e23411e..f033bb6f06d42c 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -78,6 +78,23 @@ static inline int snd_sof_dsp_suspend(struct snd_sof_dev *sdev, int state) return 0; } +static inline int snd_sof_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + if (sdev->ops->runtime_resume) + return sdev->ops->runtime_resume(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_runtime_suspend(struct snd_sof_dev *sdev, + int state) +{ + if (sdev->ops->runtime_suspend) + return sdev->ops->runtime_suspend(sdev, state); + else + return 0; +} + static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) { if (sdev->ops->set_clk) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8bef8d67ca991c..62e8f676a8886f 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -21,6 +21,8 @@ #include "ops.h" #include "sof-priv.h" +#define RUNTIME_RESUME 1 + static int sof_restore_pipelines(struct snd_sof_dev *sdev) { struct snd_sof_widget *swidget = NULL; @@ -229,18 +231,24 @@ static void sof_suspend_streams(struct snd_sof_dev *sdev) } } -static int sof_resume(struct device *dev) +static int sof_resume(struct device *dev, int runtime_resume) { struct sof_platform_priv *priv = dev_get_drvdata(dev); struct snd_sof_dev *sdev = dev_get_drvdata(&priv->pdev_pcm->dev); int ret = 0; - /* do nothing if dsp resume callback is not set */ - if (!sdev->ops->resume) + /* do nothing if dsp resume callbacks are not set */ + if (!sdev->ops->resume || !sdev->ops->runtime_resume) return 0; - /* power up DSP */ - ret = snd_sof_dsp_resume(sdev); + /* + * if the runtime_resume flag is set, call the runtime_resume routine + * or else call the system resume routine + */ + if (runtime_resume) + ret = snd_sof_dsp_runtime_resume(sdev); + else + ret = snd_sof_dsp_resume(sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to power up DSP after resume\n"); @@ -342,13 +350,13 @@ EXPORT_SYMBOL(snd_sof_runtime_suspend); int snd_sof_runtime_resume(struct device *dev) { - return sof_resume(dev); + return sof_resume(dev, RUNTIME_RESUME); } EXPORT_SYMBOL(snd_sof_runtime_resume); int snd_sof_resume(struct device *dev) { - return sof_resume(dev); + return sof_resume(dev, 0); } EXPORT_SYMBOL(snd_sof_resume); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ec559669a7f631..ee1e423ad50c5c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -81,6 +81,8 @@ struct snd_sof_dsp_ops { /* DSP PM */ int (*suspend)(struct snd_sof_dev *sof_dev, int state); int (*resume)(struct snd_sof_dev *sof_dev); + int (*runtime_suspend)(struct snd_sof_dev *sof_dev, int state); + int (*runtime_resume)(struct snd_sof_dev *sof_dev); /* DSP clocking */ int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); From 556f44a8526aac61e18e3302d71d35e4ba308b5c Mon Sep 17 00:00:00 2001 From: Zhu Yingjiang Date: Wed, 19 Sep 2018 18:31:25 +0800 Subject: [PATCH 210/298] ASoC:SOF:skl enable the ioc enabled the ioc, and fix BAR bug, the firmware status now can get Signed-off-by: Zhu Yingjiang --- sound/soc/sof/intel/hda-dsp.c | 13 +++++++++++++ sound/soc/sof/intel/hda-loader.c | 6 +++--- sound/soc/sof/intel/hda.h | 2 ++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index bb16065b58807e..6eea0a1d0ac08c 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -210,6 +210,19 @@ bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, return is_enable; } +int hda_dsp_core_stall_reset_skl(struct snd_sof_dev *sdev, + unsigned int core_mask) +{ + /* stall core */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + + /* set reset state */ + return hda_dsp_core_reset_enter(sdev, core_mask); +} + int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) { int ret; diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 38229913b51aad..f12c49958fe942 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -468,9 +468,9 @@ int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) irq_err: hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); - /* disable DSP */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - SOF_HDA_PPCTL_GPROCEN, 0); + /* power down DSP */ + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); return ret; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index cb03bbc5dd1c84..5e27345022a86d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -384,6 +384,8 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_stall_reset_skl(struct snd_sof_dev *sdev, + unsigned int core_mask); int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask); From 0064eda75f3e4e040cb8a07a2d23069fe5d7ecc8 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 19 Sep 2018 18:26:52 +0800 Subject: [PATCH 211/298] ASoC: SOF: Intel: fix suspend/resume for non-HDA mode. For non-HDA mode, we don't handle the pp and init/stop chip related, here correct it. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-dsp.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 6eea0a1d0ac08c..f0cd43efdaa4c6 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -275,8 +275,10 @@ static int hda_suspend(struct snd_sof_dev *sdev, int state) struct hdac_bus *bus = sof_to_bus(sdev); int ret = 0; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* power down all hda link */ snd_hdac_ext_bus_link_power_down_all(bus); +#endif /* power down DSP */ ret = hda_dsp_core_reset_power_down(sdev, chip->cores_mask); @@ -286,18 +288,25 @@ static int hda_suspend(struct snd_sof_dev *sdev, int state) return ret; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(bus, false); snd_hdac_ext_bus_ppcap_enable(bus, false); /* disable hda bus irw and i/o */ snd_hdac_bus_stop_chip(bus); +#endif /* disable LP retention mode */ snd_sof_pci_update_bits(sdev, PCI_TCSEL, PCI_CGCTL_LSRMD_MASK, PCI_CGCTL_LSRMD_MASK); - return 0; + /* reset HDA controller */ + ret = hda_dsp_ctrl_link_reset(sdev); + if (ret < 0) + dev_err(sdev->dev, "error: failed to reset HDA controller\n"); + + return ret; } static int hda_resume(struct snd_sof_dev *sdev) @@ -313,6 +322,7 @@ static int hda_resume(struct snd_sof_dev *sdev) */ snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* reset and start hda controller */ ret = hda_dsp_ctrl_init_chip(sdev, true); if (ret < 0) { @@ -332,6 +342,7 @@ static int hda_resume(struct snd_sof_dev *sdev) /* enable ppcap interrupt */ snd_hdac_ext_bus_ppcap_enable(bus, true); snd_hdac_ext_bus_ppcap_int_enable(bus, true); +#endif /* power up the DSP */ ret = hda_dsp_core_power_up(sdev, chip->cores_mask); @@ -341,6 +352,7 @@ static int hda_resume(struct snd_sof_dev *sdev) return ret; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* turn off the links that were off before suspend */ list_for_each_entry(hlink, &bus->hlink_list, list) { if (!hlink->ref_count) @@ -350,6 +362,7 @@ static int hda_resume(struct snd_sof_dev *sdev) /* check dma status and clean up CORB/RIRB buffers */ if (!bus->cmd_dma_state) snd_hdac_bus_stop_cmd_io(bus); +#endif return 0; } From 1b07494c5796f7a8a4cb7f8dcc24dc9a4b33cb22 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 20 Sep 2018 15:42:14 +0200 Subject: [PATCH 212/298] soc: fix a use after free case Unloading ASoC modules as used by the SOF driver leads to an object being used after it's been freed. Fix this be clearing a reference to it and making sure to check for its presence. This fixes issue #144. Signed-off-by: Guennadi Liakhovetski --- sound/soc/soc-core.c | 6 +++--- sound/soc/soc-topology.c | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 49e82131141f0a..fdbef0d89d3e62 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -946,9 +946,9 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order) { int err; - if (dai && dai->probed && - dai->driver->remove_order == order) { - if (dai->driver->remove) { + if (dai && dai->probed) { + if (dai->driver && dai->driver->remove_order == order && + dai->driver->remove) { err = dai->driver->remove(dai); if (err < 0) dev_err(dai->dev, diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 00ab616f1bfc67..fa72c892e461a1 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -508,6 +508,7 @@ static void remove_dai(struct snd_soc_component *comp, { struct snd_soc_dai_driver *dai_drv = container_of(dobj, struct snd_soc_dai_driver, dobj); + struct snd_soc_dai *dai; if (pass != SOC_TPLG_PASS_PCM_DAI) return; @@ -515,6 +516,10 @@ static void remove_dai(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->dai_unload) dobj->ops->dai_unload(comp, dobj); + list_for_each_entry(dai, &comp->dai_list, list) + if (dai->driver == dai_drv) + dai->driver = NULL; + kfree(dai_drv->name); list_del(&dobj->list); kfree(dai_drv); From 53c732e4cbcf8f35474796672089ecb2a660ecda Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Sun, 9 Sep 2018 19:54:52 +0300 Subject: [PATCH 213/298] ASOC: SOF: topology: introduce effect type for widgets Introduce effect type enumeration and vendor type to differentiate between effects. We can use these types to build multieffect pipelines in the topology and parse those in the sof driver to send the correct ipc messages to DSP firmware. Signed-off-by: Jaska Uimonen --- include/uapi/sound/sof-ipc.h | 12 ++++++++++ include/uapi/sound/sof-topology.h | 2 ++ sound/soc/sof/topology.c | 38 +++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 29cf46da5f3103..9532674d521bbd 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -732,6 +732,18 @@ struct sof_ipc_comp_eq_iir { struct sof_ipc_comp_config config; } __attribute__((packed)); +/** \brief Types of EFFECT */ +enum sof_ipc_effect_type { + SOF_EFFECT_INTEL_NONE = 0, /**< None */ + SOF_EFFECT_INTEL_EQFIR, /**< Intel FIR */ + SOF_EFFECT_INTEL_EQIIR, /**< Intel IIR */ +}; + +/* general purpose EFFECT configuration */ +struct sof_ipc_comp_effect { + enum sof_ipc_effect_type type; +} __attribute__((packed)); + /* frees components, buffers and pipelines * SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE */ diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 53d92c7b714d93..3c4ae99c8b5e0a 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -91,4 +91,6 @@ #define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705 #define SOF_TKN_INTEL_DMIC_PDM_SKEW 706 +#define SOF_TKN_EFFECT_TYPE 900 + #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 83090ae0353f2a..fcfc8c69447ec9 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -233,6 +233,28 @@ static enum sof_ipc_frame find_format(const char *name) return SOF_IPC_FRAME_S32_LE; } +struct sof_effect_types { + const char *name; + enum sof_ipc_effect_type type; +}; + +static const struct sof_effect_types sof_effects[] = { + {"EQFIR", SOF_EFFECT_INTEL_EQFIR}, + {"EQIIR", SOF_EFFECT_INTEL_EQIIR}, +}; + +static enum sof_ipc_effect_type find_effect(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_effects); i++) { + if (strcmp(name, sof_effects[i].name) == 0) + return sof_effects[i].type; + } + + return SOF_EFFECT_INTEL_NONE; +} + /* * Standard Kcontrols. */ @@ -342,6 +364,15 @@ static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) return 0; } +static int get_token_effect_type(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_effect(velem->string); + return 0; +} + /* Buffers */ static const struct sof_topology_token buffer_tokens[] = { {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -415,6 +446,13 @@ static const struct sof_topology_token eq_fir_tokens[] = { static const struct sof_topology_token eq_iir_tokens[] = { }; +/* EFFECT */ +static const struct sof_topology_token effect_tokens[] = { + {SOF_TKN_EFFECT_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, + get_token_effect_type, + offsetof(struct sof_ipc_comp_effect, type), 0}, +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, From 9d17c65092379516df7de43ec3e7ea64d0c1548e Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Sun, 9 Sep 2018 20:01:30 +0300 Subject: [PATCH 214/298] ASOC: SOF: topology: change eq to use effect type Parse iir and fir eq from widget data field specifying the effect type. Parse also optional initial eq params sent in the eq control's private data. Signed-off-by: Jaska Uimonen --- include/uapi/sound/sof-ipc.h | 4 + sound/soc/sof/topology.c | 243 ++++++++++++++++++++++++++++------- 2 files changed, 204 insertions(+), 43 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 9532674d521bbd..48e6bc867bacf9 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -724,12 +724,16 @@ struct sof_ipc_comp_tone { struct sof_ipc_comp_eq_fir { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; + int32_t size; + unsigned char data[0]; } __attribute__((packed)); /* IIR equalizer component */ struct sof_ipc_comp_eq_iir { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; + int32_t size; + unsigned char data[0]; } __attribute__((packed)); /** \brief Types of EFFECT */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index fcfc8c69447ec9..976b01c3e8fae0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -298,6 +298,8 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_ctrl_data *cdata; + struct snd_soc_tplg_bytes_control *control = + (struct snd_soc_tplg_bytes_control *)hdr; /* init the get/put bytes data */ scontrol->size = SOF_IPC_MSG_MAX_SIZE; @@ -312,6 +314,21 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); + if (le32_to_cpu(control->priv.size) > SOF_IPC_MSG_MAX_SIZE) { + dev_warn(sdev->dev, "bytes priv data size %d too big\n", + control->priv.size); + return -EINVAL; + } + + if (le32_to_cpu(control->priv.size) > 0) { + memcpy(cdata->data->data, control->priv.data, + le32_to_cpu(control->priv.size)); + cdata->data->size = control->priv.size; + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->comp_abi = SOF_ABI_VERSION; + } + return 0; } @@ -436,16 +453,6 @@ static const struct sof_topology_token src_tokens[] = { static const struct sof_topology_token tone_tokens[] = { }; -/* EQ FIR */ -/* -static const struct sof_topology_token eq_fir_tokens[] = { -}; -*/ - -/* EQ IIR */ -static const struct sof_topology_token eq_iir_tokens[] = { -}; - /* EFFECT */ static const struct sof_topology_token effect_tokens[] = { {SOF_TKN_EFFECT_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, @@ -1383,67 +1390,217 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, kfree(tone); return ret; } -/* - * Effect Topology. Only IIR equalizer is supported at this moment. - * TODO: Need to add also FIR support and have a way to add other - * effects and enhancements. - */ -static int sof_widget_load_effect(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) +static int sof_effect_fir_load(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) + { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_eq_iir *eq; + struct snd_sof_control *scontrol = NULL; + struct snd_soc_dapm_widget *widget = swidget->widget; + const struct snd_kcontrol_new *kc = NULL; + struct soc_bytes_ext *sbe; + struct sof_abi_hdr *pdata = NULL; + struct sof_ipc_comp_eq_fir *fir; + size_t ipc_size = 0, fir_data_size = 0; int ret; - eq = kzalloc(sizeof(*eq), GFP_KERNEL); - if (!eq) + /* get possible eq controls */ + kc = &widget->kcontrol_news[0]; + if (kc) { + sbe = (struct soc_bytes_ext *)kc->private_value; + scontrol = sbe->dobj.private; + } + + /* + * Check if there's eq parameters in control's private member and set + * data size accordingly. If there's no parameters eq will use defaults + * in firmware (which in this case is passthrough). + */ + if (scontrol && scontrol->cmd == SOF_CTRL_CMD_BINARY) { + pdata = scontrol->control_data->data; + if (pdata->size > 0 && pdata->magic == SOF_ABI_MAGIC) + fir_data_size = pdata->size; + } + + ipc_size = sizeof(struct sof_ipc_comp_eq_fir) + + le32_to_cpu(private->size) + + fir_data_size; + + fir = kzalloc(ipc_size, GFP_KERNEL); + if (!fir) return -ENOMEM; - /* configure IIR EQ IPC message */ - eq->comp.hdr.size = sizeof(*eq); - eq->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - eq->comp.id = swidget->comp_id; - eq->comp.pipeline_id = index; + /* configure fir IPC message */ + fir->comp.hdr.size = ipc_size; + fir->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + fir->comp.id = swidget->comp_id; + fir->comp.type = SOF_COMP_EQ_FIR; + fir->comp.pipeline_id = index; - eq->comp.type = SOF_COMP_EQ_IIR; - ret = sof_parse_tokens(scomp, eq, eq_iir_tokens, - ARRAY_SIZE(eq_iir_tokens), private->array, + ret = sof_parse_tokens(scomp, &fir->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); - if (ret) { - dev_err(sdev->dev, "error: parse EQ tokens failed %d\n", - private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse fir.cfg tokens failed %d\n", + le32_to_cpu(private->size)); goto err; } - ret = sof_parse_tokens(scomp, &eq->config, comp_tokens, + sof_dbg_comp_config(scomp, &fir->config); + + /* we have a private data found in control, so copy it */ + if (fir_data_size > 0) { + memcpy(&fir->data, pdata->data, pdata->size); + fir->size = fir_data_size; + } + + swidget->private = (void *)fir; + + ret = sof_ipc_tx_message(sdev->ipc, fir->comp.hdr.cmd, fir, + ipc_size, r, sizeof(*r)); + if (ret >= 0) + return ret; +err: + kfree(fir); + return ret; +} + +static int sof_effect_iir_load(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct snd_soc_dapm_widget *widget = swidget->widget; + const struct snd_kcontrol_new *kc = NULL; + struct soc_bytes_ext *sbe; + struct snd_sof_control *scontrol = NULL; + struct sof_abi_hdr *pdata = NULL; + struct sof_ipc_comp_eq_iir *iir; + size_t ipc_size = 0, iir_data_size = 0; + int ret; + + /* get possible eq controls */ + kc = &widget->kcontrol_news[0]; + if (kc) { + sbe = (struct soc_bytes_ext *)kc->private_value; + scontrol = sbe->dobj.private; + } + + /* + * Check if there's eq parameters in control's private member and set + * data size accordingly. If there's no parameters eq will use defaults + * in firmware (which in this case is passthrough). + */ + if (scontrol && scontrol->cmd == SOF_CTRL_CMD_BINARY) { + pdata = scontrol->control_data->data; + if (pdata->size > 0 && pdata->magic == SOF_ABI_MAGIC) + iir_data_size = pdata->size; + } + + ipc_size = sizeof(struct sof_ipc_comp_eq_iir) + + le32_to_cpu(private->size) + + iir_data_size; + + iir = kzalloc(ipc_size, GFP_KERNEL); + if (!iir) + return -ENOMEM; + + /* configure iir IPC message */ + iir->comp.hdr.size = ipc_size; + iir->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + iir->comp.id = swidget->comp_id; + iir->comp.type = SOF_COMP_EQ_IIR; + iir->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &iir->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); - if (ret) { - dev_err(sdev->dev, "error: parse EQ.cfg tokens failed %d\n", + if (ret != 0) { + dev_err(sdev->dev, "error: parse iir.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } - dev_dbg(sdev->dev, "eq iir %s created\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &iir->config); - sof_dbg_comp_config(scomp, &eq->config); - - swidget->private = (void *)eq; + /* we have a private data found in control, so copy it */ + if (iir_data_size > 0) { + memcpy(&iir->data, pdata->data, pdata->size); + iir->size = iir_data_size; + } - ret = sof_ipc_tx_message(sdev->ipc, eq->comp.hdr.cmd, eq, - sizeof(*eq), r, sizeof(*r)); + swidget->private = (void *)iir; + ret = sof_ipc_tx_message(sdev->ipc, iir->comp.hdr.cmd, iir, + ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: - kfree(eq); + kfree(iir); return ret; } +/* + * Effect Topology + */ + +static int sof_widget_load_effect(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_effect config; + int ret; + + /* check we have some tokens - we need at least effect type */ + if (le32_to_cpu(private->size) == 0) { + dev_err(sdev->dev, "error: effect tokens not found\n"); + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + + /* get the effect token */ + ret = sof_parse_tokens(scomp, &config, effect_tokens, + ARRAY_SIZE(effect_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse effect tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + /* now load effect specific data and send IPC */ + switch (config.type) { + case SOF_EFFECT_INTEL_EQFIR: + ret = sof_effect_fir_load(scomp, index, swidget, tw, r); + break; + case SOF_EFFECT_INTEL_EQIIR: + ret = sof_effect_iir_load(scomp, index, swidget, tw, r); + break; + default: + dev_err(sdev->dev, "error: invalid effect type %d\n", + config.type); + ret = -EINVAL; + break; + } + + if (ret < 0) { + dev_err(sdev->dev, "error: effect loading failed\n"); + return ret; + } + + return 0; +} + /* * Generic widget loader. */ From 3d863db83ff312e12d5a411d403983f015da248f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sun, 23 Sep 2018 23:53:35 -0700 Subject: [PATCH 215/298] ASoC: SOF: Separate out runtime suspend and system suspend routines This change mirrors the changes in the previous patch (commit id: 7128681) that separates out runtime resume and system resume. During system suspend the hdac display power needs to be turned off after powering down the hda controller and DSP. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dsp.c | 24 +++++++++++++++++++++++- sound/soc/sof/pm.c | 21 ++++++++++----------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index f0cd43efdaa4c6..930b8f21cce45a 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -381,20 +381,42 @@ int hda_dsp_resume(struct snd_sof_dev *sdev) } } + /* init hda controller and power dsp up */ return hda_resume(sdev); } int hda_dsp_runtime_resume(struct snd_sof_dev *sdev) { + /* init hda controller and power dsp up */ return hda_resume(sdev); } int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state) { + /* stop hda controller and power dsp off */ return hda_suspend(sdev, state); } int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) { - return hda_suspend(sdev, state); + struct hdac_bus *bus = sof_to_bus(sdev); + int ret; + + /* stop hda controller and power dsp off */ + ret = hda_suspend(sdev, state); + if (ret < 0) { + dev_err(bus->dev, "error: suspending dsp\n"); + return ret; + } + + /* turn display power off */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, false); + if (ret < 0) { + dev_err(bus->dev, "Cannot turn on display power on i915 after resume\n"); + return ret; + } + } + + return 0; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 62e8f676a8886f..36827c230b2b35 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -21,7 +21,7 @@ #include "ops.h" #include "sof-priv.h" -#define RUNTIME_RESUME 1 +#define RUNTIME_PM 1 static int sof_restore_pipelines(struct snd_sof_dev *sdev) { @@ -301,7 +301,7 @@ static int sof_resume(struct device *dev, int runtime_resume) return ret; } -static int sof_suspend(struct device *dev) +static int sof_suspend(struct device *dev, int runtime_suspend) { struct sof_platform_priv *priv = dev_get_drvdata(dev); struct snd_sof_dev *sdev = dev_get_drvdata(&priv->pdev_pcm->dev); @@ -333,7 +333,10 @@ static int sof_suspend(struct device *dev) snd_sof_dma_trace_release(sdev); /* power down DSP */ - ret = snd_sof_dsp_suspend(sdev, 0); + if (runtime_suspend) + ret = snd_sof_dsp_runtime_suspend(sdev, 0); + else + ret = snd_sof_dsp_suspend(sdev, 0); if (ret < 0) dev_err(sdev->dev, "error: failed to power down DSP during suspend %d\n", @@ -344,29 +347,25 @@ static int sof_suspend(struct device *dev) int snd_sof_runtime_suspend(struct device *dev) { - return sof_suspend(dev); + return sof_suspend(dev, RUNTIME_PM); } EXPORT_SYMBOL(snd_sof_runtime_suspend); int snd_sof_runtime_resume(struct device *dev) { - return sof_resume(dev, RUNTIME_RESUME); + return sof_resume(dev, RUNTIME_PM); } EXPORT_SYMBOL(snd_sof_runtime_resume); int snd_sof_resume(struct device *dev) { - return sof_resume(dev, 0); + return sof_resume(dev, !RUNTIME_PM); } EXPORT_SYMBOL(snd_sof_resume); int snd_sof_suspend(struct device *dev) { - /* suspend if dev is runtime_active */ - if (pm_runtime_active(dev)) - return sof_suspend(dev); - - return 0; + return sof_suspend(dev, !RUNTIME_PM); } EXPORT_SYMBOL(snd_sof_suspend); From aa838fd6fd494de3818d03016e7cf9808caa76f3 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sun, 23 Sep 2018 23:56:36 -0700 Subject: [PATCH 216/298] ASoC: SOF: use suspend_late/resume_early instead of suspend/resume The normal behaviour for PCI devices is that when the system suspend occurs while the device is runtime suspended, its runtime_resume method gets called to bring it back to full power. This seems to cause race condition between runtime_resume and system suspend and prevents the system from entering S2. Also, it is recommended that bus controllers be suspended in the late suspend phase and resumed in the resume early phase. This ensures that the runtime pm for the devices is disabled before the suspend_late is called. Therefore, switch to using suspend_late and resume_early for the SOF PCI device. Signed-off-by: Ranjani Sridharan Signed-off-by: Harshapriya N --- sound/soc/sof/pm.c | 12 +++++++++--- sound/soc/sof/sof-pci-dev.c | 1 + sound/soc/sof/sof-priv.h | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 36827c230b2b35..ed440738346e5e 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -359,18 +359,24 @@ EXPORT_SYMBOL(snd_sof_runtime_resume); int snd_sof_resume(struct device *dev) { - return sof_resume(dev, !RUNTIME_PM); + return 0; } EXPORT_SYMBOL(snd_sof_resume); +int snd_sof_resume_early(struct device *dev) +{ + return sof_resume(dev, !RUNTIME_PM); +} +EXPORT_SYMBOL(snd_sof_resume_early); + int snd_sof_suspend(struct device *dev) { - return sof_suspend(dev, !RUNTIME_PM); + return 0; } EXPORT_SYMBOL(snd_sof_suspend); int snd_sof_suspend_late(struct device *dev) { - return 0; + return sof_suspend(dev, !RUNTIME_PM); } EXPORT_SYMBOL(snd_sof_suspend_late); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index fcef2bb1318215..c9778efae8cc43 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -132,6 +132,7 @@ static const struct dev_pm_ops sof_pci_pm = { SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) .suspend_late = snd_sof_suspend_late, + .resume_early = snd_sof_resume_early, }; static const struct sof_ops_table mach_ops[] = { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ee1e423ad50c5c..060a38b509ffcb 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -388,6 +388,7 @@ void snd_sof_shutdown(struct device *dev); int snd_sof_runtime_suspend(struct device *dev); int snd_sof_runtime_resume(struct device *dev); int snd_sof_resume(struct device *dev); +int snd_sof_resume_early(struct device *dev); int snd_sof_suspend(struct device *dev); int snd_sof_suspend_late(struct device *dev); From 342b260b982ab9bfa1a4ab299a4590b80543bb46 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 24 Sep 2018 00:14:13 -0700 Subject: [PATCH 217/298] ASoC: SOF: reset scontrol readback control after resuming Reset scontrol readback_offset after resuming from runtime pm or system suspend as it is not valid anymore. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index ed440738346e5e..bdc68be0ae8c84 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -130,6 +130,8 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) /* restore kcontrol values */ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + /* reset readback offset for scontrol after resuming */ + scontrol->readback_offset = 0; /* notify DSP of kcontrol values */ switch (scontrol->cmd) { From b5cde886206b6f769fbbfac907f768f50a433597 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Tue, 11 Sep 2018 13:57:13 +0300 Subject: [PATCH 218/298] ASoC: SOF: Use header struct with ext bytes put/get data and add checks This patch adds to bytes put a check to not exceed the topology defined bytes control length. The tlv control header is used for retrieving length instead to directly access the header words. In bytes get the size field in the header is set the to the value that is passed to the function minus the header length. It prevents to write past the user space buffer. The size parameter originates from topology. Checks for size violations vs. topology and IPC are added. The other changes add debug prints to kernel log to see the actual size limits and do code cleanup. Signed-off-by: Seppo Ingalsuo --- sound/soc/sof/control.c | 102 ++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 29 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 71914d0898dadb..6f73041760f861 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -278,13 +278,13 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = be->dobj.private; struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; - u32 header[2]; + struct snd_ctl_tlv header; + struct snd_ctl_tlv *tlvd = (struct snd_ctl_tlv *)binary_data; int ret; int err; - int length; - const int max_length = SOF_IPC_MSG_MAX_SIZE - - sizeof(struct sof_ipc_ctrl_data) - - sizeof(struct sof_abi_hdr); + int max_length = SOF_IPC_MSG_MAX_SIZE - + sizeof(const struct sof_ipc_ctrl_data) - + sizeof(const struct sof_abi_hdr); ret = pm_runtime_get_sync(sdev->dev); if (ret < 0) { @@ -293,24 +293,35 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, return ret; } - /* the first two ints of the bytes data contain a dummy tag - * and the size, so copy from the 3rd int - * TODO: or should we send the size as well so the firmware - * can allocate memory accordingly? + /* The beginning of bytes data contains a header from where + * the length (as bytes) is needed to know the correct copy + * length of data from tlvd->tlv. */ - if (copy_from_user(&header, binary_data, 2*sizeof(u32))) + if (copy_from_user(&header, tlvd, sizeof(const struct snd_ctl_tlv))) return -EFAULT; - length = header[1]; - dev_dbg(sdev->dev, "size of bytes put data is %d\n", length); - if (length > max_length) { - dev_err(sdev->dev, "error: size is too large, bytes max is %d\n", - max_length); + /* The maximum length that can be copied is limited by IPC max + * length and topology defined length for ext bytes control. + */ + if (max_length > be->max) + max_length = be->max; + + dev_dbg(sdev->dev, "size of bytes put data is %d, max is %d\n", + header.length, max_length); + if (header.length > max_length) { + dev_err(sdev->dev, "error: size is too large\n"); ret = -EINVAL; goto out; } - if (copy_from_user(cdata->data->data, binary_data + 2, length)) + /* Check that header id matches the command */ + if (header.numid != scontrol->cmd) { + dev_err(sdev->dev, "error: incorred numid %d\n", header.numid); + ret = -EINVAL; + goto out; + } + + if (copy_from_user(cdata->data->data, tlvd->tlv, header.length)) return -EFAULT; /* set the ABI header values */ @@ -341,25 +352,58 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = be->dobj.private; struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; - unsigned int tag = 0; + struct snd_ctl_tlv header; + struct snd_ctl_tlv *tlvd = (struct snd_ctl_tlv *)binary_data; + int max_length = SOF_IPC_MSG_MAX_SIZE - + sizeof(const struct sof_ipc_ctrl_data) - + sizeof(const struct sof_abi_hdr); + int ret; - pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: bytes get failed to resume %d\n", + ret); + return ret; + } - dev_dbg(sdev->dev, "getting data and command is %d\n", scontrol->cmd); - /* get all the mixer data from DSP */ - snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_DATA, - SOF_CTRL_TYPE_DATA_GET, scontrol->cmd); + /* Decrement size to fit the ext bytes header and get the the + * upper limit from ext bytes control size from topology and + * SOF IPC max. size limit. + */ + size -= sizeof(const struct snd_ctl_tlv); + if (max_length > be->max) + max_length = be->max; - /* TODO: replace 252 with actual size */ - if (copy_to_user(binary_data, &tag, sizeof(u32))) - return -EFAULT; - if (copy_to_user(binary_data + 1, &size, sizeof(u32))) + dev_dbg(sdev->dev, "request size minus header is %d\n", size); + dev_dbg(sdev->dev, "max size is %d\n", max_length); + + /* Prevent read of other kernel data */ + if (size > max_length) { + dev_err(sdev->dev, "error: size is too large\n"); + ret = -EINVAL; + goto out; + } + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->comp_abi = SOF_ABI_VERSION; + + /* get all the component data from DSP */ + ret = snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_GET_DATA, + SOF_CTRL_TYPE_DATA_GET, scontrol->cmd); + + header.numid = scontrol->cmd; + header.length = size; + if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) return -EFAULT; - if (copy_to_user(binary_data + 2, cdata->data->data, - SOF_IPC_MSG_MAX_SIZE - sizeof(struct sof_ipc_ctrl_data))) + + if (copy_to_user(tlvd->tlv, cdata->data->data, size)) return -EFAULT; +out: pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); - return 0; + return ret; } From ea4b035ccb67ca15dd5132758402f36d2f3a2cda Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 24 Sep 2018 23:28:23 -0700 Subject: [PATCH 219/298] ASoC: SOF: do not reset HDA controller during suspend Resetting the HDA controller during suspend seems to cause a system suspend failure. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dsp.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 930b8f21cce45a..0761375e7e6e00 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -301,12 +301,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, int state) snd_sof_pci_update_bits(sdev, PCI_TCSEL, PCI_CGCTL_LSRMD_MASK, PCI_CGCTL_LSRMD_MASK); - /* reset HDA controller */ - ret = hda_dsp_ctrl_link_reset(sdev); - if (ret < 0) - dev_err(sdev->dev, "error: failed to reset HDA controller\n"); - - return ret; + return 0; } static int hda_resume(struct snd_sof_dev *sdev) From 8befd75ad711eeb22ba4f85062c5a592b938d7c6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 24 Sep 2018 17:29:57 -0700 Subject: [PATCH 220/298] ASoC: SOF: use resume instead of resume_early This patch fixes commit (aa838fd) that causes kernel errors while resuming from suspend. hdac display power cannot be turned on before i915 is resumed. So switch back to using resume instead of resume_early. But we still need to power off the dsp and the hdac display power in suspend_late. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pm.c | 8 +------- sound/soc/sof/sof-pci-dev.c | 1 - sound/soc/sof/sof-priv.h | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index bdc68be0ae8c84..78b61baad672dd 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -360,16 +360,10 @@ int snd_sof_runtime_resume(struct device *dev) EXPORT_SYMBOL(snd_sof_runtime_resume); int snd_sof_resume(struct device *dev) -{ - return 0; -} -EXPORT_SYMBOL(snd_sof_resume); - -int snd_sof_resume_early(struct device *dev) { return sof_resume(dev, !RUNTIME_PM); } -EXPORT_SYMBOL(snd_sof_resume_early); +EXPORT_SYMBOL(snd_sof_resume); int snd_sof_suspend(struct device *dev) { diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index c9778efae8cc43..fcef2bb1318215 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -132,7 +132,6 @@ static const struct dev_pm_ops sof_pci_pm = { SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) .suspend_late = snd_sof_suspend_late, - .resume_early = snd_sof_resume_early, }; static const struct sof_ops_table mach_ops[] = { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 060a38b509ffcb..ee1e423ad50c5c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -388,7 +388,6 @@ void snd_sof_shutdown(struct device *dev); int snd_sof_runtime_suspend(struct device *dev); int snd_sof_runtime_resume(struct device *dev); int snd_sof_resume(struct device *dev); -int snd_sof_resume_early(struct device *dev); int snd_sof_suspend(struct device *dev); int snd_sof_suspend_late(struct device *dev); From 160b45f36c3d5b294449867df2e62c02c9a784dd Mon Sep 17 00:00:00 2001 From: Zhu Yingjiang Date: Tue, 25 Sep 2018 17:32:44 +0800 Subject: [PATCH 221/298] ASoC: SOF: skl add cl_stream_prepare_skl v3 Signed-off-by: Zhu Yingjiang --- sound/soc/sof/intel/Makefile | 2 +- sound/soc/sof/intel/hda-loader-skl.c | 420 +++++++++++++++++++++++++++ sound/soc/sof/intel/hda-loader.c | 102 ------- sound/soc/sof/sof-priv.h | 1 + 4 files changed, 422 insertions(+), 103 deletions(-) create mode 100644 sound/soc/sof/intel/hda-loader-skl.c diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index accb3d4d209d21..a7a7c0cf7663aa 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -7,7 +7,7 @@ snd-sof-intel-hsw-objs := hsw.o snd-sof-intel-bdw-objs := bdw.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ - hda-dai.o hda-bus.o \ + hda-dai.o hda-bus.o hda-loader-skl.o \ skl.o apl.o cnl.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c new file mode 100644 index 00000000000000..df9b1e4db13cb2 --- /dev/null +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation. All rights reserved. +// +// Authors: Liam Girdwood +// Zhu Yingjiang +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +#define SKL_MAX_BUFFER_SIZE (32 * PAGE_SIZE) + +/* Stream Reset */ +#define HDA_CL_SD_CTL_SRST_SHIFT 0 +#define HDA_CL_SD_CTL_SRST(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_SRST_SHIFT) + +/* Stream Run */ +#define HDA_CL_SD_CTL_RUN_SHIFT 1 +#define HDA_CL_SD_CTL_RUN(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_RUN_SHIFT) + +/* Interrupt On Completion Enable */ +#define HDA_CL_SD_CTL_IOCE_SHIFT 2 +#define HDA_CL_SD_CTL_IOCE(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_IOCE_SHIFT) + +/* FIFO Error Interrupt Enable */ +#define HDA_CL_SD_CTL_FEIE_SHIFT 3 +#define HDA_CL_SD_CTL_FEIE(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_FEIE_SHIFT) + +/* Descriptor Error Interrupt Enable */ +#define HDA_CL_SD_CTL_DEIE_SHIFT 4 +#define HDA_CL_SD_CTL_DEIE(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_DEIE_SHIFT) + +/* FIFO Limit Change */ +#define HDA_CL_SD_CTL_FIFOLC_SHIFT 5 +#define HDA_CL_SD_CTL_FIFOLC(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_FIFOLC_SHIFT) + +/* Stripe Control */ +#define HDA_CL_SD_CTL_STRIPE_SHIFT 16 +#define HDA_CL_SD_CTL_STRIPE(x) (((x) & 0x3) << \ + HDA_CL_SD_CTL_STRIPE_SHIFT) + +/* Traffic Priority */ +#define HDA_CL_SD_CTL_TP_SHIFT 18 +#define HDA_CL_SD_CTL_TP(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_TP_SHIFT) + +/* Bidirectional Direction Control */ +#define HDA_CL_SD_CTL_DIR_SHIFT 19 +#define HDA_CL_SD_CTL_DIR(x) (((x) & 0x1) << \ + HDA_CL_SD_CTL_DIR_SHIFT) + +/* Stream Number */ +#define HDA_CL_SD_CTL_STRM_SHIFT 20 +#define HDA_CL_SD_CTL_STRM(x) (((x) & 0xf) << \ + HDA_CL_SD_CTL_STRM_SHIFT) + +#define HDA_CL_SD_CTL_INT(x) \ + (HDA_CL_SD_CTL_IOCE(x) | \ + HDA_CL_SD_CTL_FEIE(x) | \ + HDA_CL_SD_CTL_DEIE(x)) + +#define HDA_CL_SD_CTL_INT_MASK \ + (HDA_CL_SD_CTL_IOCE(1) | \ + HDA_CL_SD_CTL_FEIE(1) | \ + HDA_CL_SD_CTL_DEIE(1)) + +#define DMA_ADDRESS_128_BITS_ALIGNMENT 7 +#define BDL_ALIGN(x) ((x) >> DMA_ADDRESS_128_BITS_ALIGNMENT) + +/* Buffer Descriptor List Lower Base Address */ +#define HDA_CL_SD_BDLPLBA_SHIFT 7 +#define HDA_CL_SD_BDLPLBA_MASK (0x1ffffff << HDA_CL_SD_BDLPLBA_SHIFT) +#define HDA_CL_SD_BDLPLBA(x) \ + ((BDL_ALIGN(lower_32_bits(x)) << HDA_CL_SD_BDLPLBA_SHIFT) & \ + HDA_CL_SD_BDLPLBA_MASK) + +/* Buffer Descriptor List Upper Base Address */ +#define HDA_CL_SD_BDLPUBA_SHIFT 0 +#define HDA_CL_SD_BDLPUBA_MASK (0xffffffff << HDA_CL_SD_BDLPUBA_SHIFT) +#define HDA_CL_SD_BDLPUBA(x) \ + ((upper_32_bits(x) << HDA_CL_SD_BDLPUBA_SHIFT) & \ + HDA_CL_SD_BDLPUBA_MASK) + +/* Software Position in Buffer Enable */ +#define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT 0 +#define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK \ + (1 << HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) + +#define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(x) \ + (((x) << HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) & \ + HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK) + +static int cl_skl_cldma_setup_bdle(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab_data, + u32 **bdlp, int size, int with_ioc) +{ + u32 *bdl = *bdlp; + int frags = 0; + + while (size > 0) { + phys_addr_t addr = virt_to_phys(dmab_data->area + + (frags * size)); + + bdl[0] = cpu_to_le32(lower_32_bits(addr)); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + + bdl[2] = cpu_to_le32(size); + + size -= size; + bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); + + bdl += 4; + frags++; + } + + return frags; +} + +static void cl_skl_cldma_stream_run(struct snd_sof_dev *sdev, bool enable) +{ + unsigned char val; + int timeout; + int sd_offset = SOF_HDA_ADSP_LOADER_BASE; + u32 run = enable ? 0x1 : 0; + + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CTL, + HDA_CL_SD_CTL_RUN(1), HDA_CL_SD_CTL_RUN(run)); + + udelay(3); + timeout = 300; + do { + /* waiting for hardware to report the stream Run bit set */ + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CTL) + & HDA_CL_SD_CTL_RUN(1); + if (enable && val) + break; + else if (!enable && !val) + break; + udelay(3); + } while (--timeout); + + if (timeout == 0) + dev_err(sdev->dev, "Failed to set Run bit=%d enable=%d\n", + val, enable); +} + +static void cl_skl_cldma_stream_clear(struct snd_sof_dev *sdev) +{ + int sd_offset = SOF_HDA_ADSP_LOADER_BASE; + /* make sure Run bit is cleared before setting stream register */ + cl_skl_cldma_stream_run(sdev, 0); + + /* Disable the Interrupt On Completion, FIFO Error Interrupt, + * Descriptor Error Interrupt and set the cldma stream number to 0. + */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CTL, + HDA_CL_SD_CTL_INT_MASK, HDA_CL_SD_CTL_INT(0)); + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CTL, + HDA_CL_SD_CTL_STRM(0xf), HDA_CL_SD_CTL_STRM(0)); + + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + HDA_CL_SD_BDLPLBA(0)); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0); + + /* Set the Cyclic Buffer Length to 0. */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, 0); + /* Set the Last Valid Index. */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, 0); +} + +static void cl_skl_cldma_setup_spb(struct snd_sof_dev *sdev, + unsigned int size, bool enable) +{ + int sd_offset = SOF_DSP_REG_CL_SPBFIFO; + + if (enable) + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + sd_offset + + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, + HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK, + HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(1)); + + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB, size); +} + +static void cl_skl_cldma_cleanup_spb(struct snd_sof_dev *sdev) +{ + int sd_offset = SOF_DSP_REG_CL_SPBFIFO; + + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + sd_offset + + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, + HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK, + HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(0)); + + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB, 0); +} + +static void cl_skl_cldma_setup_controller(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab_bdl, + unsigned int max_size, u32 count) +{ + int sd_offset = SOF_HDA_ADSP_LOADER_BASE; + /* Clear the stream first and then set it. */ + cl_skl_cldma_stream_clear(sdev); + + /* setting the stream register */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + HDA_CL_SD_BDLPLBA(dmab_bdl->addr)); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + HDA_CL_SD_BDLPUBA(dmab_bdl->addr)); + + /* Set the Cyclic Buffer Length. */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, max_size); + /* Set the Last Valid Index. */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, count - 1); + + /* Set the Interrupt On Completion, FIFO Error Interrupt, + * Descriptor Error Interrupt and the cldma stream number. + */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CTL, + HDA_CL_SD_CTL_INT_MASK, HDA_CL_SD_CTL_INT(1)); + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CTL, + HDA_CL_SD_CTL_STRM(0xf), + HDA_CL_SD_CTL_STRM(1)); +} + +static int cl_stream_prepare_skl(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = sdev->pci; + int frags = 0; + int ret = 0; + u32 *bdl; + unsigned int bufsize = SKL_MAX_BUFFER_SIZE; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, bufsize, + &sdev->dmab); + if (ret < 0) { + dev_err(sdev->dev, "Alloc base fw buffer failed: %x\n", ret); + return ret; + } + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, bufsize, + &sdev->dmab_bdl); + if (ret < 0) { + dev_err(sdev->dev, "Alloc buffer for blde failed: %x\n", ret); + snd_dma_free_pages(&sdev->dmab); + return ret; + } + bdl = (u32 *)sdev->dmab_bdl.area; + + frags = cl_skl_cldma_setup_bdle(sdev, &sdev->dmab, &bdl, bufsize, 1); + cl_skl_cldma_setup_controller(sdev, &sdev->dmab_bdl, bufsize, frags); + + return ret; +} + +static void cl_cleanup_skl(struct snd_sof_dev *sdev) +{ + cl_skl_cldma_cleanup_spb(sdev); + cl_skl_cldma_stream_clear(sdev); + snd_dma_free_pages(&sdev->dmab); + snd_dma_free_pages(&sdev->dmab_bdl); + sdev->dmab.area = NULL; +} + +static int cl_dsp_init_skl(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + int ret; + + /* check if the core is already enabled, if yes, reset and make it run, + * if not, powerdown and enable it again. + */ + if (hda_dsp_core_is_enabled(sdev, HDA_DSP_CORE_MASK(0))) { + /* if enabled, reset it, and run the core. */ + ret = hda_dsp_core_stall_reset(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) + goto err; + + ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core start failed %d\n", + ret); + goto err; + } + } else { + /* if not enabled, power down it first and then powerup and run + * the core. + */ + ret = hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "dsp core0 disable fail: %d\n", ret); + goto err; + } + ret = hda_dsp_enable_core(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "dsp core0 enable fail: %d\n", ret); + goto err; + } + } + + /* prepare DMA for code loader stream */ + ret = cl_stream_prepare_skl(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: dma prepare fw loading err: %x\n", + ret); + return ret; + } + + /* enable the interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + + /* enable IPC DONE interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + + /* enable IPC BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_BUSY, + HDA_DSP_REG_HIPCCTL_BUSY); + + /* polling the ROM init status information. */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS_SKL, + HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_INIT, + HDA_DSP_INIT_TIMEOUT); + if (ret < 0) + goto err; + + return ret; + +err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + cl_cleanup_skl(sdev); + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); + return ret; +} + +int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) +{ + int ret; + + ret = cl_dsp_init_skl(sdev); + + /* retry enabling core and ROM load. seemed to help */ + if (ret < 0) { + ret = cl_dsp_init_skl(sdev); + if (ret < 0) { + dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); + dev_err(sdev->dev, "Core En/ROM load fail:%d\n", ret); + goto irq_err; + } + } + + dev_dbg(sdev->dev, "ROM init successful\n"); + + /* init for booting wait */ + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + return ret; + +irq_err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + + /* power down DSP */ + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); + cl_cleanup_skl(sdev); + + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + return ret; +} diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index f12c49958fe942..796d772a6a8d30 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -372,105 +372,3 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); return ret; } - -static int cl_dsp_init_skl(struct snd_sof_dev *sdev) -{ - const struct sof_intel_dsp_desc *chip = sdev->hda->desc; - int ret; - - /* check if the core is already enabled, if yes, reset and make it run, - * if not, powerdown and enable it again. - */ - if (hda_dsp_core_is_enabled(sdev, HDA_DSP_CORE_MASK(0))) { - /* if enabled, reset it, and run the core. */ - ret = hda_dsp_core_stall_reset(sdev, HDA_DSP_CORE_MASK(0)); - if (ret < 0) - goto err; - - ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); - if (ret < 0) { - dev_err(sdev->dev, "error: dsp core start failed %d\n", - ret); - goto err; - } - } else { - /* if not enabled, power down it first and then powerup and run - * the core. - */ - ret = hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); - if (ret < 0) { - dev_err(sdev->dev, "dsp core0 disable fail: %d\n", ret); - goto err; - } - ret = hda_dsp_enable_core(sdev, HDA_DSP_CORE_MASK(0)); - if (ret < 0) { - dev_err(sdev->dev, "dsp core0 enable fail: %d\n", ret); - goto err; - } - } - - /* enable the interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); - - /* enable IPC DONE interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, - HDA_DSP_REG_HIPCCTL_DONE, - HDA_DSP_REG_HIPCCTL_DONE); - - /* enable IPC BUSY interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, - HDA_DSP_REG_HIPCCTL_BUSY, - HDA_DSP_REG_HIPCCTL_BUSY); - - /* polling the ROM init status information. */ - ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_STATUS_SKL, - HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_INIT, - HDA_DSP_INIT_TIMEOUT); - if (ret < 0) - goto err; - - return ret; - -err: - hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); - hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); - return ret; -} - -int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) -{ - int ret; - - ret = cl_dsp_init_skl(sdev); - - /* retry enabling core and ROM load. seemed to help */ - if (ret < 0) { - ret = cl_dsp_init_skl(sdev); - if (ret < 0) { - dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_ERROR), - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_STATUS)); - dev_err(sdev->dev, "Core En/ROM load fail:%d\n", ret); - goto irq_err; - } - } - - /* init for booting wait */ - init_waitqueue_head(&sdev->boot_wait); - sdev->boot_complete = false; - - return ret; - -irq_err: - hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); - - /* power down DSP */ - hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); - - dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); - return ret; -} diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ee1e423ad50c5c..43c62793109e7a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -334,6 +334,7 @@ struct snd_sof_dev { /* firmware loader */ int cl_bar; struct snd_dma_buffer dmab; + struct snd_dma_buffer dmab_bdl; struct sof_ipc_fw_ready fw_ready; /* topology */ From e0097b0e17246c7b279bc226d47f777f45bd7b4e Mon Sep 17 00:00:00 2001 From: Bard liao Date: Tue, 25 Sep 2018 09:20:05 +0000 Subject: [PATCH 222/298] Revert "ASoC: SOF: use devm_kfree" This reverts commit 108d9cf3daec51bfd01211a7e9585013131f612a. Signed-off-by: Bard liao --- sound/soc/sof/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 976b01c3e8fae0..ad41a571b918b5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2499,7 +2499,7 @@ void snd_sof_free_topology(struct snd_sof_dev *sdev) /* free sroute and its private data */ kfree(sroute->private); - devm_kfree(sdev->dev, sroute); + kfree(sroute); } ret = snd_soc_tplg_component_remove(sdev->component, From 3f22cc6071cc075cf9e4c95246a014998fe0098e Mon Sep 17 00:00:00 2001 From: Bard liao Date: Tue, 25 Sep 2018 09:20:57 +0000 Subject: [PATCH 223/298] Revert "ASoC: SOF: use devm_kzalloc" This reverts commit 801d1765db4eec9acaaeb58edc20c64c91c2f20a. Signed-off-by: Bard liao --- sound/soc/sof/topology.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index ad41a571b918b5..6e1be4513e7df9 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2208,14 +2208,15 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, int ret = 0; /* allocate memory for sroute and connect */ - sroute = devm_kzalloc(sdev->dev, sizeof(*sroute), GFP_KERNEL); + sroute = kzalloc(sizeof(*sroute), GFP_KERNEL); if (!sroute) return -ENOMEM; sroute->sdev = sdev; - connect = devm_kzalloc(sdev->dev, sizeof(*connect), GFP_KERNEL); + connect = kzalloc(sizeof(*connect), GFP_KERNEL); if (!connect) { + kfree(sroute); return -ENOMEM; } @@ -2328,7 +2329,11 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, list_add(&sroute->list, &sdev->route_list); } + return ret; + err: + kfree(connect); + kfree(sroute); return ret; } From 323c12c97138604aecbb66b2b6f1698eb3c0146e Mon Sep 17 00:00:00 2001 From: Bard liao Date: Tue, 25 Sep 2018 12:27:41 +0000 Subject: [PATCH 224/298] ASoC: SOF: topology: free unused item before return. sroute and connect are no longer used if we don't add it to the list. Signed-off-by: Bard liao --- sound/soc/sof/topology.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 6e1be4513e7df9..fcd4888ab8cf4c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2234,9 +2234,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, spcm = snd_sof_find_spcm_name(sdev, (char *)route->source); if (spcm) { ret = spcm_bind(scomp, spcm, route->sink); - if (ret < 0) - goto err; - return 0; + goto err; } dev_err(sdev->dev, "error: source %s not found\n", @@ -2254,9 +2252,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, spcm = snd_sof_find_spcm_name(sdev, (char *)route->sink); if (spcm) { ret = spcm_bind(scomp, spcm, route->source); - if (ret < 0) - goto err; - return 0; + goto err; } dev_err(sdev->dev, "error: sink %s not found\n", @@ -2327,9 +2323,9 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, /* add route to route list */ list_add(&sroute->list, &sdev->route_list); - } - return ret; + return ret; + } err: kfree(connect); From 51ebd5d01e6d26ff7f2103417db60b37ad734776 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 11 Sep 2018 15:12:57 +0200 Subject: [PATCH 225/298] sound: fix build on Alpha A randconfig test managed to find a configuration, which breaks with undefined PAGE_SIZE and PAGE_SHIFT. Include a header to guarantee, that they are defined in all configurations. Signed-off-by: Guennadi Liakhovetski --- include/sound/memalloc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 3f8e6eade1ef52..6a7d6b8a4d342c 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -24,6 +24,8 @@ #ifndef __SOUND_MEMALLOC_H #define __SOUND_MEMALLOC_H +#include + struct device; /* From abc6888ecd56369628d89970f29e6f17aefc5563 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 26 Sep 2018 20:50:38 +0800 Subject: [PATCH 226/298] ASoC: sof: ipc: add sof_ipc_panic_info Use sof_ipc_panic_info to store panic info include filename and line number. Signed-off-by: Pan Xiuli --- include/uapi/sound/sof-ipc.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 48e6bc867bacf9..a7bcffb7643044 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -952,4 +952,14 @@ struct sof_ipc_dsp_oops_xtensa { uint32_t stack; } __attribute__((packed)); +/* + * Commom debug + */ + +/* panic info include filename and line number */ +struct sof_ipc_panic_info { + char filename[32]; + uint32_t linenum; +} __attribute__((packed)); + #endif From 1f6c9cfc12eb05218b9d8c8529854cdb09904c11 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 26 Sep 2018 20:53:49 +0800 Subject: [PATCH 227/298] ASoC: sof: add panic info into panic dump Panic info include filename and line number that panic called, dump this info when panic happen. And output panic info when panic happen to help debug. Signed-off-by: Pan Xiuli --- sound/soc/sof/core.c | 7 +++++-- sound/soc/sof/intel/bdw.c | 18 +++++++++++++----- sound/soc/sof/intel/byt.c | 18 +++++++++++++----- sound/soc/sof/intel/hda.c | 15 +++++++++++---- sound/soc/sof/intel/hsw.c | 18 +++++++++++++----- sound/soc/sof/sof-priv.h | 5 +++-- 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index ac20b901f32369..2e91bf7cea430b 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -149,8 +149,9 @@ static const struct sof_panic_msg panic_msg[] = { }; int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, void *stack, - size_t stack_words) + u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_words) { u32 code; int i; @@ -179,6 +180,8 @@ int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); out: + dev_err(sdev->dev, "error: panic happen at %s:%d\n", + panic_info->filename, panic_info->linenum); sof_oops(sdev, oops); sof_stack(sdev, oops, stack, stack_words); return -EFAULT; diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 8ed608ee6e3055..8068a48c90cc9b 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -296,27 +296,35 @@ static int bdw_set_dsp_D0(struct snd_sof_dev *sdev) static void bdw_get_registers(struct snd_sof_dev *sdev, struct sof_ipc_dsp_oops_xtensa *xoops, + struct sof_ipc_panic_info *panic_info, u32 *stack, size_t stack_words) { /* first read regsisters */ bdw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); - /* the get the stack */ - bdw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, - stack_words * sizeof(u32)); + /* then get panic info */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), + panic_info, sizeof(*panic_info)); + + /* then get the stack */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops) + + sizeof(*panic_info), stack, + stack_words * sizeof(u32)); } static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) { struct sof_ipc_dsp_oops_xtensa xoops; + struct sof_ipc_panic_info panic_info; u32 stack[BDW_STACK_DUMP_SIZE]; u32 status, panic; /* now try generic SOF status messages */ status = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); - bdw_get_registers(sdev, &xoops, stack, BDW_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, stack, + bdw_get_registers(sdev, &xoops, &panic_info, stack, + BDW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, BDW_STACK_DUMP_SIZE); } diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 14cf6b1082b85e..25d5bf0ed6f31c 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -314,27 +314,35 @@ static void byt_mailbox_read(struct snd_sof_dev *sdev, u32 offset, static void byt_get_registers(struct snd_sof_dev *sdev, struct sof_ipc_dsp_oops_xtensa *xoops, + struct sof_ipc_panic_info *panic_info, u32 *stack, size_t stack_words) { /* first read regsisters */ byt_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); - /* the get the stack */ - byt_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, - stack_words * sizeof(u32)); + /* then get panic info */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), + panic_info, sizeof(*panic_info)); + + /* then get the stack */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops) + + sizeof(*panic_info), stack, + stack_words * sizeof(u32)); } static void byt_dump(struct snd_sof_dev *sdev, u32 flags) { struct sof_ipc_dsp_oops_xtensa xoops; + struct sof_ipc_panic_info panic_info; u32 stack[BYT_STACK_DUMP_SIZE]; u32 status, panic; /* now try generic SOF status messages */ status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD); panic = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCX); - byt_get_registers(sdev, &xoops, stack, BYT_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, stack, + byt_get_registers(sdev, &xoops, &panic_info, stack, + BYT_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, BYT_STACK_DUMP_SIZE); } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index ba492ac3756658..1270939e1e83f7 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -164,19 +164,26 @@ static void hda_dsp_get_status(struct snd_sof_dev *sdev) static void hda_dsp_get_registers(struct snd_sof_dev *sdev, struct sof_ipc_dsp_oops_xtensa *xoops, + struct sof_ipc_panic_info *panic_info, u32 *stack, size_t stack_words) { /* first read registers */ hda_dsp_block_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + /* then get panic info */ + hda_dsp_block_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), + panic_info, sizeof(*panic_info)); + /* then get the stack */ - hda_dsp_block_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + hda_dsp_block_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops) + + sizeof(*panic_info), stack, stack_words * sizeof(u32)); } void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { struct sof_ipc_dsp_oops_xtensa xoops; + struct sof_ipc_panic_info panic_info; u32 stack[HDA_DSP_STACK_DUMP_SIZE]; u32 status, panic; @@ -189,10 +196,10 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP); if (sdev->boot_complete) { - hda_dsp_get_registers(sdev, &xoops, stack, + hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, stack, - HDA_DSP_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, + stack, HDA_DSP_STACK_DUMP_SIZE); } else { dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n", status, panic); diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index e4d13b952e9c5f..fef9aa0c8ec249 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -295,27 +295,35 @@ static int hsw_set_dsp_D0(struct snd_sof_dev *sdev) static void hsw_get_registers(struct snd_sof_dev *sdev, struct sof_ipc_dsp_oops_xtensa *xoops, + struct sof_ipc_panic_info *panic_info, u32 *stack, size_t stack_words) { /* first read regsisters */ hsw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); - /* the get the stack */ - hsw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, - stack_words * sizeof(u32)); + /* then get panic info */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), + panic_info, sizeof(*panic_info)); + + /* then get the stack */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops) + + sizeof(*panic_info), stack, + stack_words * sizeof(u32)); } static void hsw_dump(struct snd_sof_dev *sdev, u32 flags) { struct sof_ipc_dsp_oops_xtensa xoops; + struct sof_ipc_panic_info panic_info; u32 stack[HSW_STACK_DUMP_SIZE]; u32 status, panic; /* now try generic SOF status messages */ status = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); panic = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); - hsw_get_registers(sdev, &xoops, stack, HSW_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, stack, + hsw_get_registers(sdev, &xoops, &panic_info, stack, + HSW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, HSW_STACK_DUMP_SIZE); } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 43c62793109e7a..f35dcca6550b4a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -484,8 +484,9 @@ int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn); void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, void *stack, - size_t stack_size); + u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_size); int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); /* From 940fa1cfffef3ab0774a59de3539b598d91a4cd2 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 27 Sep 2018 15:52:51 +0800 Subject: [PATCH 228/298] ASoC: SOF: Refine registeration of platform device Machine driver may check some HW features which are probed by SOF core driver. So platform device for machine driver should be registered after SOF core is ready. And at this time, dais of SOF have been registered to ASOC. It is better to register card now. Signed-off-by: Rander Wang --- sound/soc/sof/core.c | 14 ++++++++++++++ sound/soc/sof/sof-acpi-dev.c | 18 +++++++----------- sound/soc/sof/sof-pci-dev.c | 13 ------------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2e91bf7cea430b..64601c71692d3c 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -318,6 +318,20 @@ static int sof_probe(struct platform_device *pdev) goto comp_err; } + /* register machine driver */ + plat_data->pdev_mach = + platform_device_register_data(sdev->dev, + plat_data->machine->drv_name, + -1, plat_data, + sizeof(*plat_data)); + if (IS_ERR(plat_data->pdev_mach)) { + ret = PTR_ERR(plat_data->pdev_mach); + goto comp_err; + } + + dev_dbg(sdev->dev, "created machine %s\n", + dev_name(&plat_data->pdev_mach->dev)); + /* init DMA trace */ ret = snd_sof_init_trace(sdev); if (ret < 0) { diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 3d6750526860bf..2f04b09efd3f5b 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -254,18 +254,14 @@ static int sof_acpi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, priv); /* do we need to generate any machine plat data ? */ - if (mach->new_mach_data) + if (mach->new_mach_data) { sof_pdata->pdev_mach = mach->new_mach_data(sof_pdata); - else - /* register machine driver, pass machine info as pdata */ - sof_pdata->pdev_mach = - platform_device_register_data(dev, mach->drv_name, -1, - (const void *)mach, - sizeof(*mach)); - if (IS_ERR(sof_pdata->pdev_mach)) - return PTR_ERR(sof_pdata->pdev_mach); - dev_dbg(dev, "created machine %s\n", - dev_name(&sof_pdata->pdev_mach->dev)); + + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + } /* register sof-audio platform driver */ ret = sof_create_platform_device(priv); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index fcef2bb1318215..e10a86598ad146 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -246,22 +246,9 @@ static int sof_pci_probe(struct pci_dev *pci, sof_pdata->dev = &pci->dev; sof_pdata->type = SOF_DEVICE_PCI; - /* register machine driver */ - sof_pdata->pdev_mach = - platform_device_register_data(dev, mach->drv_name, -1, - sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(sof_pdata->pdev_mach)) { - ret = PTR_ERR(sof_pdata->pdev_mach); - goto release_regions; - } - - dev_dbg(dev, "created machine %s\n", - dev_name(&sof_pdata->pdev_mach->dev)); - /* register sof-audio platform driver */ ret = sof_create_platform_device(priv); if (ret) { - platform_device_unregister(sof_pdata->pdev_mach); dev_err(dev, "error: failed to create platform device!\n"); goto release_regions; } From 4180d405861a7ca9423d137756665c497af16126 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 27 Sep 2018 16:17:23 +0800 Subject: [PATCH 229/298] ASOC: SOF: add info for hda machine driver (1)Hda codec status and platform name are used by machine driver to create dai links, so it need to be set in sof core (2)Try HDA codec if no I2S codec is found Signed-off-by: Rander Wang --- include/sound/sof.h | 4 ++++ sound/soc/sof/intel/hda.c | 3 +++ sound/soc/sof/sof-acpi-dev.c | 6 ++++-- sound/soc/sof/sof-pci-dev.c | 7 ++++--- sound/soc/sof/sof-spi-dev.c | 11 ----------- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index bc87e8800ae718..1b0bfc295a78fc 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -47,6 +47,7 @@ struct snd_sof_pdata { const struct firmware *fw; const char *drv_name; const char *name; + const char *platform; /* parent device */ struct device *dev; @@ -59,6 +60,9 @@ struct snd_sof_pdata { unsigned int gpio; unsigned int active; + /* hda codec */ + unsigned long codec_mask; + /* machine */ struct platform_device *pdev_mach; union { diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 1270939e1e83f7..2e724d4efb638b 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -422,6 +422,9 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (!bus->codec_mask) dev_info(bus->dev, "no hda codecs found!\n"); + /* used by hda machine driver to create dai links */ + sdev->pdata->codec_mask = bus->codec_mask; + /* create codec instances */ hda_codec_probe_bus(sdev); diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 2f04b09efd3f5b..cf8e8cadd47d3b 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -232,8 +232,9 @@ static int sof_acpi_probe(struct platform_device *pdev) if (ret < 0) return ret; #else - dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); - return -ENODEV; + dev_warn(dev, "No matching ASoC machine driver found - falling back to HDA codec\n"); + mach = snd_soc_acpi_intel_hda_machines; + mach->sof_fw_filename = desc->nocodec_fw_filename; #endif } #endif @@ -251,6 +252,7 @@ static int sof_acpi_probe(struct platform_device *pdev) priv->sof_pdata = sof_pdata; sof_pdata->dev = &pdev->dev; sof_pdata->type = SOF_DEVICE_APCI; + sof_pdata->platform = "sof-audio"; dev_set_drvdata(&pdev->dev, priv); /* do we need to generate any machine plat data ? */ diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index e10a86598ad146..cd2be5f4623602 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -229,9 +229,9 @@ static int sof_pci_probe(struct pci_dev *pci, if (ret < 0) goto release_regions; #else - dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); - ret = -ENODEV; - goto release_regions; + dev_warn(dev, "No matching ASoC machine driver found - falling back to HDA codec\n"); + mach = snd_soc_acpi_intel_hda_machines; + mach->sof_fw_filename = desc->nocodec_fw_filename; #endif } #endif @@ -245,6 +245,7 @@ static int sof_pci_probe(struct pci_dev *pci, priv->sof_pdata = sof_pdata; sof_pdata->dev = &pci->dev; sof_pdata->type = SOF_DEVICE_PCI; + sof_pdata->platform = "sof-audio"; /* register sof-audio platform driver */ ret = sof_create_platform_device(priv); diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index c5b3befa19e37d..dc5867b7fa4a4e 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -130,20 +130,9 @@ static int sof_spi_probe(struct spi_device *spi) sof_pdata->dev = dev; sof_pdata->type = SOF_DEVICE_SPI; - /* register machine driver */ - sof_pdata->pdev_mach = - platform_device_register_data(dev, mach->drv_name, -1, - sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(sof_pdata->pdev_mach)) - return PTR_ERR(sof_pdata->pdev_mach); - - dev_dbg(dev, "created machine %s\n", - dev_name(&sof_pdata->pdev_mach->dev)); - /* register sof-audio platform driver */ ret = sof_create_platform_device(priv); if (ret) { - platform_device_unregister(sof_pdata->pdev_mach); dev_err(dev, "error: failed to create platform device!\n"); return ret; } From 9fd4421b1d1d6f6a618e65990e0f4b50f6ec9b59 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 27 Sep 2018 16:23:27 +0800 Subject: [PATCH 230/298] ASOC: Intel: refine the type of codec_mask from u32 to unsigned long The default type of codec_mask in HDA system is unsigned long Signed-off-by: Rander Wang --- sound/soc/intel/skylake/skl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 8d48cd7c56c835..9a9688dcf26010 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -122,7 +122,7 @@ struct skl_machine_pdata { u32 dmic_num; bool use_tplg_pcm; /* use dais and dai links from topology */ const char *platform; - u32 codec_mask; + unsigned long codec_mask; }; struct skl_dsp_ops { From 32f5efe82318c88b52f299eae577e2dddbc90436 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 27 Sep 2018 16:30:21 +0800 Subject: [PATCH 231/298] ASOC: Intel: refine hda machine driver to make it work with SOF Signed-off-by: Rander Wang Signed-off-by: Mengdong Lin --- sound/soc/intel/boards/Kconfig | 7 +++++-- sound/soc/intel/boards/skl_hda_dsp_generic.c | 21 ++++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index ce36a535ac0408..c52fbbb0712e55 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -326,6 +326,10 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH create an alsa sound card for DA7219 + MAX98357A I2S audio codec. Say Y if you have such a device. +endif ## SND_SOC_INTEL_SKYLAKE + +if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_HDA + config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH tristate "SKL/KBL/BXT/APL with HDA Codecs" select SND_SOC_HDAC_HDMI @@ -335,8 +339,7 @@ config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH SKL/KBL/BXT/APL with iDisp, HDA audio codecs. 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 ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_HDA if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_CANNONLAKE diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index b213e9b47505fb..6f9382db9818c5 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "../../codecs/hdac_hdmi.h" #include "../skylake/skl.h" #include "skl_hda_dsp_common.h" @@ -101,16 +102,16 @@ static struct snd_soc_card hda_soc_card = { #define IDISP_ROUTE_COUNT (IDISP_DAI_COUNT * 2) #define IDISP_CODEC_MASK 0x4 -static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) +static int skl_hda_fill_card_info(const char *platform, + unsigned long codec_mask) { struct snd_soc_card *card = &hda_soc_card; - u32 codec_count, codec_mask; + u32 codec_count; int i, num_links, num_route; - codec_mask = pdata->codec_mask; codec_count = hweight_long(codec_mask); - if (codec_count == 1 && pdata->codec_mask & IDISP_CODEC_MASK) { + if (codec_count == 1 && codec_mask & IDISP_CODEC_MASK) { num_links = IDISP_DAI_COUNT; num_route = IDISP_ROUTE_COUNT; } else if (codec_count == 2 && codec_mask & IDISP_CODEC_MASK) { @@ -126,17 +127,22 @@ static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) card->num_dapm_routes = num_route; for (i = 0; i < num_links; i++) - skl_hda_be_dai_links[i].platform_name = pdata->platform; + skl_hda_be_dai_links[i].platform_name = platform; return 0; } static int skl_hda_audio_probe(struct platform_device *pdev) { - struct skl_machine_pdata *pdata; struct skl_hda_private *ctx; int ret; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) + struct snd_sof_pdata *pdata = dev_get_platdata(&pdev->dev); +#else + struct skl_machine_pdata *pdata = dev_get_drvdata(&pdev->dev); +#endif + dev_dbg(&pdev->dev, "%s: entry\n", __func__); ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); @@ -145,11 +151,10 @@ static int skl_hda_audio_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ctx->hdmi_pcm_list); - pdata = dev_get_drvdata(&pdev->dev); if (!pdata) return -EINVAL; - ret = skl_hda_fill_card_info(pdata); + ret = skl_hda_fill_card_info(pdata->platform, pdata->codec_mask); if (ret < 0) { dev_err(&pdev->dev, "Unsupported HDAudio/iDisp configuration found\n"); return ret; From 1a7503ce6ab1e88d8427f3c747867d9896b78e4f Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 28 Sep 2018 15:58:56 +0800 Subject: [PATCH 232/298] ASoC: SOF: hda: fix posbuf not updated issue The upper 32bits of posbuf address was not set and lead to posbuf not updated at capture, which lead to underrun when DPIB mode is selected. Here add configuring to it and fix the issue. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-stream.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index cfdc8a629affbe..a9189058f5dfc4 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -476,10 +476,13 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, /* enable position buffer */ if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) - & SOF_HDA_ADSP_DPLBASE_ENABLE)) + & SOF_HDA_ADSP_DPLBASE_ENABLE)) { + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, + upper_32_bits(bus->posbuf.addr)); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, (u32)bus->posbuf.addr | SOF_HDA_ADSP_DPLBASE_ENABLE); + } /* set interrupt enable bits */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, From c6e4dba3075f5a1ecb4541108affeacc1b89b0e0 Mon Sep 17 00:00:00 2001 From: Marcin Maka Date: Wed, 3 Oct 2018 21:13:52 +0200 Subject: [PATCH 233/298] ASoC: SOF: dsp: initialize traces early Early initialization of dma-traces enables output from the topology creation. Signed-off-by: Marcin Maka --- sound/soc/sof/core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 64601c71692d3c..12a359073b82ec 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -308,6 +308,14 @@ static int sof_probe(struct platform_device *pdev) goto fw_run_err; } + /* init DMA trace */ + ret = snd_sof_init_trace(sdev); + if (ret < 0) { + /* non fatal */ + dev_warn(sdev->dev, + "warning: failed to initialize trace %d\n", ret); + } + /* now register audio DSP platform driver and dai */ ret = snd_soc_register_component(&pdev->dev, &sdev->plat_drv, sdev->ops->drv, @@ -332,14 +340,6 @@ static int sof_probe(struct platform_device *pdev) dev_dbg(sdev->dev, "created machine %s\n", dev_name(&plat_data->pdev_mach->dev)); - /* init DMA trace */ - ret = snd_sof_init_trace(sdev); - if (ret < 0) { - /* non fatal */ - dev_warn(sdev->dev, - "warning: failed to initialize trace %d\n", ret); - } - /* autosuspend sof device */ pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); From b0f8a2bcaabe4780338ba539f2fdf7e3a8bc67db Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Sun, 30 Sep 2018 13:32:08 +0800 Subject: [PATCH 234/298] ASOC: SOF: fix kernel panic on BYT The platform device is registered in new_mach_data function on BYT. Now refine it to register devices at single point Signed-off-by: Rander Wang --- sound/soc/sof/core.c | 23 +++++++++++++++++------ sound/soc/sof/sof-acpi-dev.c | 10 ---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 12a359073b82ec..edfdc14a591829 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -232,6 +232,9 @@ static int sof_probe(struct platform_device *pdev) { struct snd_sof_pdata *plat_data = dev_get_platdata(&pdev->dev); struct snd_sof_dev *sdev; + const char *drv_name; + const void *mach; + int size; int ret; sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); @@ -326,12 +329,20 @@ static int sof_probe(struct platform_device *pdev) goto comp_err; } - /* register machine driver */ - plat_data->pdev_mach = - platform_device_register_data(sdev->dev, - plat_data->machine->drv_name, - -1, plat_data, - sizeof(*plat_data)); + /* do we need to generate any machine plat data ? */ + if (plat_data->machine->new_mach_data) { + plat_data->pdev_mach = + plat_data->machine->new_mach_data(plat_data); + } else { + drv_name = plat_data->machine->drv_name; + mach = (const void *)plat_data; + size = sizeof(*plat_data); + + /* register machine driver, pass machine info as pdata */ + plat_data->pdev_mach = + platform_device_register_data(sdev->dev, + drv_name, -1, mach, size); + } if (IS_ERR(plat_data->pdev_mach)) { ret = PTR_ERR(plat_data->pdev_mach); goto comp_err; diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index cf8e8cadd47d3b..7fe49c91c84fc2 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -255,16 +255,6 @@ static int sof_acpi_probe(struct platform_device *pdev) sof_pdata->platform = "sof-audio"; dev_set_drvdata(&pdev->dev, priv); - /* do we need to generate any machine plat data ? */ - if (mach->new_mach_data) { - sof_pdata->pdev_mach = mach->new_mach_data(sof_pdata); - - if (IS_ERR(sof_pdata->pdev_mach)) - return PTR_ERR(sof_pdata->pdev_mach); - dev_dbg(dev, "created machine %s\n", - dev_name(&sof_pdata->pdev_mach->dev)); - } - /* register sof-audio platform driver */ ret = sof_create_platform_device(priv); if (ret) { From 93853cbf8f8252ec04dd8b70328cb004d1d4566a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 4 Oct 2018 16:59:57 -0700 Subject: [PATCH 235/298] ASoC: SOF: do not restore kcontrols before system suspend PCI devices are brought back to full power before system suspend. Restoring kcontrols when the devices are resumed before system suspend interferes with the suspend sequence. Therefore, use the prepare phase of system suspend to indicate whether kcontrol values should be restored or not. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pm.c | 116 +++++++++++++++++++++++------------- sound/soc/sof/sof-pci-dev.c | 2 + sound/soc/sof/sof-priv.h | 4 ++ 3 files changed, 79 insertions(+), 43 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 78b61baad672dd..bff8d60cfeb6e0 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -23,6 +23,57 @@ #define RUNTIME_PM 1 +static int sof_restore_kcontrols(struct snd_sof_dev *sdev) +{ + struct snd_sof_control *scontrol = NULL; + int ipc_cmd, ctrl_type, sof_abi; + int ret = 0; + + /* restore kcontrol values */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + /* reset readback offset for scontrol after resuming */ + scontrol->readback_offset = 0; + + /* notify DSP of kcontrol values */ + switch (scontrol->cmd) { + case SOF_CTRL_CMD_VOLUME: + ipc_cmd = SOF_IPC_COMP_SET_VALUE; + ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; + ret = snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + ipc_cmd, ctrl_type, + scontrol->cmd); + break; + case SOF_CTRL_CMD_BINARY: + + /* Check if control data contains valid data. + * SOF_ABI_MAGIC will not match if there is no data. + */ + ipc_cmd = SOF_IPC_COMP_SET_DATA; + ctrl_type = SOF_CTRL_TYPE_DATA_SET; + sof_abi = scontrol->control_data->data->magic; + if (sof_abi == SOF_ABI_MAGIC) + ret = snd_sof_ipc_set_comp_data(sdev->ipc, + scontrol, + ipc_cmd, + ctrl_type, + scontrol->cmd); + break; + + default: + break; + } + if (ret < 0) { + dev_err(sdev->dev, + "error: failed kcontrol value set for widget: %d\n", + scontrol->comp_id); + + return ret; + } + } + + return 0; +} + static int sof_restore_pipelines(struct snd_sof_dev *sdev) { struct snd_sof_widget *swidget = NULL; @@ -30,8 +81,6 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) struct snd_sof_dai *dai; struct sof_ipc_comp_dai *comp_dai; struct sof_ipc_hdr *hdr; - struct snd_sof_control *scontrol = NULL; - int ipc_cmd, ctrl_type, sof_abi; int ret = 0; /* restore pipeline components */ @@ -128,47 +177,9 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) } } - /* restore kcontrol values */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - /* reset readback offset for scontrol after resuming */ - scontrol->readback_offset = 0; - - /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { - case SOF_CTRL_CMD_VOLUME: - ipc_cmd = SOF_IPC_COMP_SET_VALUE; - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - ret = snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd); - break; - case SOF_CTRL_CMD_BINARY: - - /* Check if control data contains valid data. - * SOF_ABI_MAGIC will not match if there is no data. - */ - ipc_cmd = SOF_IPC_COMP_SET_DATA; - ctrl_type = SOF_CTRL_TYPE_DATA_SET; - sof_abi = scontrol->control_data->data->magic; - if (sof_abi == SOF_ABI_MAGIC) - ret = snd_sof_ipc_set_comp_data(sdev->ipc, - scontrol, - ipc_cmd, - ctrl_type, - scontrol->cmd); - break; - - default: - break; - } - if (ret < 0) { - dev_err(sdev->dev, - "error: failed kcontrol value set for widget: %d\n", - scontrol->comp_id); - - return ret; - } - } + /* restore pipeline kcontrols */ + if (sdev->restore_kcontrols) + return sof_restore_kcontrols(sdev); return 0; } @@ -344,6 +355,9 @@ static int sof_suspend(struct device *dev, int runtime_suspend) "error: failed to power down DSP during suspend %d\n", ret); + /* set flag for restoring kcontrols upon resuming */ + sdev->restore_kcontrols = true; + return ret; } @@ -376,3 +390,19 @@ int snd_sof_suspend_late(struct device *dev) return sof_suspend(dev, !RUNTIME_PM); } EXPORT_SYMBOL(snd_sof_suspend_late); + +int snd_sof_prepare(struct device *dev) +{ + struct sof_platform_priv *priv = dev_get_drvdata(dev); + struct snd_sof_dev *sdev = dev_get_drvdata(&priv->pdev_pcm->dev); + + /* + * PCI devices are brought back to full power before system suspend. + * Setting this flag will prevent restoring kcontrols + * when resuming before system suspend + */ + sdev->restore_kcontrols = false; + + return 0; +} +EXPORT_SYMBOL(snd_sof_prepare); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index cd2be5f4623602..98f041247b3922 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -132,6 +132,8 @@ static const struct dev_pm_ops sof_pci_pm = { SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) .suspend_late = snd_sof_suspend_late, + .prepare = snd_sof_prepare, + }; static const struct sof_ops_table mach_ops[] = { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f35dcca6550b4a..6deb3766d9f19f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -367,6 +367,9 @@ struct snd_sof_dev { bool dtrace_is_enabled; bool dtrace_error; + /* PM */ + bool restore_kcontrols; /* restore kcontrols upon resume */ + void *private; /* core does not touch this */ }; @@ -390,6 +393,7 @@ int snd_sof_runtime_suspend(struct device *dev); int snd_sof_runtime_resume(struct device *dev); int snd_sof_resume(struct device *dev); int snd_sof_suspend(struct device *dev); +int snd_sof_prepare(struct device *dev); int snd_sof_suspend_late(struct device *dev); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); From 2a28aca029bf8ce2dd0c1bef8e09385f548b23af Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 27 Sep 2018 18:22:33 +0800 Subject: [PATCH 236/298] ASoC: Intel: Add HDMI support to bxt_pcm512x Add HDMI support to bxt_pcm512x. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bxt_pcm512x.c | 110 +++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c index 00bd50573827b1..3563ab7429c554 100644 --- a/sound/soc/intel/boards/bxt_pcm512x.c +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -29,9 +29,23 @@ #include #include #include +#include +#include "../../codecs/hdac_hdmi.h" #include "../../codecs/pcm512x.h" #include "../atom/sst-atom-controls.h" +static struct snd_soc_jack broxton_hdmi[3]; + +struct bxt_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct bxt_card_private { + struct list_head hdmi_pcm_list; +}; + static const struct snd_soc_dapm_widget dapm_widgets[] = { SND_SOC_DAPM_SPK("Ext Spk", NULL), }; @@ -102,6 +116,24 @@ static const struct snd_soc_ops aif1_ops = { .shutdown = aif1_shutdown, }; +static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct bxt_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct bxt_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + static struct snd_soc_dai_link dailink[] = { /* CODEC<->CODEC link */ /* back ends */ @@ -121,8 +153,76 @@ static struct snd_soc_dai_link dailink[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, + { + .name = "iDisp1", + .id = 1, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 2, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 3, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + }; +#define NAME_SIZE 32 +static int bxt_card_late_probe(struct snd_soc_card *card) +{ + struct bxt_card_private *ctx = snd_soc_card_get_drvdata(card); + struct bxt_hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &broxton_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &broxton_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + /* SoC card */ static struct snd_soc_card bxt_pcm512x_card = { .name = "bxt-pcm512x", @@ -133,6 +233,7 @@ static struct snd_soc_card bxt_pcm512x_card = { .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), + .late_probe = bxt_card_late_probe, }; /* i2c-:00 with HID being 8 chars */ @@ -142,10 +243,17 @@ static int bxt_pcm512x_probe(struct platform_device *pdev) { struct snd_soc_card *card; struct snd_soc_acpi_mach *mach; + struct bxt_card_private *ctx; const char *i2c_name = NULL; int dai_index = 0; int ret_val = 0, i; + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + mach = (&pdev->dev)->platform_data; card = &bxt_pcm512x_card; card->dev = &pdev->dev; @@ -166,6 +274,8 @@ static int bxt_pcm512x_probe(struct platform_device *pdev) dailink[dai_index].codec_name = codec_name; } + snd_soc_card_set_drvdata(card, ctx); + ret_val = devm_snd_soc_register_card(&pdev->dev, card); if (ret_val) { dev_err(&pdev->dev, From 856bb51dec763955f99f8df1bab8daf9cb91cba0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Oct 2018 14:32:54 -0700 Subject: [PATCH 237/298] ASoC: SOF: set up memory windows only at first boot This will also prevent the debugfs entry creation errors seen every time the device resumes from runtime suspend or system suspend. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-ipc.c | 3 ++- sound/soc/sof/loader.c | 2 ++ sound/soc/sof/sof-priv.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index e00a8ccfaefbe5..dc2df7a99d03f2 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -389,7 +389,8 @@ int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) snd_sof_fw_parse_ext_data(sdev, HDA_DSP_MBOX_UPLINK_OFFSET + sizeof(struct sof_ipc_fw_ready)); - ipc_get_windows(sdev); + if (sdev->first_boot) + ipc_get_windows(sdev); return 0; } diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 9a90045fceef66..e30531026209e9 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -269,6 +269,8 @@ int snd_sof_load_firmware(struct snd_sof_dev *sdev, { dev_dbg(sdev->dev, "loading firmware\n"); + sdev->first_boot = first_boot; + if (sdev->ops->load_firmware) return sdev->ops->load_firmware(sdev, first_boot); return 0; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6deb3766d9f19f..c5896d9ec26a22 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -369,6 +369,7 @@ struct snd_sof_dev { /* PM */ bool restore_kcontrols; /* restore kcontrols upon resume */ + bool first_boot; void *private; /* core does not touch this */ }; From 33095294ea6d00f738f23eacb835505361f6b233 Mon Sep 17 00:00:00 2001 From: Zhu Yingjiang Date: Tue, 9 Oct 2018 10:49:48 +0800 Subject: [PATCH 238/298] ASoC:SOF:skl add cldma copy firmware use code loader DMA to copy firmware binary to DSP Signed-off-by: Zhu Yingjiang --- sound/soc/sof/intel/hda-loader-skl.c | 146 ++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c index df9b1e4db13cb2..c5cce851dabf4b 100644 --- a/sound/soc/sof/intel/hda-loader-skl.c +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -28,7 +28,14 @@ #include "../ops.h" #include "hda.h" -#define SKL_MAX_BUFFER_SIZE (32 * PAGE_SIZE) +#define HDA_SKL_WAIT_TIMEOUT 500 /* 500 msec */ +#define HDA_SKL_CLDMA_MAX_BUFFER_SIZE (32 * PAGE_SIZE) + +/* Intel HD Audio SRAM Window 0*/ +#define HDA_SKL_ADSP_SRAM0_BASE 0x8000 + +/* Firmware status window */ +#define HDA_SKL_ADSP_FW_STATUS HDA_SKL_ADSP_SRAM0_BASE /* Stream Reset */ #define HDA_CL_SD_CTL_SRST_SHIFT 0 @@ -218,6 +225,14 @@ static void cl_skl_cldma_setup_spb(struct snd_sof_dev *sdev, sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB, size); } +static void cl_skl_cldma_set_intr(struct snd_sof_dev *sdev, bool enable) +{ + u32 val = enable ? HDA_DSP_ADSPIC_CL_DMA : 0; + + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_CL_DMA, val); +} + static void cl_skl_cldma_cleanup_spb(struct snd_sof_dev *sdev) { int sd_offset = SOF_DSP_REG_CL_SPBFIFO; @@ -273,7 +288,7 @@ static int cl_stream_prepare_skl(struct snd_sof_dev *sdev) int frags = 0; int ret = 0; u32 *bdl; - unsigned int bufsize = SKL_MAX_BUFFER_SIZE; + unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE; ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, bufsize, &sdev->dmab); @@ -380,6 +395,7 @@ static int cl_dsp_init_skl(struct snd_sof_dev *sdev) return ret; } +static int cl_copy_fw_skl(struct snd_sof_dev *sdev); int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) { int ret; @@ -406,6 +422,17 @@ int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) init_waitqueue_head(&sdev->boot_wait); sdev->boot_complete = false; + /* at this point DSP ROM has been initialized and should be ready for + * code loading and firmware boot + */ + ret = cl_copy_fw_skl(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: load firmware failed : %d\n", ret); + goto irq_err; + } + + dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); + return ret; irq_err: @@ -418,3 +445,118 @@ int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); return ret; } + +int cl_skl_cldma_wait_interruptible(struct snd_sof_dev *sdev) +{ + int ret = 0; + + if (!wait_event_timeout(sdev->waitq, + sdev->code_loading, + msecs_to_jiffies(HDA_SKL_WAIT_TIMEOUT))) { + dev_err(sdev->dev, "%s: Wait timeout\n", __func__); + ret = -EIO; + goto cleanup; + } + + dev_dbg(sdev->dev, "%s: Event wake\n", __func__); + if (sdev->code_loading != 1) { + dev_err(sdev->dev, "%s: DMA Error\n", __func__); + ret = -EIO; + } + +cleanup: + sdev->code_loading = 0; + return ret; +} + +static void cl_skl_cldma_fill_buffer(struct snd_sof_dev *sdev, + unsigned int bufsize, + unsigned int copysize, + const void *curr_pos, + bool intr_enable, bool trigger) +{ + /* 1. copy the image into the buffer with the maximum buffer size. */ + unsigned int size = (bufsize == copysize) ? bufsize : copysize; + + memcpy(sdev->dmab.area, curr_pos, size); + + /* 2. Setting the wait condition for every load. */ + sdev->code_loading = 0; + + /* 3. Set the interrupt. */ + if (intr_enable) + cl_skl_cldma_set_intr(sdev, true); + + /* 4. Set the SPB. */ + cl_skl_cldma_setup_spb(sdev, size, trigger); + + /* 5. Trigger the code loading stream. */ + if (trigger) + cl_skl_cldma_stream_run(sdev, true); +} + +static int +cl_skl_cldma_copy_to_buf(struct snd_sof_dev *sdev, const void *bin, + u32 total_size, u32 bufsize) +{ + int ret = 0; + unsigned int bytes_left = total_size; + const void *curr_pos = bin; + + if (total_size <= 0) + return -EINVAL; + + dev_dbg(sdev->dev, "Total binary size: %u\n", total_size); + + while (bytes_left > 0) { + if (bytes_left > bufsize) { + cl_skl_cldma_fill_buffer(sdev, bufsize, bufsize, + curr_pos, true, true); + ret = cl_skl_cldma_wait_interruptible(sdev); + if (ret < 0) { + cl_skl_cldma_stream_run(sdev, false); + return ret; + } + bytes_left -= bufsize; + curr_pos += bufsize; + } else { + cl_skl_cldma_set_intr(sdev, false); + cl_skl_cldma_fill_buffer(sdev, bufsize, bytes_left, + curr_pos, false, true); + return 0; + } + } + + return bytes_left; +} + +static int cl_copy_fw_skl(struct snd_sof_dev *sdev) +{ + struct skl_ext_manifest_hdr *hdr; + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + struct firmware stripped_fw; + unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE; + int ret = 0; + + stripped_fw.data = plat_data->fw->data; + stripped_fw.size = plat_data->fw->size; + dev_dbg(sdev->dev, "firmware size: %zu\n", stripped_fw.size); + + ret = cl_skl_cldma_copy_to_buf(sdev, stripped_fw.data, + stripped_fw.size, bufsize); + if (ret < 0) + return ret; + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_SKL_ADSP_FW_STATUS, + HDA_DSP_ROM_STS_MASK, + HDA_DSP_ROM_FW_FW_LOADED, + HDA_DSP_BASEFW_TIMEOUT); + + if (ret < 0) + dev_err(sdev->dev, "firmware transfer timeout!"); + + cl_skl_cldma_stream_run(sdev, false); + cl_cleanup_skl(sdev); + + return ret; +} From bf67a28d6732fe0f7c5e50c9c0273d97b34f2ab1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Oct 2018 22:26:22 -0700 Subject: [PATCH 239/298] ASoC: SOF: stop trace dma during suspend and re-init upon resume Previously, during pm suspend, trace dma was not stopped and this led to failures with the system entering S3. This patch fixes the issue. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index bff8d60cfeb6e0..345ef79e784fa2 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -287,7 +287,7 @@ static int sof_resume(struct device *dev, int runtime_resume) } /* init DMA trace */ - ret = snd_sof_init_trace_ipc(sdev); + ret = snd_sof_init_trace(sdev); if (ret < 0) { /* non fatal */ dev_warn(sdev->dev, @@ -324,6 +324,9 @@ static int sof_suspend(struct device *dev, int runtime_suspend) if (!sdev->ops->suspend) return 0; + /* release trace */ + snd_sof_release_trace(sdev); + /* * Suspend running pcm streams. * They will be restarted by ALSA resume trigger call. @@ -342,9 +345,6 @@ static int sof_suspend(struct device *dev, int runtime_suspend) /* drop all ipc */ sof_ipc_drop_all(sdev->ipc); - /* release trace */ - snd_sof_dma_trace_release(sdev); - /* power down DSP */ if (runtime_suspend) ret = snd_sof_dsp_runtime_suspend(sdev, 0); From 5300a52f89c72277a55d21aacf48cc2114dcfd1b Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 12 Oct 2018 17:47:44 +0800 Subject: [PATCH 240/298] ASoC: SOF: switch to unset force_ipc_position by default It is ready for DPIB mode, let's select it as default mode Signed-off-by: Keyon Jie --- sound/soc/sof/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index aab154cbf1a6f7..8db9ce50a29018 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -71,7 +71,7 @@ config SND_SOC_SOF_DEBUG_VERBOSE_IPC config SND_SOC_SOF_DEBUG_FORCE_IPC_POSITION bool "SOF force to use IPC for position update on SKL+" depends on SND_SOC_SOF_DEBUG - default y + default n help This option force to handle stream position update IPCs and run pcm elapse to inform ALSA about that, on platforms (e.g. Intel SKL+) that From c250eb3a4289638635d029f67106d04528d4c753 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Sun, 30 Sep 2018 15:47:56 +0800 Subject: [PATCH 241/298] ASOC: SOF: add a workaround to detect HDA HDMI codecs The initialization process of HDA in SOF is: (1) init HDA to set up a context for i915 initialization then stop HDA bus. (2) init i915 to init HDMI codecs. (3) init HDA again to make everything ready On WHL, only HDA analog codec is detected in step (1). And after step (2) HDMI could be detected. But in function azx_reset for step(3), the detection code is: /* detect codecs */ if (!bus->codec_mask) { bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); } At step (3) codec_mask is not zero, so no more query would be triggered. This results to miss detecting HDMI codecs. Now set codec_mask to zero to query again For GLK, none codec is detected at step(1), so it is no problem at step(3) Signed-off-by: Rander Wang --- sound/soc/sof/intel/hda.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 2e724d4efb638b..aa06a1ee897c44 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -388,7 +388,10 @@ static int hda_init_caps(struct snd_sof_dev *sdev) struct hdac_ext_link *hlink = NULL; int ret = 0; - // FIXME: we do this a lot ! + /* The initialization process of HDA in SOF is: + * (1) init HDA to set up a context for i915 initialization + * then stop HDA bus. + */ hda_dsp_ctrl_init_chip(sdev, true); device_disable_async_suspend(bus->dev); @@ -402,14 +405,24 @@ static int hda_init_caps(struct snd_sof_dev *sdev) snd_hdac_bus_stop_chip(bus); - /* probe i915 and HDA codecs */ + /* (2) probe i915 and HDA codecs, HDMI codecs */ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { ret = hda_codec_i915_init(sdev); if (ret < 0) return ret; } - // FIXME: we do this a lot ! + /* only HDA analog codec is detected in step (1) and + * codec_mask would be set. At step(3) HDMI codec can't + * be detected because HDA framework only check codecs + * when codec_mask is zero. So now set codec_mask to zero + * to force HDA framework to check codecs again. + */ + bus->codec_mask = 0; + + /* (3) init HDA again to make everything ready so that all + * the codecs can be detected. + */ ret = hda_dsp_ctrl_init_chip(sdev, true); if (ret < 0) { dev_err(bus->dev, "Init chip failed with ret: %d\n", ret); From 4a0e72f4ae9588a94e8802016ee66610fde5edb7 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 17 Oct 2018 17:46:34 +0800 Subject: [PATCH 242/298] ASoC: SOF: fix trace resume fails issue We should not create debugFW entry for resume, here fix it. Signed-off-by: Keyon Jie --- sound/soc/sof/trace.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index bd72a12bc7a6ac..5564bd4f339865 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -226,9 +226,11 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) sdev->dma_trace_pages = ret; dev_dbg(sdev->dev, "dma_trace_pages: %d\n", sdev->dma_trace_pages); - ret = trace_debugfs_create(sdev); - if (ret < 0) - goto table_err; + if (sdev->first_boot) { + ret = trace_debugfs_create(sdev); + if (ret < 0) + goto table_err; + } ret = snd_sof_init_trace_ipc(sdev); if (ret < 0) From 481973cf6ca7ac2af9e246fe2f2daa09254cc112 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Mon, 17 Sep 2018 16:52:59 +0800 Subject: [PATCH 243/298] ASoC: SOF: add sof vBE and vFE support This patch adds the sof virtio BE and FE support. It will create a dev node for the communication to the userspace. It also handles the virtio vq event. It dispatches the different vq kicks to different handlers. In the virtio vq handling, it handles all the ipc events from vFE. When there is position update from FW, it will check whether there is an available entry in notification vq. If yes, send the position update notification immediately. If there is no available entry, add the position update event in a list. As soon as the notification vq has an available entry, send the position update to the vFE. For vFE, it will create a virtual audio device driver and communite with vBE audio driver to create PCMs and playback/capture streams. Signed-off-by: Libin Yang --- include/sound/sof.h | 4 + include/uapi/sound/sof-virtio.h | 42 + sound/soc/intel/boards/bxt_tdf8532.c | 12 + sound/soc/sof/Makefile | 10 + sound/soc/sof/core.c | 33 + sound/soc/sof/ipc.c | 3 + sound/soc/sof/pcm.c | 1 + sound/soc/sof/sof-priv.h | 94 +++ sound/soc/sof/virtio-be.c | 1144 ++++++++++++++++++++++++-- sound/soc/sof/virtio-be.h | 51 ++ sound/soc/sof/virtio-fe.c | 430 ++++++++-- sound/soc/sof/virtio-fe.h | 28 + sound/soc/sof/virtio-miscdev.c | 252 ++++++ sound/soc/sof/virtio-miscdev.h | 24 + 14 files changed, 2000 insertions(+), 128 deletions(-) create mode 100644 include/uapi/sound/sof-virtio.h create mode 100644 sound/soc/sof/virtio-be.h create mode 100644 sound/soc/sof/virtio-fe.h create mode 100644 sound/soc/sof/virtio-miscdev.c create mode 100644 sound/soc/sof/virtio-miscdev.h diff --git a/include/sound/sof.h b/include/sound/sof.h index 1b0bfc295a78fc..9313b4f74cc9ba 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -22,6 +22,7 @@ #include struct snd_sof_dsp_ops; +struct virtio_device; /* SOF probe type */ enum sof_device_type { @@ -53,6 +54,9 @@ struct snd_sof_pdata { struct device *dev; enum sof_device_type type; + struct sof_vfe *vfe; + int is_vfe; + /* descriptor */ const struct sof_dev_desc *desc; diff --git a/include/uapi/sound/sof-virtio.h b/include/uapi/sound/sof-virtio.h new file mode 100644 index 00000000000000..2e4ac66453b6e6 --- /dev/null +++ b/include/uapi/sound/sof-virtio.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Contact Information: + * Author: Luo Xionghu + * Liam Girdwood . + */ + +#ifndef _SOF_VIRTIO_H +#define _SOF_VIRTIO_H + +/* Currently we defined 4 vqs to do the IPC, CMD_TX is for send the msg + * from FE to BE, and CMD_RX is to receive the reply. NOT_RX is to receive + * the notification, and NOT_TX is to send empty buffer from FE to BE. + * If we can handle the IPC with only 2 vqs, the config still need to + * be changed in the device model(VM config), then only CMD_VQ and NOT_VQ + * is needed. + */ + +#define SOF_VIRTIO_IPC_CMD_TX_VQ 0 +#define SOF_VIRTIO_IPC_CMD_RX_VQ 1 +#define SOF_VIRTIO_IPC_NOT_TX_VQ 2 +#define SOF_VIRTIO_IPC_NOT_RX_VQ 3 +#define SOF_VIRTIO_NUM_OF_VQS 4 + +/* command messages from FE to BE, trigger/open/hw_params and so on */ +#define SOF_VIRTIO_IPC_CMD_TX_VQ_NAME "sof-ipc-cmd-tx" + +/* get the reply of the command message */ +#define SOF_VIRTIO_IPC_CMD_RX_VQ_NAME "sof-ipc-cmd-rx" + +/* first the FE need send empty buffer to BE to get the notification */ +#define SOF_VIRTIO_IPC_NOT_TX_VQ_NAME "sof-ipc-not-tx" + +/* the vq to get the notification */ +#define SOF_VIRTIO_IPC_NOT_RX_VQ_NAME "sof-ipc-not-rx" + +#endif diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 336205309e2c8b..4ccd013c6e5710 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -205,6 +205,18 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .dpcm_playback = 1, .no_pcm = 1, }, + { + /* SSP4 for vm */ + .name = "vm_dai_link", + .id = 6, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "i2c-INT34C3:00", + .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, }; #if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index b8f6c67fbb7b70..934ae19f258fd1 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -4,6 +4,15 @@ ccflags-y += -DDEBUG snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o compressed.o utils.o + +ifdef CONFIG_SND_SOC_SOF_VIRTIO_BE +snd-sof-objs += virtio-be.o virtio-miscdev.o +endif + +ifdef CONFIG_SND_SOC_SOF_VIRTIO_FE +snd-virtio-fe-objs += virtio-fe.o +endif + snd-sof-spi-objs := hw-spi.o snd-sof-pci-objs := sof-pci-dev.o @@ -16,6 +25,7 @@ obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF_ACPI) += sof-acpi-dev.o obj-$(CONFIG_SND_SOC_SOF_PCI) += sof-pci-dev.o obj-$(CONFIG_SND_SOC_SOF_SPI) += sof-spi-dev.o +obj-$(CONFIG_SND_SOC_SOF_VIRTIO_FE) += virtio-fe.o obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index edfdc14a591829..11d9c8befd6846 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -81,6 +81,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, return NULL; } +EXPORT_SYMBOL(snd_sof_find_spcm_comp); struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, unsigned int pcm_id) @@ -224,6 +225,25 @@ int snd_sof_create_page_table(struct snd_sof_dev *sdev, return pages; } +static void sof_virtio_vfe_init(struct snd_sof_dev *sdev, + struct snd_sof_pdata *plat_data) +{ + sdev->is_vfe = plat_data->is_vfe; + + /* + * Currently we only support one VM. comp_id from 0 to + * SOF_VIRTIO_MAX_GOS_COMPS - 1 is for SOS. Other comp_id numbers + * are for VM1. + * TBD: comp_id number range should be dynamically assigned when + * multiple VMs are supported. + */ + if (sdev->is_vfe) { + sdev->next_comp_id = SOF_VIRTIO_MAX_GOS_COMPS; + sdev->vfe = plat_data->vfe; + sdev->vfe->sdev = sdev; + } +} + /* * SOF Driver enumeration. */ @@ -256,6 +276,7 @@ static int sof_probe(struct platform_device *pdev) INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->route_list); + INIT_LIST_HEAD(&sdev->vbe_list); dev_set_drvdata(&pdev->dev, sdev); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); @@ -288,6 +309,9 @@ static int sof_probe(struct platform_device *pdev) goto dbg_err; } + /* optionally register virtio miscdev */ + sof_virtio_miscdev_register(sdev); + /* init the IPC */ sdev->ipc = snd_sof_ipc_init(sdev); if (!sdev->ipc) { @@ -295,6 +319,12 @@ static int sof_probe(struct platform_device *pdev) goto ipc_err; } + sof_virtio_vfe_init(sdev, plat_data); + + /* vFE will not touch HW. Let's skip fw loading */ + if (sdev->is_vfe) + goto skip_load_fw_and_trace; + /* load the firmware */ ret = snd_sof_load_firmware(sdev, true); if (ret < 0) { @@ -319,6 +349,8 @@ static int sof_probe(struct platform_device *pdev) "warning: failed to initialize trace %d\n", ret); } +skip_load_fw_and_trace: + /* now register audio DSP platform driver and dai */ ret = snd_soc_register_component(&pdev->dev, &sdev->plat_drv, sdev->ops->drv, @@ -383,6 +415,7 @@ static int sof_remove(struct platform_device *pdev) snd_soc_unregister_component(&pdev->dev); snd_sof_fw_unload(sdev); snd_sof_ipc_free(sdev); + sof_virtio_miscdev_unregister(); snd_sof_free_debug(sdev); snd_sof_release_trace(sdev); snd_sof_remove(sdev); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 4ee63ce4dd2b11..80862ad1919a80 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -535,6 +535,9 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); + /* optionally update position for vBE */ + sof_vbe_update_guest_posn(sdev, &posn); + /* only inform ALSA for period_wakeup mode */ if (!spcm->stream[direction].substream->runtime->no_period_wakeup) snd_pcm_period_elapsed(spcm->stream[direction].substream); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index e1b53594434a0c..930ba3f03942c6 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -691,6 +691,7 @@ static int sof_pcm_probe(struct snd_soc_component *component) /* load the default topology */ sdev->component = component; + sdev->card = component->card; switch (plat_data->type) { case SOF_DEVICE_SPI: diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c5896d9ec26a22..4cf797bd09bbef 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,11 @@ #define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) +/* The maximum number of components a virtio guest vFE driver can use */ +#define SOF_VIRTIO_MAX_GOS_COMPS 1000 + +#define SOF_VIRTIO_COMP_ID_UNASSIGNED 0xffffffff + struct snd_sof_dev; struct snd_sof_ipc_msg; struct snd_sof_ipc; @@ -62,6 +68,8 @@ struct snd_soc_tplg_ops; struct snd_soc_component; struct sof_intel_hda_dev; struct snd_sof_pdata; +struct virtio_device; +struct virtqueue; /* * SOF DSP HW abstraction operations. @@ -290,6 +298,47 @@ struct snd_sof_dai { struct list_head list; /* list in sdev dai list */ }; +/* + * in virtio iovec array: + * iovec[0]: the ipc message data between vFE and vBE + * iovec[1]: the ipc reply data between vFE and vBE + */ +#define SOF_VIRTIO_IPC_MSG 0 +#define SOF_VIRTIO_IPC_REPLY 1 + +/* Virtio Frontend */ +struct sof_vfe { + struct sof_virtio_priv *priv; + struct snd_sof_dev *sdev; + + /* IPC cmd from frontend to backend */ + struct virtqueue *ipc_cmd_tx_vq; + + /* IPC cmd reply from backend to frontend */ + struct virtqueue *ipc_cmd_rx_vq; + + /* IPC notification from backend to frontend */ + struct virtqueue *ipc_not_rx_vq; + + /* IPC notification reply from frontend to backend */ + struct virtqueue *ipc_not_tx_vq; + + /* position update work */ + struct work_struct posn_update_work; + + /* current pending cmd message */ + struct snd_sof_ipc_msg *msg; + + /* current and pending notification */ + struct snd_sof_ipc_msg *not; + struct sof_ipc_stream_posn *posn; +}; + +struct vbs_sof_posn { + struct list_head list; + struct sof_ipc_stream_posn pos; +}; + /* * SOF Device Level. */ @@ -302,6 +351,7 @@ struct snd_sof_dev { /* ASoC components */ struct snd_soc_component_driver plat_drv; + struct snd_soc_card *card; /* DSP firmware boot */ wait_queue_head_t boot_wait; @@ -358,6 +408,11 @@ struct snd_sof_dev { wait_queue_head_t waitq; int code_loading; + /* virtio for BE and FE */ + struct list_head vbe_list; + struct sof_vfe *vfe; + int is_vfe; + /* DMA for Trace */ struct snd_dma_buffer dmatb; struct snd_dma_buffer dmatp; @@ -377,6 +432,29 @@ struct snd_sof_dev { #define sof_to_bus(s) (&(s)->hda->hbus.core) #define sof_to_hbus(s) (&(s)->hda->hbus) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_VIRTIO_BE) +int sof_virtio_miscdev_register(struct snd_sof_dev *sdev); +int sof_virtio_miscdev_unregister(void); +int sof_vbe_update_guest_posn(struct snd_sof_dev *sdev, + struct sof_ipc_stream_posn *posn); +#else +static inline int sof_virtio_miscdev_register(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int sof_virtio_miscdev_unregister(void) +{ + return 0; +} + +static inline int sof_vbe_update_guest_posn(struct snd_sof_dev *sdev, + struct sof_ipc_stream_posn *posn) +{ + return 0; +} +#endif + /* * SOF platform private struct used as drvdata of * platform dev (e.g. pci/acpi/spi...) drvdata. @@ -494,10 +572,26 @@ int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, void *stack, size_t stack_size); int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); +/* + * VirtIO + */ +int sof_virtio_submit_guest_ipc(struct snd_sof_dev *sdev, int vm_id, + void *ipc_buf, void *reply_buf, + size_t count, size_t reply_sz); +int snd_sof_virtio_fs_init(struct snd_sof_dev *sdev); +int snd_sof_virtio_fs_release(void); +int sof_virtio_update_guest_posn(void *ctx, struct sof_ipc_stream_posn *posn); +int sof_virtio_try_update_guest_posn(struct snd_sof_dev *sdev, + struct sof_ipc_stream_posn *posn); +void sof_virtio_set_spcm_posn_offset(struct snd_sof_pcm *spcm, int direction); +int sof_virtio_register_guest(void *ctx); +int sof_virtio_release_guest(int id); + /* * Platform specific ops. */ extern struct snd_compr_ops sof_compressed_ops; +extern struct snd_sof_dsp_ops snd_sof_virtio_fe_ops; /* * Kcontrols. diff --git a/sound/soc/sof/virtio-be.c b/sound/soc/sof/virtio-be.c index 4c07caeba41b0c..84b7ed8f01b3e4 100644 --- a/sound/soc/sof/virtio-be.c +++ b/sound/soc/sof/virtio-be.c @@ -5,122 +5,1122 @@ * * Copyright(c) 2017 Intel Corporation. All rights reserved. * - * Author: Luo Xionghu + * Author: Libin Yang + * Luo Xionghu * Liam Girdwood - * */ -#include +#include +#include #include +#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "sof-priv.h" #include "ops.h" +#include "virtio-be.h" +#include "virtio-miscdev.h" -/* BE driver - * - * This driver will create IO Queues for communition from FE drivers. - * The FE driver will send real IPC structures over the queue and then - * the BE driver will send the structures directlt to the DSP. The BE will - * get the IPC reply from the DSP and send it back to the FE over the queue. - * - * The virt IO message Q handlers in this file will :- - * - * 1) Check that the message is valid and not for any componenets that don't - * belong to the guest. - * - * 2) Call snd_sof_dsp_tx_msg(struct snd_sof_dev *sdev, - * struct snd_sof_ipc_msg *msg) to send the message to the DSP. - * - * Replies will be sent back using a similar method. +#define IGS(x) (((x) >> SOF_GLB_TYPE_SHIFT) & 0xf) +#define ICS(x) (((x) >> SOF_CMD_TYPE_SHIFT) & 0xfff) + +/* find client from client ID */ +static struct sof_vbe_client *vbe_client_find(struct snd_sof_dev *sdev, + int client_id) +{ + struct sof_vbe_client *client; + struct sof_vbe *vbe; + + list_for_each_entry(vbe, &sdev->vbe_list, list) { + list_for_each_entry(client, &vbe->client_list, list) { + if (client_id == client->vhm_client_id) + return client; + } + } + + return NULL; +} + +static struct sof_vbe *sbe_comp_id_to_vbe(struct snd_sof_dev *sdev, int comp_id) +{ + struct sof_vbe *vbe; + + list_for_each_entry(vbe, &sdev->vbe_list, list) { + if (comp_id < vbe->comp_id_end && comp_id >= vbe->comp_id_begin) + return vbe; + } + return NULL; +} + +static int sof_virtio_send_ipc(struct snd_sof_dev *sdev, void *ipc_data, + void *reply_data, size_t count, + size_t reply_size) +{ + struct snd_sof_ipc *ipc = sdev->ipc; + struct sof_ipc_hdr *hdr = (struct sof_ipc_hdr *)ipc_data; + + return sof_ipc_tx_message(ipc, hdr->cmd, ipc_data, count, + reply_data, reply_size); +} + +/* + * This function will be called when there is a poistion update requirement + * from vBE. Return true if posn buffer is filled successfully + */ +static bool sbe_fill_posn_vqbuf(struct sof_vbe *vbe, + struct virtio_vq_info *vq, + struct sof_ipc_stream_posn *posn, + bool *endchain) +{ + struct device *dev = vbe->sdev->dev; + struct iovec iov; + u16 idx; + int ret; + + *endchain = false; + + /* + * There is a position update requirement, let's get + * an empty buffer and fill it. + */ + while (virtio_vq_has_descs(vq)) { + ret = virtio_vq_getchain(vq, &idx, &iov, 1, NULL); + if (ret <= 0) + return false; + + /* The buffer is bad. Don't use it. Just release it. */ + if (iov.iov_len < sizeof(struct sof_ipc_stream_posn)) { + /* we need call endchain() later */ + *endchain = true; + dev_err(dev, "iov len %lu, expecting len %lu\n", + iov.iov_len, sizeof(*posn)); + virtio_vq_relchain(vq, idx, iov.iov_len); + continue; + } + + /* Get a valid buffer. Let's fill the data and kick back */ + memcpy(iov.iov_base, posn, sizeof(struct sof_ipc_stream_posn)); + *endchain = true; + virtio_vq_relchain(vq, idx, iov.iov_len); + return true; + } + + return false; +} + +/* + * IPC notification reply from vFE to vBE + * This function is called when vFE has queued an empty buffer to vBE */ +static void sbe_ipc_fe_not_reply_get(struct sof_vbe *vbe, int vq_idx) +{ + struct virtio_vq_info *vq; + struct vbs_sof_posn *entry; + unsigned long flags; + bool endchain; + + dev_dbg(vbe->sdev->dev, + "audio BE notification vq kick handling, vq_idx %d\n", vq_idx); + + spin_lock_irqsave(&vbe->posn_lock, flags); + + /* + * Now we have gotten an empty buffer from vFE. This buffer is used + * to send the position update information to vFE. + * Let's check if there is position update requirement from vBE. + * If yes, let's kick back the position information to vFE. + * If there is no position update requirement from vBE, let's untouch + * the buffer and keep the buffer in the vq for later use. + */ + if (list_empty(&vbe->posn_list)) { + /* + * No position update requirement. Don't touch the buffer and + * Keep the buffer in the vq + */ + spin_unlock_irqrestore(&vbe->posn_lock, flags); + return; + } + + vq = &vbe->vqs[vq_idx]; + entry = list_first_entry(&vbe->posn_list, + struct vbs_sof_posn, list); + list_del(&entry->list); + spin_unlock_irqrestore(&vbe->posn_lock, flags); -static int sof_virtio_validate(struct virtio_device *dev) + /* + * There are position update requirements and now we get the new + * position buffer entry. Let's try to update the position to vFE. + */ + sbe_fill_posn_vqbuf(vbe, vq, &entry->pos, &endchain); + + /* + * encchain means: we have already processed this queue + * and now let's kick back and release it. + */ + if (endchain) + virtio_vq_endchains(vq, 1); +} + +/* validate component IPC */ +static int sbe_ipc_comp(struct snd_sof_dev *sdev, int vm_id, + struct sof_ipc_hdr *hdr) { - /* do we need this func ?? */ + /*TODO validate host comp id range based on vm_id */ + + /* Nothing to be done */ return 0; } -static int sof_virtio_probe(struct virtio_device *dev) +/* + * This function is to get the BE dai link for the GOS + * It uses the dai_link name to find the BE dai link. + * The Current dai_link name "vm_dai_link" is for the GOS, + * which means only one Virtual Machine is supported. + * And the VM only support one playback pcm and one capture pcm. + * After we switch to the new topology, we can support multiple + * VMs and multiple PCM streams for each VM. + * This function may be abandoned after switching to the new topology. + */ +static struct snd_pcm_substream * +sbe_get_substream(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime **rtd, int direction) { - /* register fe device with sof core */ - //snd_sof_virtio_register_fe(dev); + struct snd_pcm *pcm; + struct snd_pcm_str *stream; + struct snd_pcm_substream *substream = NULL; + struct snd_soc_dai_link *dai_link; + struct snd_soc_card *card = sdev->card; + struct snd_soc_pcm_runtime *r; - /* create our virtqueues */s + list_for_each_entry(r, &card->rtd_list, list) { + /* + * We need to find a dedicated substream: + * pcm->streams[dir].substream which is dedicated + * used for vFE. + */ + pcm = r->pcm; + if (!pcm) + continue; - /* send topology data to fe via virtq */ + stream = &pcm->streams[direction]; + substream = stream->substream; + if (substream) { + dai_link = r->dai_link; + if (strcmp(dai_link->name, "vm_dai_link") == 0) { + /* + * In the current solution, "vm_dai_link" is + * for the vFE. + */ + if (rtd) + *rtd = r; + return substream; + } + } + } - return 0; + return NULL; } -static void sof_virtio_remove(struct virtio_device *dev) +static int sbe_pcm_open(struct snd_sof_dev *sdev, + void *ipc_data, int vm_id) { - /* remove topology from fe via virtqueue */ + /* + * TO re-use the sof callback for pcm, we should find a proper + * substream and do the correct setting for the substream. + * As there is no FE link substream in SOS for GOS (GOS FE link + * substreams are created in GOS and SOS will never see it), let's + * use BE link substream in SOS for the callbacks. + * This is save because the BE link substream is created dedicated for + * GOS in machine driver. + */ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + struct snd_sof_pcm *spcm; + struct snd_soc_pcm_runtime *rtd = NULL; + struct sof_ipc_pcm_params *pcm; + u32 comp_id; + size_t size; + int direction; + + pcm = (struct sof_ipc_pcm_params *)ipc_data; + comp_id = pcm->comp_id; + + spcm = snd_sof_find_spcm_comp(sdev, comp_id, &direction); + if (!spcm) + return -ENODEV; + + mutex_lock(&spcm->mutex); + + substream = sbe_get_substream(sdev, &rtd, direction); + if (!substream || !rtd) + return -ENODEV; + if (substream->ref_count > 0) + return -EBUSY; + substream->ref_count++; /* set it used */ + + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) + return -ENOMEM; + + size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); + runtime->status = snd_malloc_pages(size, GFP_KERNEL); + if (!runtime->status) { + kfree(runtime); + return -ENOMEM; + } + memset((void *)runtime->status, 0, size); - /* destroy virtqueue */ + size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); + runtime->control = snd_malloc_pages(size, GFP_KERNEL); + if (!runtime->control) { + dev_err(sdev->dev, "fail to alloc pages for runtime->control"); + snd_free_pages((void *)runtime->status, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); + kfree(runtime); + return -ENOMEM; + } + memset((void *)runtime->control, 0, size); + + init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->tsleep); + runtime->status->state = SNDRV_PCM_STATE_OPEN; + + substream->runtime = runtime; + substream->private_data = rtd; + rtd->sof = spcm; + substream->stream = direction; + + /* check with spcm exists or not */ + spcm->stream[direction].posn.host_posn = 0; + spcm->stream[direction].posn.dai_posn = 0; + spcm->stream[direction].substream = substream; + + /* TODO: codec open */ + + snd_sof_pcm_platform_open(sdev, substream); + + mutex_unlock(&spcm->mutex); + return 0; } -#ifdef CONFIG_PM -static int sof_virtio_freeze(struct virtio_device *dev) +static int sbe_pcm_close(struct snd_sof_dev *sdev, + void *ipc_data, int vm_id) { - /* pause and suspend any streams for this FE */ + struct snd_pcm_substream *substream; + struct snd_sof_pcm *spcm; + struct snd_soc_pcm_runtime *rtd = NULL; + struct sof_ipc_stream *stream; + u32 comp_id; + int direction; + + stream = (struct sof_ipc_stream *)ipc_data; + comp_id = stream->comp_id; + + spcm = snd_sof_find_spcm_comp(sdev, comp_id, &direction); + if (!spcm) + return 0; + mutex_lock(&spcm->mutex); + substream = sbe_get_substream(sdev, &rtd, direction); + if (!substream) { + mutex_unlock(&spcm->mutex); + return 0; + } + + snd_sof_pcm_platform_close(sdev, substream); + + /* TODO: codec close */ + + substream->ref_count = 0; + if (substream->runtime) { + snd_free_pages((void *)substream->runtime->status, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); + snd_free_pages((void *)substream->runtime->control, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); + kfree(substream->runtime); + substream->runtime = NULL; + rtd->sof = NULL; + } + mutex_unlock(&spcm->mutex); return 0; } -static int sof_virtio_restore(struct virtio_device *dev) +/* + * FIXME - this function should only convert a compressed GOS PHY page table + * into a page table of SOS physical pages. It should leave the HDA stream + * alone for HDA code to manage. + */ +static int sbe_stream_prepare(struct snd_sof_dev *sdev, + struct sof_ipc_pcm_params *pcm, int vm_id, + struct snd_sg_page *table) { - /* restore and unpause any streams for this FE */ + u32 pcm_buffer_gpa = pcm->params.buffer.phy_addr; + u64 pcm_buffer_hpa = vhm_vm_gpa2hpa(vm_id, (u64)pcm_buffer_gpa); + u8 *page_table = (uint8_t *)__va(pcm_buffer_hpa); + int idx, i; + u32 gpa_parse, pages; + u64 hpa_parse; + + pages = pcm->params.buffer.pages; + for (i = 0; i < pages; i++) { + idx = (((i << 2) + i)) >> 1; + gpa_parse = page_table[idx] | (page_table[idx + 1] << 8) + | (page_table[idx + 2] << 16); + + if (i & 0x1) + gpa_parse <<= 8; + else + gpa_parse <<= 12; + gpa_parse &= 0xfffff000; + hpa_parse = vhm_vm_gpa2hpa(vm_id, (u64)gpa_parse); + + table[i].addr = hpa_parse; + } + return 0; } -#endif -/* IDs of FEs */ -static const struct virtio_device_id *fe_id_table[] + { -}; +static int sbe_assemble_params(struct sof_ipc_pcm_params *pcm, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); -static struct virtio_driver sof_be_virtio_driver = { - .driver = { - .name = "sof-virtio-be", - .owner = THIS_MODULE, - }, + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = + pcm->params.channels; - .id_table = fe_id_table, + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min = + pcm->params.rate; - //const unsigned int *feature_table; - //unsigned int feature_table_size; - //const unsigned int *feature_table_legacy; - //unsigned int feature_table_size_legacy; + hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)->min = + pcm->params.host_period_bytes; - validate = sof_virtio_validate, - probe = sof_virtio_probe, - remove = sof_virtio_remove, + hw_param_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min = + pcm->params.buffer.size; -#ifdef CONFIG_PM - freeze = sof_virtio_freeze, - restore = sof_virtio_restore, -#endif -}; + snd_mask_none(fmt); + switch (pcm->params.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32); + break; + case SOF_IPC_FRAME_FLOAT: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_FLOAT); + break; + default: + return -EINVAL; + } + return 0; +} -/* this will be called by sof core when core is ready */ -int sof_virtio_register(struct snd_sof_dev *sdev) +static int sbe_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_ipc_pcm_params *pcm, int vm_id) { + struct snd_sg_page *table; + struct snd_sg_buf sgbuf; /* FIXME alloc at topology load */ + struct snd_dma_buffer dmab; /* FIXME alloc at topology load */ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + struct snd_pcm_hw_params params; + int direction = pcm->params.direction; + u32 pages; int ret; - ret = register_virtio_driver(&sof_be_virtio_driver); - /* do we need to do anythig else here */ + /* find the proper substream */ + substream = sbe_get_substream(sdev, NULL, direction); + if (!substream) + return -ENODEV; + + runtime = substream->runtime; + if (!runtime) { + dev_err(sdev->dev, "no runtime is available for hw_params\n"); + return -ENODEV; + } + + /* setup hw */ + pages = pcm->params.buffer.pages; + table = kcalloc(pages, sizeof(*table), GFP_KERNEL); + sgbuf.table = table; + dmab.private_data = &sgbuf; + runtime->dma_buffer_p = &dmab; /* use the audiobuf from FE */ + + /* TODO: codec hw_params */ + + /* convert buffer GPA to HPA */ + ret = sbe_stream_prepare(sdev, pcm, vm_id, table); + + /* Use different stream_tag from FE. This is the real tag */ + sbe_assemble_params(pcm, ¶ms); + pcm->params.stream_tag = + snd_sof_pcm_platform_hw_params(sdev, substream, ¶ms); + dev_dbg(sdev->dev, "stream_tag %d", + pcm->params.stream_tag); + + kfree(table); + return ret; +} + +/* handle the stream ipc */ +static int sbe_ipc_stream_codec(struct snd_sof_dev *sdev, int vm_id, + struct sof_ipc_hdr *hdr) +{ + struct sof_ipc_pcm_params *pcm; + struct sof_ipc_stream *stream; + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + struct snd_soc_dai *codec_dai; + const struct snd_soc_dai_ops *ops; + int ret, direction, comp_id; + int i; + u32 cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; + + /* TODO validate host comp id range based on vm_id */ + + switch (cmd) { + case ICS(SOF_IPC_STREAM_TRIG_START): + stream = (struct sof_ipc_stream *)hdr; + comp_id = stream->comp_id; + snd_sof_find_spcm_comp(sdev, comp_id, &direction); + substream = sbe_get_substream(sdev, &rtd, direction); + + for (i = 0; i < rtd->num_codecs; i++) { + /* + * Now we are ready to trigger start. + * Let's unmute the codec firstly + */ + codec_dai = rtd->codec_dais[i]; + snd_soc_dai_digital_mute(codec_dai, 0, direction); + ops = codec_dai->driver->ops; + if (ops->trigger) { + ret = ops->trigger(substream, + SNDRV_PCM_TRIGGER_START, + codec_dai); + if (ret < 0) + return ret; + } + } + break; + default: + dev_dbg(sdev->dev, "0x%x!\n", cmd); + break; + } + + return 0; +} + +/* handle the stream ipc */ +static int sbe_ipc_stream(struct snd_sof_dev *sdev, int vm_id, + struct sof_ipc_hdr *hdr) +{ + struct sof_ipc_pcm_params *pcm; + struct sof_ipc_stream *stream; + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + struct snd_soc_dai *codec_dai; + const struct snd_soc_dai_ops *ops; + int ret, direction, comp_id, i; + u32 cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; + + /* TODO validate host comp id range based on vm_id */ + + switch (cmd) { + case ICS(SOF_IPC_STREAM_PCM_PARAMS): + sbe_pcm_open(sdev, hdr, vm_id); + pcm = (struct sof_ipc_pcm_params *)hdr; + ret = sbe_stream_hw_params(sdev, pcm, vm_id); + break; + case ICS(SOF_IPC_STREAM_TRIG_START): + stream = (struct sof_ipc_stream *)hdr; + comp_id = stream->comp_id; + snd_sof_find_spcm_comp(sdev, comp_id, &direction); + substream = sbe_get_substream(sdev, &rtd, direction); + snd_sof_pcm_platform_trigger(sdev, substream, + SNDRV_PCM_TRIGGER_START); + break; + case ICS(SOF_IPC_STREAM_TRIG_STOP): + stream = (struct sof_ipc_stream *)hdr; + comp_id = stream->comp_id; + snd_sof_find_spcm_comp(sdev, comp_id, &direction); + substream = sbe_get_substream(sdev, &rtd, direction); + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + ops = codec_dai->driver->ops; + if (ops->trigger) { + ret = ops->trigger(substream, + SNDRV_PCM_TRIGGER_STOP, + codec_dai); + if (ret < 0) { + dev_err(sdev->dev, + "trigger stop fails\n"); + return ret; + } + } + } + snd_sof_pcm_platform_trigger(sdev, substream, + SNDRV_PCM_TRIGGER_STOP); + break; + case ICS(SOF_IPC_STREAM_PCM_FREE): + sbe_pcm_close(sdev, hdr, vm_id); + break; + case ICS(SOF_IPC_STREAM_POSITION): + /* + * TODO: this is special case, we do not send this IPC to DSP + * but read back position directly from memory (like SOS) and + * then reply to FE. + * Use stream ID to get correct stream data + */ + break; + default: + dev_dbg(sdev->dev, "0x%x!\n", cmd); + break; + } + + return 0; +} + +static int sbe_ipc_tplg_comp_new(struct snd_sof_dev *sdev, int vm_id, + struct sof_ipc_hdr *hdr) +{ + struct snd_sof_pcm *spcm; + struct sof_ipc_comp *comp = (struct sof_ipc_comp *)hdr; + struct sof_ipc_comp_host *host; + + switch (comp->type) { + case SOF_COMP_HOST: + /* + * TODO: below is a temporary solution. next step is + * to create a whole pcm staff incluing substream + * based on Liam's suggestion. + */ + + /* + * let's create spcm in HOST ipc + * spcm should be created in pcm load, but there is no such ipc + * so let create it here + */ + host = (struct sof_ipc_comp_host *)hdr; + spcm = kzalloc(sizeof(*spcm), GFP_KERNEL); + if (!spcm) + return -ENOMEM; + + spcm->sdev = sdev; + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = + SOF_VIRTIO_COMP_ID_UNASSIGNED; + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = + SOF_VIRTIO_COMP_ID_UNASSIGNED; + mutex_init(&spcm->mutex); + spcm->stream[host->direction].comp_id = host->comp.id; + list_add(&spcm->list, &sdev->pcm_list); + break; + default: + break; + } + return 0; +} + +/* validate topology IPC */ +static int sbe_ipc_tplg(struct snd_sof_dev *sdev, int vm_id, + struct sof_ipc_hdr *hdr) +{ + /* TODO validate host comp id range based on vm_id */ + + u32 cmd; + int ret = 0; + + cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; + + switch (cmd) { + case ICS(SOF_IPC_TPLG_COMP_NEW): + ret = sbe_ipc_tplg_comp_new(sdev, vm_id, hdr); + break; + default: + break; + } + + return ret; +} + +static int sbe_ipc_stream_param_post(struct snd_sof_dev *sdev, + void *ipc_buf, void *reply_buf) +{ + struct sof_ipc_pcm_params_reply *ipc_params_reply; + struct snd_sof_pcm *spcm; + int direction; + u32 comp_id; + int posn_offset; + + ipc_params_reply = (struct sof_ipc_pcm_params_reply *)reply_buf; + comp_id = ipc_params_reply->comp_id; + posn_offset = ipc_params_reply->posn_offset; + + spcm = snd_sof_find_spcm_comp(sdev, comp_id, &direction); + if (!spcm) + return -ENODEV; + + spcm->posn_offset[direction] = + sdev->stream_box.offset + posn_offset; + return 0; +} + +/* + * For some IPCs, the reply needs to be handled. + * This function is used to handle the replies of these IPCs. + */ +static int sbe_ipc_post(struct snd_sof_dev *sdev, + void *ipc_buf, void *reply_buf) +{ + struct sof_ipc_hdr *hdr; + u32 type, cmd; + int ret = 0; + + hdr = (struct sof_ipc_hdr *)ipc_buf; + type = (hdr->cmd & SOF_GLB_TYPE_MASK) >> SOF_GLB_TYPE_SHIFT; + cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; + + switch (type) { + case IGS(SOF_IPC_GLB_STREAM_MSG): + switch (cmd) { + case ICS(SOF_IPC_STREAM_PCM_PARAMS): + ret = sbe_ipc_stream_param_post(sdev, + ipc_buf, reply_buf); + break; + default: + break; + } + break; + default: + break; + } + + return ret; +} + +/* + * TODO: The guest base ID is passed to guest at boot. + * TODO rename function name, not submit but consume + * TODO add topology ipc support and manage the multiple pcm and vms + */ +static int sbe_ipc_fwd(struct snd_sof_dev *sdev, int vm_id, + void *ipc_buf, void *reply_buf, + size_t count, size_t reply_sz) +{ + struct sof_ipc_hdr *hdr; + u32 type; + int ret = 0; + + /* validate IPC */ + if (!count) { + dev_err(sdev->dev, "error: guest IPC size is 0\n"); + return -EINVAL; + } + + hdr = (struct sof_ipc_hdr *)ipc_buf; + type = (hdr->cmd & SOF_GLB_TYPE_MASK) >> SOF_GLB_TYPE_SHIFT; + + /* validate the ipc */ + switch (type) { + case IGS(SOF_IPC_GLB_COMP_MSG): + ret = sbe_ipc_comp(sdev, vm_id, hdr); + if (ret < 0) + return ret; + break; + case IGS(SOF_IPC_GLB_STREAM_MSG): + ret = sbe_ipc_stream(sdev, vm_id, hdr); + if (ret < 0) + return ret; + break; + case IGS(SOF_IPC_GLB_DAI_MSG): + /* + * After we use the new topology solution for FE, + * we will not touch DAI anymore. + */ + break; + case IGS(SOF_IPC_GLB_TPLG_MSG): + ret = sbe_ipc_tplg(sdev, vm_id, hdr); + if (ret < 0) + return ret; + break; + case IGS(SOF_IPC_GLB_TRACE_MSG): + /* Trace should be initialized in SOS, skip FE requirement */ + return 0; + default: + dev_info(sdev->dev, "unhandled IPC 0x%x!\n", type); + break; + } + + /* now send the IPC */ + ret = sof_virtio_send_ipc(sdev, ipc_buf, reply_buf, count, reply_sz); + if (ret < 0) { + dev_err(sdev->dev, "err: failed to send virtio IPC %d\n", ret); + return ret; + } + + /* For some IPCs, the reply needs to be handled */ + ret = sbe_ipc_post(sdev, ipc_buf, reply_buf); + + switch (type) { + case IGS(SOF_IPC_GLB_STREAM_MSG): + /* setup the codec */ + ret = sbe_ipc_stream_codec(sdev, vm_id, hdr); + if (ret < 0) + return ret; + break; + default: + break; + } + return ret; } -/* called by sof core when driver is removed */ -void sof_virtio_unregister(struct snd_sof_dev *sdev) +/* IPC commands coming from FE to BE */ +static void sbe_ipc_fe_cmd_get(struct sof_vbe *vbe, int vq_idx) +{ + struct virtio_vq_info *vq = &vbe->vqs[vq_idx]; + struct device *dev = vbe->sdev->dev; + struct iovec iov[2]; + u16 idx; + void *ipc_buf; + void *reply_buf; + size_t len1, len2; + int vm_id; + int ret, i; + + vm_id = vbe->vm_id; + memset(iov, 0, sizeof(iov)); + + /* while there are mesages in virtio queue */ + while (virtio_vq_has_descs(vq)) { + /* FE uses items, first is command second is reply data */ + ret = virtio_vq_getchain(vq, &idx, iov, 2, NULL); + if (ret < 2) { + /* something wrong in vq, no item is fetched */ + if (ret < 0) { + /* + * This should never happen. + * FE should be aware this situation already + */ + virtio_vq_endchains(vq, 1); + return; + } + + dev_err(dev, "ipc buf and reply buf not paired\n"); + + /* no enough items, let drop this kick */ + for (i = 0; i <= ret; i++) { + virtio_vq_relchain(vq, idx + i, + iov[i].iov_len); + } + virtio_vq_endchains(vq, 1); + return; + } + + /* + * let's check the ipc message and reply buffer's + * length is valid or not + */ + len1 = iov[SOF_VIRTIO_IPC_MSG].iov_len; + len2 = iov[SOF_VIRTIO_IPC_REPLY].iov_len; + if (!len1 || !len2) { + if (len1) + virtio_vq_relchain(vq, idx, len1); + if (len2) + virtio_vq_relchain(vq, idx + 1, len2); + } else { + /* OK, the buffer is valid. let's handle the ipc */ + ipc_buf = iov[SOF_VIRTIO_IPC_MSG].iov_base; + reply_buf = iov[SOF_VIRTIO_IPC_REPLY].iov_base; + + /* send IPC to HW */ + ret = sbe_ipc_fwd(vbe->sdev, vm_id, ipc_buf, reply_buf, + len1, len2); + if (ret < 0) + dev_err(dev, "submit guest ipc command fail\n"); + + virtio_vq_relchain(vq, idx, len1); + virtio_vq_relchain(vq, idx + 1, len2); + } + } + + /* BE has finished the operations, now let's kick back */ + virtio_vq_endchains(vq, 1); +} + +static void handle_vq_kick(struct sof_vbe *vbe, int vq_idx) +{ + dev_dbg(vbe->sdev->dev, "vq_idx %d\n", vq_idx); + + switch (vq_idx) { + case SOF_VIRTIO_IPC_CMD_TX_VQ: + /* IPC command from FE to DSP */ + return sbe_ipc_fe_cmd_get(vbe, vq_idx); + case SOF_VIRTIO_IPC_CMD_RX_VQ: + /* IPC command reply from DSP to FE - NOT kick */ + break; + case SOF_VIRTIO_IPC_NOT_TX_VQ: + /* IPC notification reply from FE to DSP */ + return sbe_ipc_fe_not_reply_get(vbe, vq_idx); + case SOF_VIRTIO_IPC_NOT_RX_VQ: + /* IPC notification from DSP to FE - NOT kick */ + break; + default: + dev_err(vbe->sdev->dev, "idx %d is invalid\n", vq_idx); + break; + } +} + +/* + * handle_kick() is used to handle the event that vFE kick a queue + * entry to vBE. First, check this event is valid or not. If it is + * valid and needs to be handled, let's call hanle_vq_kick() + */ +static int handle_kick(int client_id, unsigned long *ioreqs_map) +{ + struct vhm_request *req; + struct sof_vbe_client *client; + struct sof_vbe *vbe; + struct snd_sof_dev *sdev = sof_virtio_get_sof(); + int i, handle; + + if (!sdev) { + pr_err("error: no BE registered for SOF!\n"); + return -EINVAL; + } + + dev_dbg(sdev->dev, "virtio audio kick handling!\n"); + + /* get the client this notification is for/from? */ + client = vbe_client_find(sdev, client_id); + if (!client) { + dev_err(sdev->dev, "Ooops! client %d not found!\n", client_id); + return -EINVAL; + } + vbe = client->vbe; + + /* go through all vcpu for the valid request buffer */ + for (i = 0; i < client->max_vcpu; i++) { + req = &client->req_buf[i]; + handle = 0; + + /* is request valid and for this client */ + if (!req->valid) + continue; + if (req->client != client->vhm_client_id) + continue; + + /* ignore if not processing state */ + if (req->processed != REQ_STATE_PROCESSING) + continue; + + dev_dbg(sdev->dev, + "ioreq type %d, direction %d, addr 0x%llx, size 0x%llx, value 0x%x\n", + req->type, + req->reqs.pio_request.direction, + req->reqs.pio_request.address, + req->reqs.pio_request.size, + req->reqs.pio_request.value); + + if (req->reqs.pio_request.direction == REQUEST_READ) { + /* + * currently we handle kick only, + * so read will return 0 + */ + req->reqs.pio_request.value = 0; + } else { + req->reqs.pio_request.value >= 0 ? + (handle = 1) : (handle = 0); + } + + req->processed = REQ_STATE_SUCCESS; + + /* + * notify hypervisor this event request is finished. + * virtio driver is ready to handle this event. + */ + acrn_ioreq_complete_request(client->vhm_client_id, i); + + /* handle VQ kick if needed */ + if (handle) + handle_vq_kick(vbe, req->reqs.pio_request.value); + } + + return 0; +} + +/* + * register vhm client with virtio. + * vhm use the client to handle the io access from FE + */ +int sof_vbe_register_client(struct sof_vbe *vbe) +{ + struct virtio_dev_info *dev_info = &vbe->dev_info; + struct snd_sof_dev *sdev = vbe->sdev; + struct vm_info info; + struct sof_vbe_client *client; + unsigned int vmid; + int ret; + + /* + * vbs core has mechanism to manage the client + * there is no need to handle this in the special BE driver + * let's use the vbs core client management later + */ + client = devm_kzalloc(sdev->dev, sizeof(*client), GFP_KERNEL); + if (!client) + return -EINVAL; + + client->vbe = vbe; + + vmid = dev_info->_ctx.vmid; + client->vhm_client_id = acrn_ioreq_create_client(vmid, handle_kick, + "sof_vbe kick init\n"); + if (client->vhm_client_id < 0) { + dev_err(sdev->dev, "failed to create client of acrn ioreq!\n"); + return client->vhm_client_id; + } + + ret = acrn_ioreq_add_iorange(client->vhm_client_id, REQ_PORTIO, + dev_info->io_range_start, + dev_info->io_range_start + + dev_info->io_range_len - 1); + if (ret < 0) { + dev_err(sdev->dev, "failed to add iorange to acrn ioreq!\n"); + goto err; + } + + /* + * setup the vm information, such as max_vcpu and max_gfn + * BE need this information to handle the vqs + */ + ret = vhm_get_vm_info(vmid, &info); + if (ret < 0) { + dev_err(sdev->dev, "failed in vhm_get_vm_info!\n"); + goto err; + } + client->max_vcpu = info.max_vcpu; + + /* + * Setup the reqbuf for this client. The reqbuf is ready in + * acrn system now. + */ + client->req_buf = acrn_ioreq_get_reqbuf(client->vhm_client_id); + if (!client->req_buf) { + dev_err(sdev->dev, "failed in acrn_ioreq_get_reqbuf!\n"); + goto err; + } + + /* just attach once as vhm will kick kthread */ + acrn_ioreq_attach_client(client->vhm_client_id, 0); + + /* complete client init and add to list */ + list_add(&client->list, &vbe->client_list); + + return 0; +err: + acrn_ioreq_destroy_client(client->vhm_client_id); + return -EINVAL; +} + +/* register SOF audio BE with virtio/acrn */ +int sof_vbe_register(struct snd_sof_dev *sdev, struct sof_vbe **svbe) +{ + struct sof_vbe *vbe; + struct virtio_vq_info *vqs; + int i; + + vbe = devm_kzalloc(sdev->dev, sizeof(*vbe), GFP_KERNEL); + if (!vbe) + return -ENOMEM; + + INIT_LIST_HEAD(&vbe->client_list); + INIT_LIST_HEAD(&vbe->posn_list); + spin_lock_init(&vbe->posn_lock); + vbe->sdev = sdev; + + /* + * We currently only support one VM. The comp_id range will be + * dynamically assigned when multiple VMs are supported. + */ + vbe->comp_id_begin = SOF_VIRTIO_MAX_GOS_COMPS; + vbe->comp_id_end = vbe->comp_id_begin + SOF_VIRTIO_MAX_GOS_COMPS; + + vqs = vbe->vqs; + for (i = 0; i < SOF_VIRTIO_NUM_OF_VQS; i++) { + vqs[i].dev = &vbe->dev_info; + + /* + * currently relies on VHM to kick us, + * thus vq_notify not used + */ + vqs[i].vq_notify = NULL; + } + + /* link dev and vqs */ + vbe->dev_info.vqs = vqs; + + virtio_dev_init(&vbe->dev_info, vqs, SOF_VIRTIO_NUM_OF_VQS); + + *svbe = vbe; + + return 0; +} + +int sof_vbe_update_guest_posn(struct snd_sof_dev *sdev, + struct sof_ipc_stream_posn *posn) { - unregister_virtio_driver(&sof_be_virtio_driver); - /* do we need to do anythig else here */ + struct sof_vbe *vbe = sbe_comp_id_to_vbe(sdev, posn->comp_id); + struct virtio_vq_info *vq = &vbe->vqs[SOF_VIRTIO_IPC_NOT_RX_VQ]; + struct vbs_sof_posn *entry; + unsigned long flags; + bool ret, endchain; + + /* posn update for SOS */ + if (!vbe) + return 0; + + /* + * let's try to get a notification RX vq available buffer + * If there is an available buffer, let's notify immediately + */ + ret = sbe_fill_posn_vqbuf(vbe, vq, posn, &endchain); + if (ret) { + if (endchain) + virtio_vq_endchains(vq, 1); + return 0; + } + + spin_lock_irqsave(&vbe->posn_lock, flags); + + /* + * Notification RX vq buffer is not available. Let's save the posn + * update msg. And send the msg when vq buffer is available. + */ + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) { + spin_unlock_irqrestore(&vbe->posn_lock, flags); + return -ENOMEM; + } + + memcpy(&entry->pos, posn, sizeof(struct vbs_sof_posn)); + list_add_tail(&entry->list, &vbe->posn_list); + spin_unlock_irqrestore(&vbe->posn_lock, flags); + + if (endchain) + virtio_vq_endchains(vq, 1); + + return 0; } diff --git a/sound/soc/sof/virtio-be.h b/sound/soc/sof/virtio-be.h new file mode 100644 index 00000000000000..78beb3a69dcf50 --- /dev/null +++ b/sound/soc/sof/virtio-be.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Libin Yang + */ + +#ifndef __SOUND_SOC_SOF_VIRTIO_BE_H +#define __SOUND_SOC_SOF_VIRTIO_BE_H + +#include + +struct sof_vbe; + +/* Virtio Backend */ +struct sof_vbe { + struct snd_sof_dev *sdev; + + struct virtio_dev_info dev_info; + struct virtio_vq_info vqs[SOF_VIRTIO_NUM_OF_VQS]; + + int vm_id; /* vm id number */ + + /* the comp_ids for this vm audio */ + int comp_id_begin; + int comp_id_end; + + spinlock_t posn_lock; /* lock for position update */ + + struct list_head client_list; + struct list_head posn_list; + + struct list_head list; +}; + +struct sof_vbe_client { + struct sof_vbe *vbe; + int vhm_client_id; + int max_vcpu; + struct vhm_request *req_buf; + struct list_head list; +}; + +struct snd_sof_dev *sof_virtio_get_sof(void); +int sof_vbe_register(struct snd_sof_dev *sdev, struct sof_vbe **svbe); +int sof_vbe_register_client(struct sof_vbe *vbe); + +#endif /* __SOUND_SOC_SOF_VIRTIO_BE_H */ diff --git a/sound/soc/sof/virtio-fe.c b/sound/soc/sof/virtio-fe.c index 1019e65876b923..d04c2857fbb3d0 100644 --- a/sound/soc/sof/virtio-fe.c +++ b/sound/soc/sof/virtio-fe.c @@ -5,7 +5,8 @@ * * Copyright(c) 2017 Intel Corporation. All rights reserved. * - * Author: Luo Xionghu + * Author: Libin Yang + * Luo Xionghu * Liam Girdwood */ @@ -19,112 +20,429 @@ * The virtIO message Q will use the *exact* same IPC structures as we currently * use in the mailbox. * - * Guest OS SOF core -> SOF FE -> virtIO Q -> SOF BE -> - * System OS SOF core -> DSP - * * The mailbox IO and TX/RX msg functions below will do IO on the virt IO Q. */ -#include -#include -#include #include +#include #include #include -#include -#include -#include +#include #include +#include #include #include +#include -#include "sof-priv.h" +#include "virtio-fe.h" #include "ops.h" -#include "intel.h" +#include "sof-priv.h" +#include "intel/hda.h" + +static const char *const sof_vq_names[SOF_VIRTIO_NUM_OF_VQS] = { + SOF_VIRTIO_IPC_CMD_TX_VQ_NAME, + SOF_VIRTIO_IPC_CMD_RX_VQ_NAME, + SOF_VIRTIO_IPC_NOT_TX_VQ_NAME, + SOF_VIRTIO_IPC_NOT_RX_VQ_NAME, +}; + +static const struct sof_dev_desc virt_desc = { + .nocodec_fw_filename = NULL, + .nocodec_tplg_filename = "intel/sof-apl-nocodec.tplg", + .resindex_lpe_base = -1, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, +}; + /* * IPC Firmware ready. */ -static int virtio_fe_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +static int vfe_is_ready(struct snd_sof_dev *sdev) +{ + /* is message still pending */ + if (sdev->vfe->msg) + return 0; + + /* ready for next message */ + return 1; +} + +static int vfe_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) { - /* not needed for FE ? */ return 0; +}; + +/* used to send IPC to BE */ +static int vfe_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct sof_vfe *vfe; + struct scatterlist sgs[2]; + int ret = 0; + + vfe = sdev->vfe; + + sg_init_table(sgs, 2); + + sg_set_buf(&sgs[SOF_VIRTIO_IPC_MSG], + msg->msg_data, msg->msg_size); + sg_set_buf(&sgs[SOF_VIRTIO_IPC_REPLY], + msg->reply_data, msg->reply_size); + + vfe->msg = msg; + + ret = virtqueue_add_outbuf(vfe->ipc_cmd_tx_vq, sgs, 2, + msg->msg_data, GFP_KERNEL); + if (ret < 0) + dev_err(sdev->dev, "error: could not send IPC %d\n", ret); + + virtqueue_kick(vfe->ipc_cmd_tx_vq); + + return ret; } -/* - * IPC Mailbox IO - */ +/* get IPC reply from BE */ +static int vfe_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct sof_vfe *vfe = sdev->vfe; -static void virtio_fe_mailbox_write(struct snd_sof_dev *sdev, u32 offset, - void *message, size_t bytes) + vfe->msg = NULL; + return 0; +} + +/* get stream message from virtio */ +static int vfe_get_stream_message(struct snd_sof_dev *sdev) { - /* write data to message Q buffer before sending message */ + struct sof_vfe *vfe = sdev->vfe; + void *buf = NULL; + unsigned int buflen = 0; + + buf = virtqueue_get_buf(vfe->ipc_not_rx_vq, &buflen); + if (unlikely(!buf)) { + dev_err(sdev->dev, "error rx not from virtio:%d!\n", buflen); + return -ENOMEM; + } + + return 0; } -static void virtio_fe_mailbox_read(struct snd_sof_dev *sdev, u32 offset, - void *message, size_t bytes) +/* tell DSP we have processed notification */ +static int vfe_cmd_done(struct snd_sof_dev *sdev, int dir) { - /* read data from message Q buffer after receiving message */ + return 0; } -static int virtio_fe_tx_busy(struct snd_sof_dev *sdev) +/* Send the IPC message completed. This means vBE has received the cmd */ +static void vfe_cmd_tx_done(struct virtqueue *vq) { - /* return 1 if tx message Q is busy */ + struct snd_sof_ipc_msg *msg; + struct sof_vfe *vfe; + + vfe = vq->vdev->priv; + msg = vfe->msg; + msg->complete = true; + wake_up(&msg->waitq); } -static int virtio_fe_tx_msg(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) +static void vfe_cmd_handle_rx(struct virtqueue *vq) { - /* write msg to the virtio queue message for BE */ +} - return 0; +static void vfe_not_tx_done(struct virtqueue *vq) +{ } -static int virtio_fe_rx_msg(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) +static void vfe_posn_update(struct work_struct *work) { - /* read the virtio queue message from BE and copy to msg */ - return 0; + struct sof_ipc_stream_posn *posn = NULL; + struct sof_vfe *vfe = + container_of(work, struct sof_vfe, posn_update_work); + struct snd_sof_pcm *spcm; + struct scatterlist sg; + struct snd_sof_dev *sdev; + struct virtqueue *vq; + unsigned int buflen = 0; + int direction; + + vq = vfe->ipc_not_rx_vq; + sdev = vfe->sdev; + + /* virtio protects and make sure no re-entry */ + while ((posn = virtqueue_get_buf(vq, &buflen)) != NULL) { + spcm = snd_sof_find_spcm_comp(sdev, posn->comp_id, &direction); + if (!spcm) { + dev_err(sdev->dev, + "err: period elapsed for unused component %d\n", + posn->comp_id); + + /* kick back the empty posn buffer immediately */ + sg_init_one(&sg, posn, sizeof(*posn)); + virtqueue_add_inbuf(vq, &sg, 1, posn, GFP_KERNEL); + virtqueue_kick(vq); + continue; + } + + /* + * The position update requirement is valid. + * Let's update the position now. + */ + memcpy(&spcm->stream[direction].posn, posn, sizeof(*posn)); + snd_pcm_period_elapsed(spcm->stream[direction].substream); + + /* kick back the empty posn buffer immediately */ + sg_init_one(&sg, posn, sizeof(*posn)); + virtqueue_add_inbuf(vq, &sg, 1, posn, GFP_KERNEL); + virtqueue_kick(vq); + } } /* - * Probe and remove. + * handle the pos_update, receive the posn and send to up layer, then + * resend the buffer to BE */ +static void vfe_not_handle_rx(struct virtqueue *vq) +{ + struct sof_vfe *vfe; + + vfe = vq->vdev->priv; + + schedule_work(&vfe->posn_update_work); +} + +static struct sof_virtio_priv *sof_vfe_init(struct virtio_device *vdev) +{ + struct device *dev; + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *sof_pdata; + struct sof_virtio_priv *priv; + int ret; + + dev = &vdev->dev; -static int virtio_fe_probe(struct snd_sof_dev *sdev) + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return NULL; + + sof_pdata->drv_name = "sof-nocodec"; + sof_pdata->is_vfe = 1; /* This is audio vFE device */ + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + if (!mach) + return NULL; + + ret = sof_nocodec_setup(dev, sof_pdata, mach, &virt_desc, + &snd_sof_vfe_ops); + if (ret < 0) + return NULL; + + mach->asoc_plat_name = "sof-vfe"; + mach->pdata = &snd_sof_vfe_ops; + + /* + * FIXME:currently, we use the guest local tplg file loading for easy + * debug, should swich to service request later. + */ + + sof_pdata->id = vdev->id.device; + sof_pdata->name = dev_name(&vdev->dev); + sof_pdata->machine = mach; + sof_pdata->desc = &virt_desc; + sof_pdata->dev = dev; + sof_pdata->vfe = vdev->priv; + + /* register machine driver */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(sof_pdata->pdev_mach)) { + pr_debug("creating sof machine driver failed\n"); + return NULL; + } + + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + dev_set_drvdata(dev, priv); + + priv->sof_pdata = sof_pdata; + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + platform_device_unregister(sof_pdata->pdev_mach); + return NULL; + } + + return priv; +} + +static void sof_vfe_deinit(struct virtio_device *vdev) { - /* register virtio device */ + struct sof_vfe *vfe = vdev->priv; + struct sof_virtio_priv *priv = vfe->priv; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(priv->pdev_pcm); + platform_device_unregister(sof_pdata->pdev_mach); +} + +/* Probe and remove. */ +static int vfe_probe(struct virtio_device *vdev) +{ + struct virtqueue *vqs[SOF_VIRTIO_NUM_OF_VQS]; + struct device *dev; + struct scatterlist sg; + struct sof_vfe *vfe; + int ret; + + /* the processing callback number must be the same as the vqueues.*/ + vq_callback_t *cbs[SOF_VIRTIO_NUM_OF_VQS] = { + vfe_cmd_tx_done, + vfe_cmd_handle_rx, + vfe_not_tx_done, + vfe_not_handle_rx + }; + + dev = &vdev->dev; + + dev->coherent_dma_mask = DMA_BIT_MASK(64); + dev->dma_mask = &dev->coherent_dma_mask; + vfe = devm_kzalloc(dev, sizeof(*vfe), GFP_KERNEL); + if (!vfe) + return -ENOMEM; + + vdev->priv = vfe; + + INIT_WORK(&vfe->posn_update_work, vfe_posn_update); + + /* create virt queue for vfe to send/receive IPC message. */ + ret = virtio_find_vqs(vdev, SOF_VIRTIO_NUM_OF_VQS, + vqs, cbs, sof_vq_names, NULL); + if (ret) { + dev_err(dev, "error: find vqs fail with %d\n", ret); + return ret; + } + + /* virtques */ + vfe->ipc_cmd_tx_vq = vqs[SOF_VIRTIO_IPC_CMD_TX_VQ]; + vfe->ipc_cmd_rx_vq = vqs[SOF_VIRTIO_IPC_CMD_RX_VQ]; + vfe->ipc_not_tx_vq = vqs[SOF_VIRTIO_IPC_NOT_TX_VQ]; + vfe->ipc_not_rx_vq = vqs[SOF_VIRTIO_IPC_NOT_RX_VQ]; + + virtio_device_ready(vdev); - /* conenct virt queues to BE */ + vfe->posn = kmalloc(sizeof(*vfe->posn), GFP_KERNEL); + sg_init_one(&sg, vfe->posn, sizeof(struct sof_ipc_stream_posn)); + if (vfe->ipc_not_rx_vq) { + ret = virtqueue_add_inbuf(vfe->ipc_not_rx_vq, + &sg, 1, vfe->posn, GFP_KERNEL); + } + virtqueue_kick(vfe->ipc_not_rx_vq); + + /* + * add the SOF related functions here, to load the + * topology, generate the components, and send IPC + */ + vfe->priv = sof_vfe_init(vdev); + + return ret; } -static int virtio_fe_remove(struct snd_sof_dev *sdev) +static void vfe_remove(struct virtio_device *vdev) { /* free virtio resurces and unregister device */ + struct sof_vfe *vfe = vdev->priv; + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + cancel_work_sync(&vfe->posn_update_work); + kfree(vfe->posn); + + /* unregister the devices of SOF */ + sof_vfe_deinit(vdev); + + return; } -/* baytrail ops */ -struct snd_sof_dsp_ops snd_sof_virtio_fe_ops = { - /* device init */ - .probe = virtio_fe_probe, - .remove = virtio_fe_remove, +static void virtaudio_config_changed(struct virtio_device *vdev) +{ +} + +const struct virtio_device_id id_table[] = { + {VIRTIO_ID_AUDIO, VIRTIO_DEV_ANY_ID}, + {0}, +}; + +/* + * TODO: There still need a shutdown to handle the case the UOS + * is poweroff, restart. + */ - /* mailbox */ - .mailbox_read = virtio_fe_mailbox_read, - .mailbox_write = virtio_fe_mailbox_write, +static struct virtio_driver vfe_audio_driver = { + .feature_table = NULL, + .feature_table_size = 0, + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = vfe_probe, + .remove = vfe_remove, + .config_changed = virtaudio_config_changed, +}; - /* ipc */ - .tx_msg = virtio_fe_tx_msg, - .rx_msg = virtio_fe_rx_msg, - .fw_ready = virtio_fe_fw_ready, - .tx_busy = virtio_fe_tx_busy, +static int vfe_register(struct snd_sof_dev *sdev) +{ + return 0; +} + +static int vfe_unregister(struct snd_sof_dev *sdev) +{ + return 0; +} + +#define SKL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_dai_driver virtio_dai[] = { + { + .name = "SSP4 Pin", + .playback = SOF_DAI_STREAM("ssp4 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp4 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + }, +}; + +/* virtio fe ops */ +struct snd_sof_dsp_ops snd_sof_vfe_ops = { + /* device init */ + .probe = vfe_register, + .remove = vfe_unregister, - /* module loading */ -// .load_module = snd_sof_parse_module_memcpy, + /* IPC */ + .send_msg = vfe_send_msg, + .get_reply = vfe_get_reply, + .is_ready = vfe_is_ready, + .fw_ready = vfe_fw_ready, + .cmd_done = vfe_cmd_done, - /*Firmware loading */ - .load_firmware = snd_sof_load_firmware_memcpy, + /* DAI drivers */ + .drv = virtio_dai, + .num_drv = 1, }; -EXPORT_SYMBOL(snd_sof_virtio_fe_ops); +EXPORT_SYMBOL(snd_sof_vfe_ops); +module_virtio_driver(vfe_audio_driver); +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Sound Open Firmware Virtio FE"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/virtio-fe.h b/sound/soc/sof/virtio-fe.h new file mode 100644 index 00000000000000..3e38944f7a3ab3 --- /dev/null +++ b/sound/soc/sof/virtio-fe.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Libin Yang + */ + +#ifndef __SOUND_SOC_SOF_VIRTIO_FE_H +#define __SOUND_SOC_SOF_VIRTIO_FE_H + +#include +#include +#include +#include +#include +#include + +struct sof_virtio_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +struct snd_sof_dsp_ops snd_sof_vfe_ops; + +#endif /* __SOUND_SOC_SOF_VIRTIO_FE_H */ diff --git a/sound/soc/sof/virtio-miscdev.c b/sound/soc/sof/virtio-miscdev.c new file mode 100644 index 00000000000000..571526c964d1f3 --- /dev/null +++ b/sound/soc/sof/virtio-miscdev.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Libin Yang + * Luo Xionghu + * Liam Girdwood + */ + +#include +#include +#include +#include +#include + +#include "sof-priv.h" +#include "virtio-be.h" +#include "virtio-miscdev.h" +#include + +/* + * This module registers a device node /dev/vbs_k_audio, + * that handle the communication between Device Model and + * the virtio backend service. The device model can + * control the backend to : set the status, set the vq account + * and etc. The config of the DM and VBS must be accordance. + */ + +static struct virtio_miscdev *virtio_audio; + +static struct virtio_miscdev *get_virtio_audio(void) +{ + return virtio_audio; +} + +struct snd_sof_dev *sof_virtio_get_sof(void) +{ + struct virtio_miscdev *vaudio = get_virtio_audio(); + + if (vaudio) + return (struct snd_sof_dev *)vaudio->priv; + + return NULL; +} + +static int sof_virtio_open(struct file *f, void *data) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)data; + struct sof_vbe *vbe; + int ret; + + ret = sof_vbe_register(sdev, &vbe); + if (ret) + return ret; + + /* + * link to sdev->vbe_list + * Maybe virtio_miscdev managing the list is more reasonable. + * Let's use sdev to manage the FE audios now. + */ + list_add(&vbe->list, &sdev->vbe_list); + f->private_data = vbe; + + return 0; +} + +static long sof_virtio_ioctl(struct file *f, void *data, unsigned int ioctl, + unsigned long arg) +{ + struct sof_vbe *vbe = f->private_data; + void __user *argp = (void __user *)arg; + int ret; + + switch (ioctl) { + case VBS_SET_DEV: + ret = virtio_dev_ioctl(&vbe->dev_info, ioctl, argp); + if (!ret) + vbe->vm_id = vbe->dev_info._ctx.vmid; + break; + case VBS_SET_VQ: + ret = virtio_vqs_ioctl(&vbe->dev_info, ioctl, argp); + if (ret) + return ret; + + /* + * Maybe we should move sof_register_vhm_client() + * in VBS_SET_DEV + */ + ret = sof_vbe_register_client(vbe); + if (ret) + return ret; + /* + * TODO: load tplg and send to FE here + * + * The better method is FE driver send FE-tplg id + * and request FE-tplg. + * Then BE loads the corresponding tplg based on + * the FE-tplg id and send to FE driver. + */ + break; + default: + return -ENOIOCTLCMD; + } + + return ret; +} + +static int sof_virtio_release(struct file *f, void *data) +{ + struct sof_vbe *vbe = f->private_data; + + list_del(&vbe->list); + devm_kfree(vbe->sdev->dev, vbe); + f->private_data = NULL; + return 0; +} + +static int vbs_audio_open(struct inode *inode, struct file *f) +{ + struct virtio_miscdev *vaudio = get_virtio_audio(); + + if (!vaudio) + return -ENODEV; /* This should never happen */ + + dev_dbg(vaudio->dev, "virtio audio open\n"); + if (vaudio->open) + return vaudio->open(f, virtio_audio->priv); + + return 0; +} + +static long vbs_audio_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct virtio_miscdev *vaudio = get_virtio_audio(); + + if (!vaudio) + return -ENODEV; /* This should never happen */ + + dev_dbg(vaudio->dev, "virtio audio ioctl\n"); + if (vaudio->ioctl) + return vaudio->ioctl(f, vaudio->priv, ioctl, arg); + else + return -ENXIO; +} + +static int vbs_audio_release(struct inode *inode, struct file *f) +{ + struct virtio_miscdev *vaudio = get_virtio_audio(); + + if (!vaudio) + return -ENODEV; /* This should never happen */ + + dev_dbg(vaudio->dev, "release virtio audio\n"); + + if (vaudio->release) + vaudio->release(f, vaudio->priv); + + return 0; +} + +static const struct file_operations vbs_audio_fops = { + .owner = THIS_MODULE, + .release = vbs_audio_release, + .unlocked_ioctl = vbs_audio_ioctl, + .open = vbs_audio_open, + .llseek = noop_llseek, +}; + +static struct miscdevice vbs_audio_k = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vbs_k_audio", + .fops = &vbs_audio_fops, +}; + +static int audio_virtio_miscdev_register(struct device *dev, void *data, + struct virtio_miscdev **va) +{ + struct virtio_miscdev *vaudio; + int ret; + + ret = misc_register(&vbs_audio_k); + if (ret) { + dev_err(dev, "misc device register failed %d\n", ret); + return ret; + } + + vaudio = kzalloc(sizeof(*vaudio), GFP_KERNEL); + if (!vaudio) { + misc_deregister(&vbs_audio_k); + return -ENOMEM; + } + + vaudio->priv = data; + vaudio->dev = dev; + virtio_audio = vaudio; + *va = vaudio; + + return 0; +} + +static int audio_virtio_miscdev_unregister(void) +{ + if (virtio_audio) { + misc_deregister(&vbs_audio_k); + kfree(virtio_audio); + virtio_audio = NULL; + } + + return 0; +} + +/** + * sof_virtio_miscdev_register() - init the virtio be audio driver + * @sdev: the snd_sof_dev of sof core + * + * This function registers the misc device, which will be used + * by the user space to communicate with the audio driver. + * + * Return: 0 for success or negative value for err + */ +int sof_virtio_miscdev_register(struct snd_sof_dev *sdev) +{ + struct virtio_miscdev *vaudio; + int ret; + + ret = audio_virtio_miscdev_register(sdev->dev, sdev, &vaudio); + if (ret) + return ret; + + vaudio->open = sof_virtio_open; + vaudio->ioctl = sof_virtio_ioctl; + vaudio->release = sof_virtio_release; + + return 0; +} +EXPORT_SYMBOL(sof_virtio_miscdev_register); + +/** + * sof_virtio_miscdev_unregister() - release the virtio be audio driver + * + * This function deregisters the misc device, and free virtio_miscdev + * + */ +int sof_virtio_miscdev_unregister(void) +{ + return audio_virtio_miscdev_unregister(); +} +EXPORT_SYMBOL(sof_virtio_miscdev_unregister); diff --git a/sound/soc/sof/virtio-miscdev.h b/sound/soc/sof/virtio-miscdev.h new file mode 100644 index 00000000000000..cce0ac7876cbea --- /dev/null +++ b/sound/soc/sof/virtio-miscdev.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Libin Yang + * Liam Girdwood + */ + +#ifndef __SOUND_SOF_VIRTIO_MISCDEV_H +#define __SOUND_SOF_VIRTIO_MISCDEV_H + +struct virtio_miscdev { + struct device *dev; + int (*open)(struct file *f, void *data); + long (*ioctl)(struct file *f, void *data, unsigned int ioctl, + unsigned long arg); + int (*release)(struct file *f, void *data); + void *priv; +}; + +#endif /* __SOUND_SOF_VIRTIO_MISCDEV_H */ From e1f6cec9c0ba38562b7b43d7bb63d7dcd7900ec6 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 18 Oct 2018 18:09:15 +0800 Subject: [PATCH 244/298] ASoC: SOF: fix trace doesn't work after suspend/resume issue We don't delete the debugFS entry for trace at suspend, so we should not allocate buffer again for it at resume, otherwise, the trace will be copied to new buffer and can't be read via old entry. Signed-off-by: Keyon Jie --- sound/soc/sof/core.c | 2 +- sound/soc/sof/pm.c | 4 ++-- sound/soc/sof/sof-priv.h | 1 + sound/soc/sof/trace.c | 13 ++++++++++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 11d9c8befd6846..11be7df75bd174 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -417,7 +417,7 @@ static int sof_remove(struct platform_device *pdev) snd_sof_ipc_free(sdev); sof_virtio_miscdev_unregister(); snd_sof_free_debug(sdev); - snd_sof_release_trace(sdev); + snd_sof_free_trace(sdev); snd_sof_remove(sdev); return 0; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 345ef79e784fa2..a235b6ccf48436 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -286,8 +286,8 @@ static int sof_resume(struct device *dev, int runtime_resume) return ret; } - /* init DMA trace */ - ret = snd_sof_init_trace(sdev); + /* resume DMA trace, only need send ipc */ + ret = snd_sof_init_trace_ipc(sdev); if (ret < 0) { /* non fatal */ dev_warn(sdev->dev, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 4cf797bd09bbef..98af791ed12e65 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -558,6 +558,7 @@ int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, */ int snd_sof_init_trace(struct snd_sof_dev *sdev); void snd_sof_release_trace(struct snd_sof_dev *sdev); +void snd_sof_free_trace(struct snd_sof_dev *sdev); int snd_sof_dbg_init(struct snd_sof_dev *sdev); void snd_sof_free_debug(struct snd_sof_dev *sdev); int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 5564bd4f339865..89367a826a05a7 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -151,6 +151,9 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) struct sof_ipc_reply ipc_reply; int ret; + if (sdev->dtrace_is_enabled) + return; + /* set IPC parameters */ params.hdr.size = sizeof(params); params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS; @@ -289,7 +292,15 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: fail in snd_sof_dma_trace_release %d\n", ret); + sdev->dtrace_is_enabled = false; +} +EXPORT_SYMBOL(snd_sof_release_trace); + +void snd_sof_free_trace(struct snd_sof_dev *sdev) +{ + snd_sof_release_trace(sdev); + snd_dma_free_pages(&sdev->dmatb); snd_dma_free_pages(&sdev->dmatp); } -EXPORT_SYMBOL(snd_sof_release_trace); +EXPORT_SYMBOL(snd_sof_free_trace); From 89bef7af66b5f5d46e5ea5bf1d2d5a722f4d154b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 18 Oct 2018 14:29:11 +0100 Subject: [PATCH 245/298] ASoC: SOF: Intel HDA: continue probe if no HDMI devices found Normal flow if no HDMI devices are connected. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index aa06a1ee897c44..26b26be61d251b 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -408,8 +408,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev) /* (2) probe i915 and HDA codecs, HDMI codecs */ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { ret = hda_codec_i915_init(sdev); - if (ret < 0) - return ret; + dev_dbg(&pci->dev, "no HDMI audio devices found\n"); } /* only HDA analog codec is detected in step (1) and From 34f2d7d09019b8834d83396d47056f39491bc8a3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 18 Oct 2018 14:57:04 +0100 Subject: [PATCH 246/298] [SQUASHME] ASoC: SOF: topology. make widget debug verbose Signed-off-by: Liam Girdwood --- sound/soc/sof/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index fcd4888ab8cf4c..e2393105f9dcf9 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -869,7 +869,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_pcm_runtime *rtd; list_for_each_entry(rtd, &card->rtd_list, list) { - dev_dbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", + dev_vdbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", w->name, w->sname, rtd->dai_link->stream_name); if (!w->sname || !rtd->dai_link->stream_name) From f725efaa8a27a0d9dd9d6b2f713bfad85197ecad Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 18 Oct 2018 14:58:51 +0100 Subject: [PATCH 247/298] [SQUASHME] ASoC: SOF: core. make page table debug verbose Signed-off-by: Liam Girdwood --- sound/soc/sof/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 11be7df75bd174..9227d6624ce448 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -212,7 +212,7 @@ int snd_sof_create_page_table(struct snd_sof_dev *sdev, u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; u32 *pg_table; - dev_dbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + dev_vdbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); pg_table = (u32 *)(page_table + idx); From f4b3c3027d88bc42178942bfbca79871b044307e Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 18 Oct 2018 19:05:39 +0800 Subject: [PATCH 248/298] ASoC: sof: topology: add dai direction token header Signed-off-by: Rander Wang Signed-off-by: Pan Xiuli --- include/uapi/sound/sof-topology.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 3c4ae99c8b5e0a..f83524cf1c5e3d 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -36,6 +36,7 @@ #define SOF_TKN_DAI_DMAC_CONFIG 153 #define SOF_TKN_DAI_TYPE 154 #define SOF_TKN_DAI_INDEX 155 +#define SOF_TKN_DAI_DIRECTION 156 /* scheduling */ #define SOF_TKN_SCHED_DEADLINE 200 From c3bec6fd0967c32f4c6d654e9efae022b914290a Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 18 Oct 2018 19:06:19 +0800 Subject: [PATCH 249/298] ASoC: sof: topology: parse direction for dai widget Set direction for dai from topology Signed-off-by: Rander Wang Signed-off-by: Pan Xiuli --- sound/soc/sof/topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e2393105f9dcf9..696a4b076025b4 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -406,6 +406,8 @@ static const struct sof_topology_token dai_tokens[] = { offsetof(struct sof_ipc_comp_dai, type), 0}, {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_comp_dai, dai_index), 0}, + {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, direction), 0}, }; /* BE DAI link */ From 7ef809df5d085996d43d1449fcd565537af181f1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 19 Oct 2018 12:57:01 +0100 Subject: [PATCH 250/298] ASoC: SOF: Intel HDA - cleanup CL on SKL Cleaned up for easier debug. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-loader-skl.c | 218 ++++++++++++++++----------- sound/soc/sof/intel/hda.h | 1 + 2 files changed, 127 insertions(+), 92 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c index c5cce851dabf4b..6a97e591e9741b 100644 --- a/sound/soc/sof/intel/hda-loader-skl.c +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -175,13 +175,14 @@ static void cl_skl_cldma_stream_run(struct snd_sof_dev *sdev, bool enable) } while (--timeout); if (timeout == 0) - dev_err(sdev->dev, "Failed to set Run bit=%d enable=%d\n", + dev_err(sdev->dev, "error: failed to set Run bit=%d enable=%d\n", val, enable); } static void cl_skl_cldma_stream_clear(struct snd_sof_dev *sdev) { int sd_offset = SOF_HDA_ADSP_LOADER_BASE; + /* make sure Run bit is cleared before setting stream register */ cl_skl_cldma_stream_run(sdev, 0); @@ -293,18 +294,20 @@ static int cl_stream_prepare_skl(struct snd_sof_dev *sdev) ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, bufsize, &sdev->dmab); if (ret < 0) { - dev_err(sdev->dev, "Alloc base fw buffer failed: %x\n", ret); + dev_err(sdev->dev, "error: failed to alloc fw buffer: %x\n", + ret); return ret; } + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, bufsize, &sdev->dmab_bdl); if (ret < 0) { - dev_err(sdev->dev, "Alloc buffer for blde failed: %x\n", ret); + dev_err(sdev->dev, "error: failed to alloc blde: %x\n", ret); snd_dma_free_pages(&sdev->dmab); return ret; } - bdl = (u32 *)sdev->dmab_bdl.area; + bdl = (u32 *)sdev->dmab_bdl.area; frags = cl_skl_cldma_setup_bdle(sdev, &sdev->dmab, &bdl, bufsize, 1); cl_skl_cldma_setup_controller(sdev, &sdev->dmab_bdl, bufsize, frags); @@ -329,6 +332,7 @@ static int cl_dsp_init_skl(struct snd_sof_dev *sdev) * if not, powerdown and enable it again. */ if (hda_dsp_core_is_enabled(sdev, HDA_DSP_CORE_MASK(0))) { + /* if enabled, reset it, and run the core. */ ret = hda_dsp_core_stall_reset(sdev, HDA_DSP_CORE_MASK(0)); if (ret < 0) @@ -395,72 +399,68 @@ static int cl_dsp_init_skl(struct snd_sof_dev *sdev) return ret; } -static int cl_copy_fw_skl(struct snd_sof_dev *sdev); -int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) +static void cl_skl_cldma_fill_buffer(struct snd_sof_dev *sdev, + unsigned int bufsize, + unsigned int copysize, + const void *curr_pos, + bool intr_enable) { - int ret; - - ret = cl_dsp_init_skl(sdev); - - /* retry enabling core and ROM load. seemed to help */ - if (ret < 0) { - ret = cl_dsp_init_skl(sdev); - if (ret < 0) { - dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_ERROR), - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_STATUS)); - dev_err(sdev->dev, "Core En/ROM load fail:%d\n", ret); - goto irq_err; - } - } - - dev_dbg(sdev->dev, "ROM init successful\n"); - - /* init for booting wait */ - init_waitqueue_head(&sdev->boot_wait); - sdev->boot_complete = false; - - /* at this point DSP ROM has been initialized and should be ready for - * code loading and firmware boot - */ - ret = cl_copy_fw_skl(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: load firmware failed : %d\n", ret); - goto irq_err; - } + /* 1. copy the image into the buffer with the maximum buffer size. */ + unsigned int size = (bufsize == copysize) ? bufsize : copysize; - dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); + memcpy(sdev->dmab.area, curr_pos, size); - return ret; + /* 2. Setting the wait condition for every load. */ + sdev->code_loading = 1; -irq_err: - hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + /* 3. Set the interrupt. */ + if (intr_enable) + cl_skl_cldma_set_intr(sdev, true); - /* power down DSP */ - hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); - cl_cleanup_skl(sdev); + /* 4. Set the SPB. */ + cl_skl_cldma_setup_spb(sdev, size, true); - dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); - return ret; + /* 5. Trigger the code loading stream. */ + cl_skl_cldma_stream_run(sdev, true); } -int cl_skl_cldma_wait_interruptible(struct snd_sof_dev *sdev) +static int cl_skl_cldma_wait_interruptible(struct snd_sof_dev *sdev) { int ret = 0; if (!wait_event_timeout(sdev->waitq, - sdev->code_loading, + !sdev->code_loading, msecs_to_jiffies(HDA_SKL_WAIT_TIMEOUT))) { - dev_err(sdev->dev, "%s: Wait timeout\n", __func__); + dev_err(sdev->dev, "cldma copy timeout\n"); + dev_err(sdev->dev, "ROM code=0x%x: FW status=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); + + /* TODO: temp debug to be removed */ + dev_err(sdev->dev, "ADSPCS=0x%x: ADSPIC=0x%x: ADSPIS=0x%x INTCTL=0x%x INTSTS=0x%x PPCTL=0x%x PPSTS=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIC), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIS), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + SOF_HDA_INTCTL), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + SOF_HDA_INTSTS), + snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPCTL), + snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPSTS)); ret = -EIO; goto cleanup; } - dev_dbg(sdev->dev, "%s: Event wake\n", __func__); - if (sdev->code_loading != 1) { - dev_err(sdev->dev, "%s: DMA Error\n", __func__); + dev_dbg(sdev->dev, "cldma buffer copy complete\n"); + if (!sdev->code_loading) { + dev_err(sdev->dev, "error: cldma DMA copy failed\n"); ret = -EIO; } @@ -469,32 +469,6 @@ int cl_skl_cldma_wait_interruptible(struct snd_sof_dev *sdev) return ret; } -static void cl_skl_cldma_fill_buffer(struct snd_sof_dev *sdev, - unsigned int bufsize, - unsigned int copysize, - const void *curr_pos, - bool intr_enable, bool trigger) -{ - /* 1. copy the image into the buffer with the maximum buffer size. */ - unsigned int size = (bufsize == copysize) ? bufsize : copysize; - - memcpy(sdev->dmab.area, curr_pos, size); - - /* 2. Setting the wait condition for every load. */ - sdev->code_loading = 0; - - /* 3. Set the interrupt. */ - if (intr_enable) - cl_skl_cldma_set_intr(sdev, true); - - /* 4. Set the SPB. */ - cl_skl_cldma_setup_spb(sdev, size, trigger); - - /* 5. Trigger the code loading stream. */ - if (trigger) - cl_skl_cldma_stream_run(sdev, true); -} - static int cl_skl_cldma_copy_to_buf(struct snd_sof_dev *sdev, const void *bin, u32 total_size, u32 bufsize) @@ -506,23 +480,34 @@ cl_skl_cldma_copy_to_buf(struct snd_sof_dev *sdev, const void *bin, if (total_size <= 0) return -EINVAL; - dev_dbg(sdev->dev, "Total binary size: %u\n", total_size); - while (bytes_left > 0) { + if (bytes_left > bufsize) { + + dev_dbg(sdev->dev, "cldma copy 0x%x bytes\n", + bufsize); + cl_skl_cldma_fill_buffer(sdev, bufsize, bufsize, - curr_pos, true, true); + curr_pos, true); + ret = cl_skl_cldma_wait_interruptible(sdev); if (ret < 0) { + dev_err(sdev->dev, "error: fw failed to load. 0x%x bytes remaining\n", + bytes_left); cl_skl_cldma_stream_run(sdev, false); return ret; } + bytes_left -= bufsize; curr_pos += bufsize; } else { + + dev_dbg(sdev->dev, "cldma copy 0x%x bytes\n", + bytes_left); + cl_skl_cldma_set_intr(sdev, false); cl_skl_cldma_fill_buffer(sdev, bufsize, bytes_left, - curr_pos, false, true); + curr_pos, false); return 0; } } @@ -532,26 +517,25 @@ cl_skl_cldma_copy_to_buf(struct snd_sof_dev *sdev, const void *bin, static int cl_copy_fw_skl(struct snd_sof_dev *sdev) { - struct skl_ext_manifest_hdr *hdr; struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); - struct firmware stripped_fw; + const struct firmware *fw = plat_data->fw; unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE; int ret = 0; - stripped_fw.data = plat_data->fw->data; - stripped_fw.size = plat_data->fw->size; - dev_dbg(sdev->dev, "firmware size: %zu\n", stripped_fw.size); + dev_dbg(sdev->dev, "firmware size: 0x%zx buffer size 0x%x\n", fw->size, + bufsize); - ret = cl_skl_cldma_copy_to_buf(sdev, stripped_fw.data, - stripped_fw.size, bufsize); - if (ret < 0) + ret = cl_skl_cldma_copy_to_buf(sdev, fw->data, fw->size, bufsize); + if (ret < 0) { + dev_err(sdev->dev, "error: fw copy failed %d\n", ret); return ret; + } + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, HDA_SKL_ADSP_FW_STATUS, HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_FW_FW_LOADED, HDA_DSP_BASEFW_TIMEOUT); - if (ret < 0) dev_err(sdev->dev, "firmware transfer timeout!"); @@ -560,3 +544,53 @@ static int cl_copy_fw_skl(struct snd_sof_dev *sdev) return ret; } + +int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) +{ + int ret; + + ret = cl_dsp_init_skl(sdev); + + /* retry enabling core and ROM load. seemed to help */ + if (ret < 0) { + ret = cl_dsp_init_skl(sdev); + if (ret < 0) { + dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); + dev_err(sdev->dev, "Core En/ROM load fail:%d\n", ret); + goto irq_err; + } + } + + dev_dbg(sdev->dev, "ROM init successful\n"); + + /* init for booting wait */ + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + /* at this point DSP ROM has been initialized and should be ready for + * code loading and firmware boot + */ + ret = cl_copy_fw_skl(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: load firmware failed : %d\n", ret); + goto irq_err; + } + + dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); + + return ret; + +irq_err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + + /* power down DSP */ + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0)); + cl_cleanup_skl(sdev); + + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + return ret; +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 5e27345022a86d..e0df277387b44a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -52,6 +52,7 @@ #define SOF_HDA_PP_CAP_ID 0x3 #define SOF_HDA_REG_PP_PPCH 0x10 #define SOF_HDA_REG_PP_PPCTL 0x04 +#define SOF_HDA_REG_PP_PPSTS 0x08 #define SOF_HDA_PPCTL_PIE BIT(31) #define SOF_HDA_PPCTL_GPROCEN BIT(30) From 60c282ca71cdcc6fbd53206fa79c9bf793f96974 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 22 Oct 2018 17:11:14 +0800 Subject: [PATCH 251/298] [SQUASHME] ASoC: SOF: fix a return type error. Signed-off-by: Keyon Jie --- sound/soc/sof/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 89367a826a05a7..5aeb6ea3c58b2b 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -152,7 +152,7 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) int ret; if (sdev->dtrace_is_enabled) - return; + return -EINVAL; /* set IPC parameters */ params.hdr.size = sizeof(params); From 2996b7e8cd0dcaef3bfd6304982772f19ab0703a Mon Sep 17 00:00:00 2001 From: ArturX Kloniecki Date: Tue, 9 Oct 2018 14:09:08 +0200 Subject: [PATCH 252/298] SOF: Update uapi/sof-abi.h header to conform to current SOF master Signed-off-by: ArturX Kloniecki --- include/uapi/sound/sof-abi.h | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/sof-abi.h b/include/uapi/sound/sof-abi.h index ff208d2f7573ee..665cdadb440ce3 100644 --- a/include/uapi/sound/sof-abi.h +++ b/include/uapi/sound/sof-abi.h @@ -9,7 +9,28 @@ #ifndef __INCLUDE_UAPI_ABI_H__ #define __INCLUDE_UAPI_ABI_H__ -#define SOF_ABI_VERSION 1 +#define SOF_ABI_VER(major, minor, micro) \ + (((major) << 8) | ((minor) << 4) | (micro)) +#define SOF_ABI_VERSION_MAJOR(version) (((version) >> 8) & 0xff) +#define SOF_ABI_VERSION_MINOR(version) (((version) >> 4) & 0xf) +#define SOF_ABI_VERSION_MICRO(version) ((version) & 0xf) +#define SOF_ABI_VERSION_INCOMPATIBLE(sof_ver, client_ver) \ + (SOF_ABI_VERSION_MAJOR((sof_ver)) != \ + SOF_ABI_VERSION_MAJOR((client_ver)) || \ + ( \ + SOF_ABI_VERSION_MAJOR((sof_ver)) == \ + SOF_ABI_VERSION_MAJOR((client_ver)) && \ + SOF_ABI_VERSION_MINOR((sof_ver)) != \ + SOF_ABI_VERSION_MINOR((client_ver)) \ + ) \ + ) + +#define SOF_ABI_MAJOR 1 +#define SOF_ABI_MINOR 0 +#define SOF_ABI_MICRO 0 + +#define SOF_ABI_VERSION SOF_ABI_VER(SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_MICRO) + #define SOF_ABI_MAGIC 0x00464F53 /* "SOF\0" */ /* From 5b3990454d17ebde13d3b10173de760c0e9115c6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 14:59:51 -0500 Subject: [PATCH 253/298] Revert "sound: fix build on Alpha" This reverts commit 51ebd5d01e6d26ff7f2103417db60b37ad734776. Signed-off-by: Pierre-Louis Bossart --- include/sound/memalloc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 6a7d6b8a4d342c..3f8e6eade1ef52 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -24,8 +24,6 @@ #ifndef __SOUND_MEMALLOC_H #define __SOUND_MEMALLOC_H -#include - struct device; /* From a979c3100a8dc30fae96ec94ce3ad826983411f9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 15:01:24 -0500 Subject: [PATCH 254/298] Revert "sound: snd_sgbuf_aligned_pages() doesn't need CONFIG_SND_DMA_SGBUF" This reverts commit 66dc735d45f64fcfc868c3772bb2c4c52446c000. Signed-off-by: Pierre-Louis Bossart --- include/sound/memalloc.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 3f8e6eade1ef52..9c3db3dce32b4a 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -90,6 +90,14 @@ struct snd_sg_buf { struct device *dev; }; +/* + * return the pages matching with the given byte size + */ +static inline unsigned int snd_sgbuf_aligned_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + /* * return the physical address at the corresponding offset */ @@ -132,14 +140,6 @@ static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab, #endif /* CONFIG_SND_DMA_SGBUF */ -/* - * return the pages matching with the given byte size - */ -static inline unsigned int snd_sgbuf_aligned_pages(size_t size) -{ - return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; -} - /* allocate/release a buffer */ int snd_dma_alloc_pages(int type, struct device *dev, size_t size, struct snd_dma_buffer *dmab); From 57cff61ed61969215fe4ba1b769f5f39407fdf07 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 25 Jul 2018 15:15:56 -0500 Subject: [PATCH 255/298] ALSA: memalloc: declare snd_sgbuf_aligned_pages() unconditionally Make this helper inline function available for all platforms. This helps solve 0-day compilation issues when CONFIG_SND_DMA_SGBUF is not defined. Reported-by: kbuild test robot Signed-off-by: Pierre-Louis Bossart Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown (cherry picked from commit 4cae99d9b5305ab8cccc839fccceb81ec9e5abda) --- include/sound/memalloc.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 9c3db3dce32b4a..c669900e6cbe66 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -67,6 +67,14 @@ struct snd_dma_buffer { void *private_data; /* private for allocator; don't touch */ }; +/* + * return the pages matching with the given byte size + */ +static inline unsigned int snd_sgbuf_aligned_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + #ifdef CONFIG_SND_DMA_SGBUF /* * Scatter-Gather generic device pages @@ -90,14 +98,6 @@ struct snd_sg_buf { struct device *dev; }; -/* - * return the pages matching with the given byte size - */ -static inline unsigned int snd_sgbuf_aligned_pages(size_t size) -{ - return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; -} - /* * return the physical address at the corresponding offset */ From c66eb49323a84fcefc21c5fd079d5c60851fe48b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 30 Jul 2018 08:46:40 +0200 Subject: [PATCH 256/298] ALSA: memalloc: Fix missing PAGE_SIZE definition The recent fix moved the inline snd_sgbuf_aligned_pages() outside the ifdef, and this triggered a build error on some architectures due to the undefined PAGE_SIZE, as spotted by 0day bot. Fix it by adding the missing header inclusion. Fixes: 4cae99d9b530 ("ALSA: memalloc: declare snd_sgbuf_aligned_pages() unconditionally") Reported-by: kbuild test robot Cc: Pierre-Louis Bossart Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown (cherry picked from commit 345a9ca37aa69bb3133c1a8390a71f993abcef0c) --- include/sound/memalloc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index c669900e6cbe66..67561b99791508 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -24,6 +24,8 @@ #ifndef __SOUND_MEMALLOC_H #define __SOUND_MEMALLOC_H +#include + struct device; /* From 3d450b4c10b4ceffdfd4117eed1063937ce68734 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:04:09 -0500 Subject: [PATCH 257/298] ASoC: SOF: sof.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- include/sound/sof.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 9313b4f74cc9ba..7fe85c0462a185 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -1,12 +1,12 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ #ifndef __INCLUDE_SOUND_SOF_H #define __INCLUDE_SOUND_SOF_H From 67a8373a0ca826261484b46657c6a3b294d0b29c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:05:43 -0500 Subject: [PATCH 258/298] ASoC: SOF: sof-abi.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-abi.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/uapi/sound/sof-abi.h b/include/uapi/sound/sof-abi.h index 665cdadb440ce3..88c61f8c2a4a7a 100644 --- a/include/uapi/sound/sof-abi.h +++ b/include/uapi/sound/sof-abi.h @@ -1,10 +1,10 @@ -// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + */ #ifndef __INCLUDE_UAPI_ABI_H__ #define __INCLUDE_UAPI_ABI_H__ From 846b55847f933c163bd7659c862199277617115a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:06:22 -0500 Subject: [PATCH 259/298] ASoC: SOF: sof-eq.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-eq.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/uapi/sound/sof-eq.h b/include/uapi/sound/sof-eq.h index 081a68a512e696..bd82fea5206ed0 100644 --- a/include/uapi/sound/sof-eq.h +++ b/include/uapi/sound/sof-eq.h @@ -1,12 +1,12 @@ -// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Seppo Ingalsuo -// +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ #ifndef EQ_H #define EQ_H From 6da76cef90ce5e5a9e5892ec3a3b8f6bdceb24e6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:07:23 -0500 Subject: [PATCH 260/298] ASoC: SOF: sof-fw.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-fw.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/uapi/sound/sof-fw.h b/include/uapi/sound/sof-fw.h index 3bf86ce00f93f3..e55ea88ee69607 100644 --- a/include/uapi/sound/sof-fw.h +++ b/include/uapi/sound/sof-fw.h @@ -1,10 +1,10 @@ -// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + */ /* * Firmware file format . From 38bcbeff3afbd1e83eeb0bde13a75ac4ac5d001a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:07:53 -0500 Subject: [PATCH 261/298] ASoC: SOF: sof-ipc.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-ipc.h | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index a7bcffb7643044..c2cd888c7f3d51 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -1,13 +1,14 @@ -// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// Keyon Jie -// +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ + +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ #ifndef __INCLUDE_UAPI_SOF_IPC_H__ #define __INCLUDE_UAPI_SOF_IPC_H__ From 88deb909b076586d265cbe9ee0f9a31c96834b81 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:09:07 -0500 Subject: [PATCH 262/298] ASoC: SOF: sof-tone.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-tone.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/uapi/sound/sof-tone.h b/include/uapi/sound/sof-tone.h index 81f7bc1eca8e88..ce258cd7ef70ec 100644 --- a/include/uapi/sound/sof-tone.h +++ b/include/uapi/sound/sof-tone.h @@ -1,11 +1,11 @@ -// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// Author: Seppo Ingalsuo -// +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * Author: Seppo Ingalsuo + */ #ifndef TONE_H #define TONE_H From a5ef04080dcb2b1bc9b52040004c843a98ea925f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:09:39 -0500 Subject: [PATCH 263/298] ASoC: SOF: sof-topology.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-topology.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index f83524cf1c5e3d..3be53a5512b354 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -1,12 +1,12 @@ -// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// Author: Liam Girdwood -// Keyon Jie -// +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * Author: Liam Girdwood + * Keyon Jie + */ /* * Topology IDs and tokens. From a5a49550ccb7b38ae65ca44ac04432424422f2ce Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:10:10 -0500 Subject: [PATCH 264/298] ASoC: SOF: control.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/control.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 6f73041760f861..7b72361c3baee1 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -333,7 +333,6 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_DATA, SOF_CTRL_TYPE_DATA_SET, scontrol->cmd); - out: pm_runtime_mark_last_busy(sdev->dev); err = pm_runtime_put_autosuspend(sdev->dev); From f4fbd11e4e7da99bd6d6db464cea75c576322fad Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:10:34 -0500 Subject: [PATCH 265/298] ASoC: SOF: hw-spi.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/hw-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c index 71325c44031156..75b8ff857dc7ce 100644 --- a/sound/soc/sof/hw-spi.c +++ b/sound/soc/sof/hw-spi.c @@ -212,7 +212,7 @@ static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) } else { /* reply correct size ? */ if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected 0x%zu got 0x%x bytes\n", + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", msg->reply_size, reply.hdr.size); size = msg->reply_size; ret = -EINVAL; From 75132f32906410ea3d94b8e64cae21270166d8ce Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:10:57 -0500 Subject: [PATCH 266/298] ASoC: SOF: hda-bus.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c index 0942ffb9110fa4..0973ef85291033 100644 --- a/sound/soc/sof/intel/hda-bus.c +++ b/sound/soc/sof/intel/hda-bus.c @@ -51,13 +51,13 @@ static u8 sof_hda_readb(u8 __iomem *addr) } static int sof_hda_dma_alloc_pages(struct hdac_bus *bus, int type, - size_t size, struct snd_dma_buffer *buf) + size_t size, struct snd_dma_buffer *buf) { return snd_dma_alloc_pages(type, bus->dev, size, buf); } static void sof_hda_dma_free_pages(struct hdac_bus *bus, - struct snd_dma_buffer *buf) + struct snd_dma_buffer *buf) { snd_dma_free_pages(buf); } From 7aece1669ac9bdd635c2ebb113372f578c90a0d9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:11:23 -0500 Subject: [PATCH 267/298] ASoC: SOF: hda-codec.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 6971e0e2d3adc7..fbf64635b309e6 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -68,7 +68,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) address, resp); hda_priv = devm_kzalloc(&hbus->pci->dev, sizeof(*hda_priv), - GFP_KERNEL); + GFP_KERNEL); if (!hda_priv) return -ENOMEM; From 630df0f6ea94d75edfc12c8ebb5d93fe0fedfe90 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:11:55 -0500 Subject: [PATCH 268/298] ASoC: SOF: hda-loader-skl.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-loader-skl.c | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c index 6a97e591e9741b..ca8a5bd3dd9abe 100644 --- a/sound/soc/sof/intel/hda-loader-skl.c +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -433,27 +433,27 @@ static int cl_skl_cldma_wait_interruptible(struct snd_sof_dev *sdev) msecs_to_jiffies(HDA_SKL_WAIT_TIMEOUT))) { dev_err(sdev->dev, "cldma copy timeout\n"); dev_err(sdev->dev, "ROM code=0x%x: FW status=0x%x\n", - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_ERROR), - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_STATUS)); + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); /* TODO: temp debug to be removed */ dev_err(sdev->dev, "ADSPCS=0x%x: ADSPIC=0x%x: ADSPIS=0x%x INTCTL=0x%x INTSTS=0x%x PPCTL=0x%x PPSTS=0x%x\n", - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPCS), - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPIC), - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPIS), - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - SOF_HDA_INTCTL), - snd_sof_dsp_read(sdev, HDA_DSP_BAR, - SOF_HDA_INTSTS), - snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, - SOF_HDA_REG_PP_PPCTL), - snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, - SOF_HDA_REG_PP_PPSTS)); + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIC), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIS), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + SOF_HDA_INTCTL), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + SOF_HDA_INTSTS), + snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPCTL), + snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPSTS)); ret = -EIO; goto cleanup; } From 0ca9ff0c0e1fcf3547d0bf74b5fff9db7068b19e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:12:28 -0500 Subject: [PATCH 269/298] ASoC: SOF: hda-loader.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-loader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 796d772a6a8d30..76ac08d8d0883e 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -257,8 +257,8 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) /* get stream with tag */ list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == SNDRV_PCM_STREAM_PLAYBACK - && s->stream_tag == tag) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK && + s->stream_tag == tag) { stream = stream_to_hdac_ext_stream(s); break; } From 7459a12cb0fcc8ba0cfca6e1b5a648678806e944 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:13:04 -0500 Subject: [PATCH 270/298] ASoC: SOF: hda-stream.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-stream.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index a9189058f5dfc4..42a986ce35a08f 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -123,7 +123,7 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, !stream->no_period_wakeup : 0; for (i = 0; i < periods; i++) { - if (i == periods - 1 && remain) + if (i == (periods - 1) && remain) /* set the last small entry */ offset = hda_setup_bdle(sdev, dmab, stream, &bdl, offset, @@ -162,7 +162,6 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, return 0; } - /* get next unused stream */ struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) @@ -199,8 +198,8 @@ hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) /* get an unused playback stream */ list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == SNDRV_PCM_STREAM_PLAYBACK - && !s->opened) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK && + !s->opened) { s->opened = true; stream = stream_to_hdac_ext_stream(s); break; @@ -224,8 +223,8 @@ hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) /* get an unused capture stream */ list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == SNDRV_PCM_STREAM_CAPTURE - && !s->opened) { + if (s->direction == SNDRV_PCM_STREAM_CAPTURE && + !s->opened) { s->opened = true; stream = stream_to_hdac_ext_stream(s); break; @@ -247,8 +246,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int tag) /* find used stream */ list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == direction - && s->opened && s->stream_tag == tag) { + if (s->direction == direction && + s->opened && s->stream_tag == tag) { s->opened = false; return 0; } @@ -266,8 +265,8 @@ int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) /* find used playback stream */ list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == SNDRV_PCM_STREAM_PLAYBACK - && s->opened && s->stream_tag == tag) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK && + s->opened && s->stream_tag == tag) { s->opened = false; return 0; } @@ -285,8 +284,8 @@ int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int tag) /* find used capture stream */ list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == SNDRV_PCM_STREAM_CAPTURE - && s->opened && s->stream_tag == tag) { + if (s->direction == SNDRV_PCM_STREAM_CAPTURE && + s->opened && s->stream_tag == tag) { s->opened = false; return 0; } @@ -423,7 +422,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0x0); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0x0); /* clear stream status */ @@ -545,8 +544,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) /* check streams */ list_for_each_entry(s, &bus->stream_list, list) { - if (status & (1 << s->index) - && s->opened) { + if (status & (1 << s->index) && s->opened) { sd_status = snd_hdac_stream_readb(s, SD_STS); dev_vdbg(bus->dev, "stream %d status 0x%x\n", From 356dd5e0edbc928322814f6aa9122834c4d32fe7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:13:31 -0500 Subject: [PATCH 271/298] ASoC: SOF: hda.c: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 26b26be61d251b..cd8962eb7994c8 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -363,7 +363,7 @@ static int hda_init(struct snd_sof_dev *sdev) /* initialise hdac bus */ bus->addr = pci_resource_start(pci, 0); bus->remap_addr = pci_ioremap_bar(pci, 0); - if (bus->remap_addr == NULL) { + if (!bus->remap_addr) { dev_err(bus->dev, "ioremap error\n"); return -ENXIO; } @@ -623,10 +623,10 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) list_for_each_entry(stream, &bus->stream_list, list) { sd_offset = SOF_STREAM_SD_OFFSET(stream); snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); + sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); } /* clear WAKESTS */ From 4193dd74020c4e9ef00872739f3a2c73951ebd1f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:13:49 -0500 Subject: [PATCH 272/298] ASoC: SOF: hda.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index e0df277387b44a..bcd95c1e35e881 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -496,7 +496,7 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset); * HDA bus operations. */ int sof_hda_bus_init(struct hdac_bus *bus, struct device *dev, - const struct hdac_ext_bus_ops *ext_ops); + const struct hdac_ext_bus_ops *ext_ops); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* From 37eabe41c3fb7c8a0b9e2dc5a518e941a0f4431b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:14:08 -0500 Subject: [PATCH 273/298] ASoC: SOF: ops.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/ops.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index f033bb6f06d42c..5bd6e3921b5510 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -1,12 +1,12 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ #ifndef __SOUND_SOC_SOF_IO_H #define __SOUND_SOC_SOF_IO_H From 19fbfb0d760819a46335067f6ac26b383de77076 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:14:37 -0500 Subject: [PATCH 274/298] ASoC: SOF: sof-priv.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/sof-priv.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 98af791ed12e65..f0358d50ebbfe9 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -1,12 +1,12 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ #ifndef __SOUND_SOC_SOF_PRIV_H #define __SOUND_SOC_SOF_PRIV_H From ccaa1bb60a37bd90e060af1cad77e93e7f59c274 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:14:58 -0500 Subject: [PATCH 275/298] ASoC: SOF: sof-priv.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/topology.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 696a4b076025b4..7e8ae449e3d5c4 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -872,7 +872,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, list_for_each_entry(rtd, &card->rtd_list, list) { dev_vdbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", - w->name, w->sname, rtd->dai_link->stream_name); + w->name, w->sname, rtd->dai_link->stream_name); if (!w->sname || !rtd->dai_link->stream_name) continue; @@ -2313,7 +2313,8 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, } if (route->control) { - sroute->route.control = kstrdup(route->control, GFP_KERNEL); + sroute->route.control = kstrdup(route->control, + GFP_KERNEL); if (!sroute->route.control) { kfree(sroute->route.source); kfree(sroute->route.sink); From f677a1a75771c772d7edd932fe9c8ba042ab2ad1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 17:15:28 -0500 Subject: [PATCH 276/298] ASoC: SOF: virtio-miscdev.h: fix style issues Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/virtio-miscdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/virtio-miscdev.h b/sound/soc/sof/virtio-miscdev.h index cce0ac7876cbea..afdb8db88e2b20 100644 --- a/sound/soc/sof/virtio-miscdev.h +++ b/sound/soc/sof/virtio-miscdev.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ /* * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. From 51c855ff233f95c1093c4ab313a7334db0a04e07 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 10:57:01 -0500 Subject: [PATCH 277/298] ASoC: SOF: ipc: align pm_core_config with firmware no new code, just add definitions Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-ipc.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index c2cd888c7f3d51..d7815e2aada3ad 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -70,6 +70,7 @@ #define SOF_IPC_PM_CLK_SET SOF_CMD_TYPE(0x004) #define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005) #define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006) +#define SOF_IPC_PM_CORE_ENABLE SOF_CMD_TYPE(0x007) /* component runtime config - multiple different types */ #define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001) @@ -823,6 +824,12 @@ struct sof_ipc_pm_ctx { struct sof_ipc_pm_ctx_elem elems[]; }; +/* enable or disable cores - SOF_IPC_PM_CORE_ENABLE */ +struct sof_ipc_pm_core_config { + struct sof_ipc_hdr hdr; + uint32_t enable_mask; +}; + /* * Firmware boot and version */ From 6c3c420437b9cc2418d46d5d1cf7ab7fb4049ae8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 10:59:03 -0500 Subject: [PATCH 278/298] ASoC: SOF: ipc: align ssp_config with firmware Clock controls is now a single field. This change impacts topology files due to a renumbering of the tokens. Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-ipc.h | 25 ++++++++++++++++++++----- include/uapi/sound/sof-topology.h | 14 ++++++-------- sound/soc/sof/topology.c | 12 +++--------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index d7815e2aada3ad..aaf80ebf5435e9 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -219,6 +219,24 @@ struct sof_ipc_compound_hdr { #define SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX 38 #define SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX 31 +/* SSP clocks control settings + * + * Macros for clks_control field in sof_ipc_dai_ssp_params struct. + */ + +/* mclk 0 disable */ +#define SOF_DAI_INTEL_SSP_MCLK_0_DISABLE BIT(0) +/* mclk 1 disable */ +#define SOF_DAI_INTEL_SSP_MCLK_1_DISABLE BIT(1) +/* mclk keep active */ +#define SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_KA BIT(2) +/* bclk keep active */ +#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_KA BIT(3) +/* fs keep active */ +#define SOF_DAI_INTEL_SSP_CLKCTRL_FS_KA BIT(4) +/* bclk idle */ +#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH BIT(5) + /** \brief Types of DAI */ enum sof_ipc_dai_type { SOF_DAI_INTEL_NONE = 0, /**< None */ @@ -248,14 +266,11 @@ struct sof_ipc_dai_ssp_params { /* MCLK */ uint32_t mclk_direction; - uint32_t mclk_keep_active; - uint32_t bclk_keep_active; - uint32_t fs_keep_active; uint16_t frame_pulse_width; - uint32_t quirks; // FIXME: is 32 bits enough ? - uint16_t tdm_per_slot_padding_flag; + uint32_t clks_control; + uint32_t quirks; // FIXME: is 32 bits enough ? /* private data, e.g. for quirks */ //uint32_t pdata[10]; // FIXME: would really need ~16 u32 } __attribute__((packed)); diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 3be53a5512b354..0db5253a37bf35 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -64,14 +64,12 @@ #define SOF_TKN_COMP_PRELOAD_COUNT 403 /* SSP */ -#define SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE 500 -#define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 -#define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 -#define SOF_TKN_INTEL_SSP_MCLK_ID 503 -#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 504 -#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 505 -#define SOF_TKN_INTEL_SSP_QUIRKS 506 -#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 507 +#define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 +#define SOF_TKN_INTEL_SSP_MCLK_ID 501 +#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 502 +#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 503 +#define SOF_TKN_INTEL_SSP_QUIRKS 504 +#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 505 /* DMIC */ #define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 7e8ae449e3d5c4..1ecba1a2a0b464 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -486,15 +486,9 @@ static const struct sof_topology_token comp_tokens[] = { /* SSP */ static const struct sof_topology_token ssp_tokens[] = { - {SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE, - SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, mclk_keep_active), 0}, - {SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE, - SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, bclk_keep_active), 0}, - {SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE, - SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, fs_keep_active), 0}, + {SOF_TKN_INTEL_SSP_CLKS_CONTROL, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, clks_control), 0}, {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, From e05df1ff88b651c854c0ccf77a5ee29b8482b7c5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 11:01:58 -0500 Subject: [PATCH 279/298] ASoC: SOF: ipc: align eq_config with firmware EQ ipc was using int as size, so change it to unsigned as it won't ever be negative. Also fix effect enum type none. Signed-off-by: Jaska Uimonen Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-ipc.h | 6 +++--- sound/soc/sof/topology.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index aaf80ebf5435e9..6c88b43deb370f 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -741,7 +741,7 @@ struct sof_ipc_comp_tone { struct sof_ipc_comp_eq_fir { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; - int32_t size; + uint32_t size; unsigned char data[0]; } __attribute__((packed)); @@ -749,13 +749,13 @@ struct sof_ipc_comp_eq_fir { struct sof_ipc_comp_eq_iir { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; - int32_t size; + uint32_t size; unsigned char data[0]; } __attribute__((packed)); /** \brief Types of EFFECT */ enum sof_ipc_effect_type { - SOF_EFFECT_INTEL_NONE = 0, /**< None */ + SOF_EFFECT_NONE = 0, /**< None */ SOF_EFFECT_INTEL_EQFIR, /**< Intel FIR */ SOF_EFFECT_INTEL_EQIIR, /**< Intel IIR */ }; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 1ecba1a2a0b464..36c2278cd4825b 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -252,7 +252,7 @@ static enum sof_ipc_effect_type find_effect(const char *name) return sof_effects[i].type; } - return SOF_EFFECT_INTEL_NONE; + return SOF_EFFECT_NONE; } /* From 3ead65ef7b37e429060132057e1e7d51f2a31259 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 11:22:21 -0500 Subject: [PATCH 280/298] ASoC: SOF: ipc: align ipc_pipe config with firmware s/timer/timer_delay Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-ipc.h | 4 +++- sound/soc/sof/topology.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 6c88b43deb370f..f3fbf12bd3c03e 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -795,7 +795,9 @@ struct sof_ipc_pipe_new { uint32_t mips; /* worst case instruction count per period */ uint32_t frames_per_sched;/* output frames of pipeline, 0 is variable */ uint32_t xrun_limit_usecs; /* report xruns greater than limit */ - uint32_t timer;/* non zero if timer scheduled otherwise DAI scheduled */ + + /* non zero if timer scheduled, otherwise DAI DMA irq scheduled */ + uint32_t timer_delay; } __attribute__((packed)); /* pipeline construction complete - SOF_IPC_TPLG_PIPE_COMPLETE */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 36c2278cd4825b..68e88faca445da 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -431,7 +431,7 @@ static const struct sof_topology_token sched_tokens[] = { {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0}, {SOF_TKN_SCHED_TIMER, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, timer), 0}, + offsetof(struct sof_ipc_pipe_new, timer_delay), 0}, }; /* volume */ From 1c56e46bee924948da3efbdafb78b9f20766d0cf Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Oct 2018 11:23:29 -0500 Subject: [PATCH 281/298] ASoC: SOF: ipc: align fw_version with firmware reclaim padding for abi_version Signed-off-by: Pierre-Louis Bossart --- include/uapi/sound/sof-ipc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index f3fbf12bd3c03e..5b042079ea9595 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -867,7 +867,8 @@ struct sof_ipc_fw_version { uint8_t date[12]; uint8_t time[10]; uint8_t tag[6]; - uint8_t pad[2]; /* Make sure the total size is 4 bytes aligned */ + uint16_t abi_version; + /* Make sure the total size is 4 bytes aligned */ } __attribute__((packed)); /* FW ready Message - sent by firmware when boot has completed */ From f7f73dd940ddccc1dc65686904794916c44d4abd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 24 Oct 2018 11:21:03 -0500 Subject: [PATCH 282/298] Revert "ASoC: SOF: virtio-miscdev.h: fix style issues" This reverts commit f677a1a75771c772d7edd932fe9c8ba042ab2ad1. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/virtio-miscdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/virtio-miscdev.h b/sound/soc/sof/virtio-miscdev.h index afdb8db88e2b20..cce0ac7876cbea 100644 --- a/sound/soc/sof/virtio-miscdev.h +++ b/sound/soc/sof/virtio-miscdev.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. From 23f507e10bdf04ef0dbc3828735654027adcbede Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 24 Oct 2018 11:21:59 -0500 Subject: [PATCH 283/298] Revert "ASoC: SOF: add sof vBE and vFE support" This reverts commit 481973cf6ca7ac2af9e246fe2f2daa09254cc112. Signed-off-by: Pierre-Louis Bossart --- include/sound/sof.h | 4 - include/uapi/sound/sof-virtio.h | 42 - sound/soc/intel/boards/bxt_tdf8532.c | 12 - sound/soc/sof/Makefile | 10 - sound/soc/sof/core.c | 33 - sound/soc/sof/ipc.c | 3 - sound/soc/sof/pcm.c | 1 - sound/soc/sof/sof-priv.h | 94 --- sound/soc/sof/virtio-be.c | 1144 ++------------------------ sound/soc/sof/virtio-be.h | 51 -- sound/soc/sof/virtio-fe.c | 430 ++-------- sound/soc/sof/virtio-fe.h | 28 - sound/soc/sof/virtio-miscdev.c | 252 ------ sound/soc/sof/virtio-miscdev.h | 24 - 14 files changed, 128 insertions(+), 2000 deletions(-) delete mode 100644 include/uapi/sound/sof-virtio.h delete mode 100644 sound/soc/sof/virtio-be.h delete mode 100644 sound/soc/sof/virtio-fe.h delete mode 100644 sound/soc/sof/virtio-miscdev.c delete mode 100644 sound/soc/sof/virtio-miscdev.h diff --git a/include/sound/sof.h b/include/sound/sof.h index 7fe85c0462a185..06f5d9d015b046 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -22,7 +22,6 @@ #include struct snd_sof_dsp_ops; -struct virtio_device; /* SOF probe type */ enum sof_device_type { @@ -54,9 +53,6 @@ struct snd_sof_pdata { struct device *dev; enum sof_device_type type; - struct sof_vfe *vfe; - int is_vfe; - /* descriptor */ const struct sof_dev_desc *desc; diff --git a/include/uapi/sound/sof-virtio.h b/include/uapi/sound/sof-virtio.h deleted file mode 100644 index 2e4ac66453b6e6..00000000000000 --- a/include/uapi/sound/sof-virtio.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Contact Information: - * Author: Luo Xionghu - * Liam Girdwood . - */ - -#ifndef _SOF_VIRTIO_H -#define _SOF_VIRTIO_H - -/* Currently we defined 4 vqs to do the IPC, CMD_TX is for send the msg - * from FE to BE, and CMD_RX is to receive the reply. NOT_RX is to receive - * the notification, and NOT_TX is to send empty buffer from FE to BE. - * If we can handle the IPC with only 2 vqs, the config still need to - * be changed in the device model(VM config), then only CMD_VQ and NOT_VQ - * is needed. - */ - -#define SOF_VIRTIO_IPC_CMD_TX_VQ 0 -#define SOF_VIRTIO_IPC_CMD_RX_VQ 1 -#define SOF_VIRTIO_IPC_NOT_TX_VQ 2 -#define SOF_VIRTIO_IPC_NOT_RX_VQ 3 -#define SOF_VIRTIO_NUM_OF_VQS 4 - -/* command messages from FE to BE, trigger/open/hw_params and so on */ -#define SOF_VIRTIO_IPC_CMD_TX_VQ_NAME "sof-ipc-cmd-tx" - -/* get the reply of the command message */ -#define SOF_VIRTIO_IPC_CMD_RX_VQ_NAME "sof-ipc-cmd-rx" - -/* first the FE need send empty buffer to BE to get the notification */ -#define SOF_VIRTIO_IPC_NOT_TX_VQ_NAME "sof-ipc-not-tx" - -/* the vq to get the notification */ -#define SOF_VIRTIO_IPC_NOT_RX_VQ_NAME "sof-ipc-not-rx" - -#endif diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 4ccd013c6e5710..336205309e2c8b 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -205,18 +205,6 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .dpcm_playback = 1, .no_pcm = 1, }, - { - /* SSP4 for vm */ - .name = "vm_dai_link", - .id = 6, - .cpu_dai_name = "SSP4 Pin", - .codec_name = "i2c-INT34C3:00", - .codec_dai_name = "tdf8532-hifi", - .platform_name = "0000:00:0e.0", - .ignore_suspend = 1, - .dpcm_playback = 1, - .no_pcm = 1, - }, }; #if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 934ae19f258fd1..b8f6c67fbb7b70 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -4,15 +4,6 @@ ccflags-y += -DDEBUG snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o compressed.o utils.o - -ifdef CONFIG_SND_SOC_SOF_VIRTIO_BE -snd-sof-objs += virtio-be.o virtio-miscdev.o -endif - -ifdef CONFIG_SND_SOC_SOF_VIRTIO_FE -snd-virtio-fe-objs += virtio-fe.o -endif - snd-sof-spi-objs := hw-spi.o snd-sof-pci-objs := sof-pci-dev.o @@ -25,7 +16,6 @@ obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF_ACPI) += sof-acpi-dev.o obj-$(CONFIG_SND_SOC_SOF_PCI) += sof-pci-dev.o obj-$(CONFIG_SND_SOC_SOF_SPI) += sof-spi-dev.o -obj-$(CONFIG_SND_SOC_SOF_VIRTIO_FE) += virtio-fe.o obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9227d6624ce448..edd5ea3e505fa8 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -81,7 +81,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, return NULL; } -EXPORT_SYMBOL(snd_sof_find_spcm_comp); struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, unsigned int pcm_id) @@ -225,25 +224,6 @@ int snd_sof_create_page_table(struct snd_sof_dev *sdev, return pages; } -static void sof_virtio_vfe_init(struct snd_sof_dev *sdev, - struct snd_sof_pdata *plat_data) -{ - sdev->is_vfe = plat_data->is_vfe; - - /* - * Currently we only support one VM. comp_id from 0 to - * SOF_VIRTIO_MAX_GOS_COMPS - 1 is for SOS. Other comp_id numbers - * are for VM1. - * TBD: comp_id number range should be dynamically assigned when - * multiple VMs are supported. - */ - if (sdev->is_vfe) { - sdev->next_comp_id = SOF_VIRTIO_MAX_GOS_COMPS; - sdev->vfe = plat_data->vfe; - sdev->vfe->sdev = sdev; - } -} - /* * SOF Driver enumeration. */ @@ -276,7 +256,6 @@ static int sof_probe(struct platform_device *pdev) INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->route_list); - INIT_LIST_HEAD(&sdev->vbe_list); dev_set_drvdata(&pdev->dev, sdev); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); @@ -309,9 +288,6 @@ static int sof_probe(struct platform_device *pdev) goto dbg_err; } - /* optionally register virtio miscdev */ - sof_virtio_miscdev_register(sdev); - /* init the IPC */ sdev->ipc = snd_sof_ipc_init(sdev); if (!sdev->ipc) { @@ -319,12 +295,6 @@ static int sof_probe(struct platform_device *pdev) goto ipc_err; } - sof_virtio_vfe_init(sdev, plat_data); - - /* vFE will not touch HW. Let's skip fw loading */ - if (sdev->is_vfe) - goto skip_load_fw_and_trace; - /* load the firmware */ ret = snd_sof_load_firmware(sdev, true); if (ret < 0) { @@ -349,8 +319,6 @@ static int sof_probe(struct platform_device *pdev) "warning: failed to initialize trace %d\n", ret); } -skip_load_fw_and_trace: - /* now register audio DSP platform driver and dai */ ret = snd_soc_register_component(&pdev->dev, &sdev->plat_drv, sdev->ops->drv, @@ -415,7 +383,6 @@ static int sof_remove(struct platform_device *pdev) snd_soc_unregister_component(&pdev->dev); snd_sof_fw_unload(sdev); snd_sof_ipc_free(sdev); - sof_virtio_miscdev_unregister(); snd_sof_free_debug(sdev); snd_sof_free_trace(sdev); snd_sof_remove(sdev); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 80862ad1919a80..4ee63ce4dd2b11 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -535,9 +535,6 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); - /* optionally update position for vBE */ - sof_vbe_update_guest_posn(sdev, &posn); - /* only inform ALSA for period_wakeup mode */ if (!spcm->stream[direction].substream->runtime->no_period_wakeup) snd_pcm_period_elapsed(spcm->stream[direction].substream); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 930ba3f03942c6..e1b53594434a0c 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -691,7 +691,6 @@ static int sof_pcm_probe(struct snd_soc_component *component) /* load the default topology */ sdev->component = component; - sdev->card = component->card; switch (plat_data->type) { case SOF_DEVICE_SPI: diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f0358d50ebbfe9..0f7ddf19bf8a15 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -55,11 +54,6 @@ #define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) -/* The maximum number of components a virtio guest vFE driver can use */ -#define SOF_VIRTIO_MAX_GOS_COMPS 1000 - -#define SOF_VIRTIO_COMP_ID_UNASSIGNED 0xffffffff - struct snd_sof_dev; struct snd_sof_ipc_msg; struct snd_sof_ipc; @@ -68,8 +62,6 @@ struct snd_soc_tplg_ops; struct snd_soc_component; struct sof_intel_hda_dev; struct snd_sof_pdata; -struct virtio_device; -struct virtqueue; /* * SOF DSP HW abstraction operations. @@ -298,47 +290,6 @@ struct snd_sof_dai { struct list_head list; /* list in sdev dai list */ }; -/* - * in virtio iovec array: - * iovec[0]: the ipc message data between vFE and vBE - * iovec[1]: the ipc reply data between vFE and vBE - */ -#define SOF_VIRTIO_IPC_MSG 0 -#define SOF_VIRTIO_IPC_REPLY 1 - -/* Virtio Frontend */ -struct sof_vfe { - struct sof_virtio_priv *priv; - struct snd_sof_dev *sdev; - - /* IPC cmd from frontend to backend */ - struct virtqueue *ipc_cmd_tx_vq; - - /* IPC cmd reply from backend to frontend */ - struct virtqueue *ipc_cmd_rx_vq; - - /* IPC notification from backend to frontend */ - struct virtqueue *ipc_not_rx_vq; - - /* IPC notification reply from frontend to backend */ - struct virtqueue *ipc_not_tx_vq; - - /* position update work */ - struct work_struct posn_update_work; - - /* current pending cmd message */ - struct snd_sof_ipc_msg *msg; - - /* current and pending notification */ - struct snd_sof_ipc_msg *not; - struct sof_ipc_stream_posn *posn; -}; - -struct vbs_sof_posn { - struct list_head list; - struct sof_ipc_stream_posn pos; -}; - /* * SOF Device Level. */ @@ -351,7 +302,6 @@ struct snd_sof_dev { /* ASoC components */ struct snd_soc_component_driver plat_drv; - struct snd_soc_card *card; /* DSP firmware boot */ wait_queue_head_t boot_wait; @@ -408,11 +358,6 @@ struct snd_sof_dev { wait_queue_head_t waitq; int code_loading; - /* virtio for BE and FE */ - struct list_head vbe_list; - struct sof_vfe *vfe; - int is_vfe; - /* DMA for Trace */ struct snd_dma_buffer dmatb; struct snd_dma_buffer dmatp; @@ -432,29 +377,6 @@ struct snd_sof_dev { #define sof_to_bus(s) (&(s)->hda->hbus.core) #define sof_to_hbus(s) (&(s)->hda->hbus) -#if IS_ENABLED(CONFIG_SND_SOC_SOF_VIRTIO_BE) -int sof_virtio_miscdev_register(struct snd_sof_dev *sdev); -int sof_virtio_miscdev_unregister(void); -int sof_vbe_update_guest_posn(struct snd_sof_dev *sdev, - struct sof_ipc_stream_posn *posn); -#else -static inline int sof_virtio_miscdev_register(struct snd_sof_dev *sdev) -{ - return 0; -} - -static inline int sof_virtio_miscdev_unregister(void) -{ - return 0; -} - -static inline int sof_vbe_update_guest_posn(struct snd_sof_dev *sdev, - struct sof_ipc_stream_posn *posn) -{ - return 0; -} -#endif - /* * SOF platform private struct used as drvdata of * platform dev (e.g. pci/acpi/spi...) drvdata. @@ -573,26 +495,10 @@ int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, void *stack, size_t stack_size); int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); -/* - * VirtIO - */ -int sof_virtio_submit_guest_ipc(struct snd_sof_dev *sdev, int vm_id, - void *ipc_buf, void *reply_buf, - size_t count, size_t reply_sz); -int snd_sof_virtio_fs_init(struct snd_sof_dev *sdev); -int snd_sof_virtio_fs_release(void); -int sof_virtio_update_guest_posn(void *ctx, struct sof_ipc_stream_posn *posn); -int sof_virtio_try_update_guest_posn(struct snd_sof_dev *sdev, - struct sof_ipc_stream_posn *posn); -void sof_virtio_set_spcm_posn_offset(struct snd_sof_pcm *spcm, int direction); -int sof_virtio_register_guest(void *ctx); -int sof_virtio_release_guest(int id); - /* * Platform specific ops. */ extern struct snd_compr_ops sof_compressed_ops; -extern struct snd_sof_dsp_ops snd_sof_virtio_fe_ops; /* * Kcontrols. diff --git a/sound/soc/sof/virtio-be.c b/sound/soc/sof/virtio-be.c index 84b7ed8f01b3e4..4c07caeba41b0c 100644 --- a/sound/soc/sof/virtio-be.c +++ b/sound/soc/sof/virtio-be.c @@ -5,1122 +5,122 @@ * * Copyright(c) 2017 Intel Corporation. All rights reserved. * - * Author: Libin Yang - * Luo Xionghu + * Author: Luo Xionghu * Liam Girdwood + * */ -#include -#include +#include #include -#include -#include -#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "sof-priv.h" #include "ops.h" -#include "virtio-be.h" -#include "virtio-miscdev.h" - -#define IGS(x) (((x) >> SOF_GLB_TYPE_SHIFT) & 0xf) -#define ICS(x) (((x) >> SOF_CMD_TYPE_SHIFT) & 0xfff) - -/* find client from client ID */ -static struct sof_vbe_client *vbe_client_find(struct snd_sof_dev *sdev, - int client_id) -{ - struct sof_vbe_client *client; - struct sof_vbe *vbe; - - list_for_each_entry(vbe, &sdev->vbe_list, list) { - list_for_each_entry(client, &vbe->client_list, list) { - if (client_id == client->vhm_client_id) - return client; - } - } - - return NULL; -} - -static struct sof_vbe *sbe_comp_id_to_vbe(struct snd_sof_dev *sdev, int comp_id) -{ - struct sof_vbe *vbe; - - list_for_each_entry(vbe, &sdev->vbe_list, list) { - if (comp_id < vbe->comp_id_end && comp_id >= vbe->comp_id_begin) - return vbe; - } - return NULL; -} - -static int sof_virtio_send_ipc(struct snd_sof_dev *sdev, void *ipc_data, - void *reply_data, size_t count, - size_t reply_size) -{ - struct snd_sof_ipc *ipc = sdev->ipc; - struct sof_ipc_hdr *hdr = (struct sof_ipc_hdr *)ipc_data; - - return sof_ipc_tx_message(ipc, hdr->cmd, ipc_data, count, - reply_data, reply_size); -} - -/* - * This function will be called when there is a poistion update requirement - * from vBE. Return true if posn buffer is filled successfully - */ -static bool sbe_fill_posn_vqbuf(struct sof_vbe *vbe, - struct virtio_vq_info *vq, - struct sof_ipc_stream_posn *posn, - bool *endchain) -{ - struct device *dev = vbe->sdev->dev; - struct iovec iov; - u16 idx; - int ret; - - *endchain = false; - - /* - * There is a position update requirement, let's get - * an empty buffer and fill it. - */ - while (virtio_vq_has_descs(vq)) { - ret = virtio_vq_getchain(vq, &idx, &iov, 1, NULL); - if (ret <= 0) - return false; - - /* The buffer is bad. Don't use it. Just release it. */ - if (iov.iov_len < sizeof(struct sof_ipc_stream_posn)) { - /* we need call endchain() later */ - *endchain = true; - dev_err(dev, "iov len %lu, expecting len %lu\n", - iov.iov_len, sizeof(*posn)); - virtio_vq_relchain(vq, idx, iov.iov_len); - continue; - } - - /* Get a valid buffer. Let's fill the data and kick back */ - memcpy(iov.iov_base, posn, sizeof(struct sof_ipc_stream_posn)); - *endchain = true; - virtio_vq_relchain(vq, idx, iov.iov_len); - return true; - } - - return false; -} - -/* - * IPC notification reply from vFE to vBE - * This function is called when vFE has queued an empty buffer to vBE - */ -static void sbe_ipc_fe_not_reply_get(struct sof_vbe *vbe, int vq_idx) -{ - struct virtio_vq_info *vq; - struct vbs_sof_posn *entry; - unsigned long flags; - bool endchain; - - dev_dbg(vbe->sdev->dev, - "audio BE notification vq kick handling, vq_idx %d\n", vq_idx); - - spin_lock_irqsave(&vbe->posn_lock, flags); - - /* - * Now we have gotten an empty buffer from vFE. This buffer is used - * to send the position update information to vFE. - * Let's check if there is position update requirement from vBE. - * If yes, let's kick back the position information to vFE. - * If there is no position update requirement from vBE, let's untouch - * the buffer and keep the buffer in the vq for later use. - */ - if (list_empty(&vbe->posn_list)) { - /* - * No position update requirement. Don't touch the buffer and - * Keep the buffer in the vq - */ - spin_unlock_irqrestore(&vbe->posn_lock, flags); - return; - } - - vq = &vbe->vqs[vq_idx]; - entry = list_first_entry(&vbe->posn_list, - struct vbs_sof_posn, list); - list_del(&entry->list); - spin_unlock_irqrestore(&vbe->posn_lock, flags); - - /* - * There are position update requirements and now we get the new - * position buffer entry. Let's try to update the position to vFE. - */ - sbe_fill_posn_vqbuf(vbe, vq, &entry->pos, &endchain); - - /* - * encchain means: we have already processed this queue - * and now let's kick back and release it. - */ - if (endchain) - virtio_vq_endchains(vq, 1); -} - -/* validate component IPC */ -static int sbe_ipc_comp(struct snd_sof_dev *sdev, int vm_id, - struct sof_ipc_hdr *hdr) -{ - /*TODO validate host comp id range based on vm_id */ - - /* Nothing to be done */ - return 0; -} - -/* - * This function is to get the BE dai link for the GOS - * It uses the dai_link name to find the BE dai link. - * The Current dai_link name "vm_dai_link" is for the GOS, - * which means only one Virtual Machine is supported. - * And the VM only support one playback pcm and one capture pcm. - * After we switch to the new topology, we can support multiple - * VMs and multiple PCM streams for each VM. - * This function may be abandoned after switching to the new topology. - */ -static struct snd_pcm_substream * -sbe_get_substream(struct snd_sof_dev *sdev, - struct snd_soc_pcm_runtime **rtd, int direction) -{ - struct snd_pcm *pcm; - struct snd_pcm_str *stream; - struct snd_pcm_substream *substream = NULL; - struct snd_soc_dai_link *dai_link; - struct snd_soc_card *card = sdev->card; - struct snd_soc_pcm_runtime *r; - - list_for_each_entry(r, &card->rtd_list, list) { - /* - * We need to find a dedicated substream: - * pcm->streams[dir].substream which is dedicated - * used for vFE. - */ - pcm = r->pcm; - if (!pcm) - continue; - - stream = &pcm->streams[direction]; - substream = stream->substream; - if (substream) { - dai_link = r->dai_link; - if (strcmp(dai_link->name, "vm_dai_link") == 0) { - /* - * In the current solution, "vm_dai_link" is - * for the vFE. - */ - if (rtd) - *rtd = r; - return substream; - } - } - } - - return NULL; -} -static int sbe_pcm_open(struct snd_sof_dev *sdev, - void *ipc_data, int vm_id) -{ - /* - * TO re-use the sof callback for pcm, we should find a proper - * substream and do the correct setting for the substream. - * As there is no FE link substream in SOS for GOS (GOS FE link - * substreams are created in GOS and SOS will never see it), let's - * use BE link substream in SOS for the callbacks. - * This is save because the BE link substream is created dedicated for - * GOS in machine driver. - */ - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; - struct snd_sof_pcm *spcm; - struct snd_soc_pcm_runtime *rtd = NULL; - struct sof_ipc_pcm_params *pcm; - u32 comp_id; - size_t size; - int direction; - - pcm = (struct sof_ipc_pcm_params *)ipc_data; - comp_id = pcm->comp_id; - - spcm = snd_sof_find_spcm_comp(sdev, comp_id, &direction); - if (!spcm) - return -ENODEV; - - mutex_lock(&spcm->mutex); - - substream = sbe_get_substream(sdev, &rtd, direction); - if (!substream || !rtd) - return -ENODEV; - if (substream->ref_count > 0) - return -EBUSY; - substream->ref_count++; /* set it used */ - - runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); - if (!runtime) - return -ENOMEM; - - size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); - runtime->status = snd_malloc_pages(size, GFP_KERNEL); - if (!runtime->status) { - kfree(runtime); - return -ENOMEM; - } - memset((void *)runtime->status, 0, size); - - size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); - runtime->control = snd_malloc_pages(size, GFP_KERNEL); - if (!runtime->control) { - dev_err(sdev->dev, "fail to alloc pages for runtime->control"); - snd_free_pages((void *)runtime->status, - PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); - kfree(runtime); - return -ENOMEM; - } - memset((void *)runtime->control, 0, size); - - init_waitqueue_head(&runtime->sleep); - init_waitqueue_head(&runtime->tsleep); - runtime->status->state = SNDRV_PCM_STATE_OPEN; - - substream->runtime = runtime; - substream->private_data = rtd; - rtd->sof = spcm; - substream->stream = direction; - - /* check with spcm exists or not */ - spcm->stream[direction].posn.host_posn = 0; - spcm->stream[direction].posn.dai_posn = 0; - spcm->stream[direction].substream = substream; - - /* TODO: codec open */ - - snd_sof_pcm_platform_open(sdev, substream); - - mutex_unlock(&spcm->mutex); - return 0; -} - -static int sbe_pcm_close(struct snd_sof_dev *sdev, - void *ipc_data, int vm_id) -{ - struct snd_pcm_substream *substream; - struct snd_sof_pcm *spcm; - struct snd_soc_pcm_runtime *rtd = NULL; - struct sof_ipc_stream *stream; - u32 comp_id; - int direction; - - stream = (struct sof_ipc_stream *)ipc_data; - comp_id = stream->comp_id; - - spcm = snd_sof_find_spcm_comp(sdev, comp_id, &direction); - if (!spcm) - return 0; - mutex_lock(&spcm->mutex); - substream = sbe_get_substream(sdev, &rtd, direction); - if (!substream) { - mutex_unlock(&spcm->mutex); - return 0; - } - - snd_sof_pcm_platform_close(sdev, substream); - - /* TODO: codec close */ - - substream->ref_count = 0; - if (substream->runtime) { - snd_free_pages((void *)substream->runtime->status, - PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); - snd_free_pages((void *)substream->runtime->control, - PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); - kfree(substream->runtime); - substream->runtime = NULL; - rtd->sof = NULL; - } - mutex_unlock(&spcm->mutex); - return 0; -} - -/* - * FIXME - this function should only convert a compressed GOS PHY page table - * into a page table of SOS physical pages. It should leave the HDA stream - * alone for HDA code to manage. +/* BE driver + * + * This driver will create IO Queues for communition from FE drivers. + * The FE driver will send real IPC structures over the queue and then + * the BE driver will send the structures directlt to the DSP. The BE will + * get the IPC reply from the DSP and send it back to the FE over the queue. + * + * The virt IO message Q handlers in this file will :- + * + * 1) Check that the message is valid and not for any componenets that don't + * belong to the guest. + * + * 2) Call snd_sof_dsp_tx_msg(struct snd_sof_dev *sdev, + * struct snd_sof_ipc_msg *msg) to send the message to the DSP. + * + * Replies will be sent back using a similar method. */ -static int sbe_stream_prepare(struct snd_sof_dev *sdev, - struct sof_ipc_pcm_params *pcm, int vm_id, - struct snd_sg_page *table) -{ - u32 pcm_buffer_gpa = pcm->params.buffer.phy_addr; - u64 pcm_buffer_hpa = vhm_vm_gpa2hpa(vm_id, (u64)pcm_buffer_gpa); - u8 *page_table = (uint8_t *)__va(pcm_buffer_hpa); - int idx, i; - u32 gpa_parse, pages; - u64 hpa_parse; - - pages = pcm->params.buffer.pages; - for (i = 0; i < pages; i++) { - idx = (((i << 2) + i)) >> 1; - gpa_parse = page_table[idx] | (page_table[idx + 1] << 8) - | (page_table[idx + 2] << 16); - - if (i & 0x1) - gpa_parse <<= 8; - else - gpa_parse <<= 12; - gpa_parse &= 0xfffff000; - hpa_parse = vhm_vm_gpa2hpa(vm_id, (u64)gpa_parse); - - table[i].addr = hpa_parse; - } - - return 0; -} -static int sbe_assemble_params(struct sof_ipc_pcm_params *pcm, - struct snd_pcm_hw_params *params) +static int sof_virtio_validate(struct virtio_device *dev) { - struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = - pcm->params.channels; - - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min = - pcm->params.rate; - - hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)->min = - pcm->params.host_period_bytes; - - hw_param_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min = - pcm->params.buffer.size; - - snd_mask_none(fmt); - switch (pcm->params.frame_fmt) { - case SOF_IPC_FRAME_S16_LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16); - break; - case SOF_IPC_FRAME_S24_4LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24); - break; - case SOF_IPC_FRAME_S32_LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32); - break; - case SOF_IPC_FRAME_FLOAT: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_FLOAT); - break; - default: - return -EINVAL; - } + /* do we need this func ?? */ return 0; } -static int sbe_stream_hw_params(struct snd_sof_dev *sdev, - struct sof_ipc_pcm_params *pcm, int vm_id) -{ - struct snd_sg_page *table; - struct snd_sg_buf sgbuf; /* FIXME alloc at topology load */ - struct snd_dma_buffer dmab; /* FIXME alloc at topology load */ - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; - struct snd_pcm_hw_params params; - int direction = pcm->params.direction; - u32 pages; - int ret; - - /* find the proper substream */ - substream = sbe_get_substream(sdev, NULL, direction); - if (!substream) - return -ENODEV; - - runtime = substream->runtime; - if (!runtime) { - dev_err(sdev->dev, "no runtime is available for hw_params\n"); - return -ENODEV; - } - - /* setup hw */ - pages = pcm->params.buffer.pages; - table = kcalloc(pages, sizeof(*table), GFP_KERNEL); - sgbuf.table = table; - dmab.private_data = &sgbuf; - runtime->dma_buffer_p = &dmab; /* use the audiobuf from FE */ - - /* TODO: codec hw_params */ - - /* convert buffer GPA to HPA */ - ret = sbe_stream_prepare(sdev, pcm, vm_id, table); - - /* Use different stream_tag from FE. This is the real tag */ - sbe_assemble_params(pcm, ¶ms); - pcm->params.stream_tag = - snd_sof_pcm_platform_hw_params(sdev, substream, ¶ms); - dev_dbg(sdev->dev, "stream_tag %d", - pcm->params.stream_tag); - - kfree(table); - return ret; -} - -/* handle the stream ipc */ -static int sbe_ipc_stream_codec(struct snd_sof_dev *sdev, int vm_id, - struct sof_ipc_hdr *hdr) +static int sof_virtio_probe(struct virtio_device *dev) { - struct sof_ipc_pcm_params *pcm; - struct sof_ipc_stream *stream; - struct snd_soc_pcm_runtime *rtd; - struct snd_pcm_substream *substream; - struct snd_soc_dai *codec_dai; - const struct snd_soc_dai_ops *ops; - int ret, direction, comp_id; - int i; - u32 cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; + /* register fe device with sof core */ + //snd_sof_virtio_register_fe(dev); - /* TODO validate host comp id range based on vm_id */ + /* create our virtqueues */s - switch (cmd) { - case ICS(SOF_IPC_STREAM_TRIG_START): - stream = (struct sof_ipc_stream *)hdr; - comp_id = stream->comp_id; - snd_sof_find_spcm_comp(sdev, comp_id, &direction); - substream = sbe_get_substream(sdev, &rtd, direction); - - for (i = 0; i < rtd->num_codecs; i++) { - /* - * Now we are ready to trigger start. - * Let's unmute the codec firstly - */ - codec_dai = rtd->codec_dais[i]; - snd_soc_dai_digital_mute(codec_dai, 0, direction); - ops = codec_dai->driver->ops; - if (ops->trigger) { - ret = ops->trigger(substream, - SNDRV_PCM_TRIGGER_START, - codec_dai); - if (ret < 0) - return ret; - } - } - break; - default: - dev_dbg(sdev->dev, "0x%x!\n", cmd); - break; - } + /* send topology data to fe via virtq */ return 0; } -/* handle the stream ipc */ -static int sbe_ipc_stream(struct snd_sof_dev *sdev, int vm_id, - struct sof_ipc_hdr *hdr) +static void sof_virtio_remove(struct virtio_device *dev) { - struct sof_ipc_pcm_params *pcm; - struct sof_ipc_stream *stream; - struct snd_soc_pcm_runtime *rtd; - struct snd_pcm_substream *substream; - struct snd_soc_dai *codec_dai; - const struct snd_soc_dai_ops *ops; - int ret, direction, comp_id, i; - u32 cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; - - /* TODO validate host comp id range based on vm_id */ - - switch (cmd) { - case ICS(SOF_IPC_STREAM_PCM_PARAMS): - sbe_pcm_open(sdev, hdr, vm_id); - pcm = (struct sof_ipc_pcm_params *)hdr; - ret = sbe_stream_hw_params(sdev, pcm, vm_id); - break; - case ICS(SOF_IPC_STREAM_TRIG_START): - stream = (struct sof_ipc_stream *)hdr; - comp_id = stream->comp_id; - snd_sof_find_spcm_comp(sdev, comp_id, &direction); - substream = sbe_get_substream(sdev, &rtd, direction); - snd_sof_pcm_platform_trigger(sdev, substream, - SNDRV_PCM_TRIGGER_START); - break; - case ICS(SOF_IPC_STREAM_TRIG_STOP): - stream = (struct sof_ipc_stream *)hdr; - comp_id = stream->comp_id; - snd_sof_find_spcm_comp(sdev, comp_id, &direction); - substream = sbe_get_substream(sdev, &rtd, direction); - for (i = 0; i < rtd->num_codecs; i++) { - codec_dai = rtd->codec_dais[i]; - ops = codec_dai->driver->ops; - if (ops->trigger) { - ret = ops->trigger(substream, - SNDRV_PCM_TRIGGER_STOP, - codec_dai); - if (ret < 0) { - dev_err(sdev->dev, - "trigger stop fails\n"); - return ret; - } - } - } - snd_sof_pcm_platform_trigger(sdev, substream, - SNDRV_PCM_TRIGGER_STOP); - break; - case ICS(SOF_IPC_STREAM_PCM_FREE): - sbe_pcm_close(sdev, hdr, vm_id); - break; - case ICS(SOF_IPC_STREAM_POSITION): - /* - * TODO: this is special case, we do not send this IPC to DSP - * but read back position directly from memory (like SOS) and - * then reply to FE. - * Use stream ID to get correct stream data - */ - break; - default: - dev_dbg(sdev->dev, "0x%x!\n", cmd); - break; - } + /* remove topology from fe via virtqueue */ - return 0; + /* destroy virtqueue */ } -static int sbe_ipc_tplg_comp_new(struct snd_sof_dev *sdev, int vm_id, - struct sof_ipc_hdr *hdr) +#ifdef CONFIG_PM +static int sof_virtio_freeze(struct virtio_device *dev) { - struct snd_sof_pcm *spcm; - struct sof_ipc_comp *comp = (struct sof_ipc_comp *)hdr; - struct sof_ipc_comp_host *host; - - switch (comp->type) { - case SOF_COMP_HOST: - /* - * TODO: below is a temporary solution. next step is - * to create a whole pcm staff incluing substream - * based on Liam's suggestion. - */ - - /* - * let's create spcm in HOST ipc - * spcm should be created in pcm load, but there is no such ipc - * so let create it here - */ - host = (struct sof_ipc_comp_host *)hdr; - spcm = kzalloc(sizeof(*spcm), GFP_KERNEL); - if (!spcm) - return -ENOMEM; - - spcm->sdev = sdev; - spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = - SOF_VIRTIO_COMP_ID_UNASSIGNED; - spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = - SOF_VIRTIO_COMP_ID_UNASSIGNED; - mutex_init(&spcm->mutex); - spcm->stream[host->direction].comp_id = host->comp.id; - list_add(&spcm->list, &sdev->pcm_list); - break; - default: - break; - } + /* pause and suspend any streams for this FE */ return 0; } -/* validate topology IPC */ -static int sbe_ipc_tplg(struct snd_sof_dev *sdev, int vm_id, - struct sof_ipc_hdr *hdr) -{ - /* TODO validate host comp id range based on vm_id */ - - u32 cmd; - int ret = 0; - - cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; - - switch (cmd) { - case ICS(SOF_IPC_TPLG_COMP_NEW): - ret = sbe_ipc_tplg_comp_new(sdev, vm_id, hdr); - break; - default: - break; - } - - return ret; -} - -static int sbe_ipc_stream_param_post(struct snd_sof_dev *sdev, - void *ipc_buf, void *reply_buf) +static int sof_virtio_restore(struct virtio_device *dev) { - struct sof_ipc_pcm_params_reply *ipc_params_reply; - struct snd_sof_pcm *spcm; - int direction; - u32 comp_id; - int posn_offset; - - ipc_params_reply = (struct sof_ipc_pcm_params_reply *)reply_buf; - comp_id = ipc_params_reply->comp_id; - posn_offset = ipc_params_reply->posn_offset; - - spcm = snd_sof_find_spcm_comp(sdev, comp_id, &direction); - if (!spcm) - return -ENODEV; - - spcm->posn_offset[direction] = - sdev->stream_box.offset + posn_offset; + /* restore and unpause any streams for this FE */ return 0; } +#endif -/* - * For some IPCs, the reply needs to be handled. - * This function is used to handle the replies of these IPCs. - */ -static int sbe_ipc_post(struct snd_sof_dev *sdev, - void *ipc_buf, void *reply_buf) -{ - struct sof_ipc_hdr *hdr; - u32 type, cmd; - int ret = 0; - - hdr = (struct sof_ipc_hdr *)ipc_buf; - type = (hdr->cmd & SOF_GLB_TYPE_MASK) >> SOF_GLB_TYPE_SHIFT; - cmd = (hdr->cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; - - switch (type) { - case IGS(SOF_IPC_GLB_STREAM_MSG): - switch (cmd) { - case ICS(SOF_IPC_STREAM_PCM_PARAMS): - ret = sbe_ipc_stream_param_post(sdev, - ipc_buf, reply_buf); - break; - default: - break; - } - break; - default: - break; - } - - return ret; -} - -/* - * TODO: The guest base ID is passed to guest at boot. - * TODO rename function name, not submit but consume - * TODO add topology ipc support and manage the multiple pcm and vms - */ -static int sbe_ipc_fwd(struct snd_sof_dev *sdev, int vm_id, - void *ipc_buf, void *reply_buf, - size_t count, size_t reply_sz) -{ - struct sof_ipc_hdr *hdr; - u32 type; - int ret = 0; - - /* validate IPC */ - if (!count) { - dev_err(sdev->dev, "error: guest IPC size is 0\n"); - return -EINVAL; - } - - hdr = (struct sof_ipc_hdr *)ipc_buf; - type = (hdr->cmd & SOF_GLB_TYPE_MASK) >> SOF_GLB_TYPE_SHIFT; - - /* validate the ipc */ - switch (type) { - case IGS(SOF_IPC_GLB_COMP_MSG): - ret = sbe_ipc_comp(sdev, vm_id, hdr); - if (ret < 0) - return ret; - break; - case IGS(SOF_IPC_GLB_STREAM_MSG): - ret = sbe_ipc_stream(sdev, vm_id, hdr); - if (ret < 0) - return ret; - break; - case IGS(SOF_IPC_GLB_DAI_MSG): - /* - * After we use the new topology solution for FE, - * we will not touch DAI anymore. - */ - break; - case IGS(SOF_IPC_GLB_TPLG_MSG): - ret = sbe_ipc_tplg(sdev, vm_id, hdr); - if (ret < 0) - return ret; - break; - case IGS(SOF_IPC_GLB_TRACE_MSG): - /* Trace should be initialized in SOS, skip FE requirement */ - return 0; - default: - dev_info(sdev->dev, "unhandled IPC 0x%x!\n", type); - break; - } - - /* now send the IPC */ - ret = sof_virtio_send_ipc(sdev, ipc_buf, reply_buf, count, reply_sz); - if (ret < 0) { - dev_err(sdev->dev, "err: failed to send virtio IPC %d\n", ret); - return ret; - } - - /* For some IPCs, the reply needs to be handled */ - ret = sbe_ipc_post(sdev, ipc_buf, reply_buf); - - switch (type) { - case IGS(SOF_IPC_GLB_STREAM_MSG): - /* setup the codec */ - ret = sbe_ipc_stream_codec(sdev, vm_id, hdr); - if (ret < 0) - return ret; - break; - default: - break; - } - - return ret; -} - -/* IPC commands coming from FE to BE */ -static void sbe_ipc_fe_cmd_get(struct sof_vbe *vbe, int vq_idx) -{ - struct virtio_vq_info *vq = &vbe->vqs[vq_idx]; - struct device *dev = vbe->sdev->dev; - struct iovec iov[2]; - u16 idx; - void *ipc_buf; - void *reply_buf; - size_t len1, len2; - int vm_id; - int ret, i; - - vm_id = vbe->vm_id; - memset(iov, 0, sizeof(iov)); - - /* while there are mesages in virtio queue */ - while (virtio_vq_has_descs(vq)) { - /* FE uses items, first is command second is reply data */ - ret = virtio_vq_getchain(vq, &idx, iov, 2, NULL); - if (ret < 2) { - /* something wrong in vq, no item is fetched */ - if (ret < 0) { - /* - * This should never happen. - * FE should be aware this situation already - */ - virtio_vq_endchains(vq, 1); - return; - } - - dev_err(dev, "ipc buf and reply buf not paired\n"); - - /* no enough items, let drop this kick */ - for (i = 0; i <= ret; i++) { - virtio_vq_relchain(vq, idx + i, - iov[i].iov_len); - } - virtio_vq_endchains(vq, 1); - return; - } - - /* - * let's check the ipc message and reply buffer's - * length is valid or not - */ - len1 = iov[SOF_VIRTIO_IPC_MSG].iov_len; - len2 = iov[SOF_VIRTIO_IPC_REPLY].iov_len; - if (!len1 || !len2) { - if (len1) - virtio_vq_relchain(vq, idx, len1); - if (len2) - virtio_vq_relchain(vq, idx + 1, len2); - } else { - /* OK, the buffer is valid. let's handle the ipc */ - ipc_buf = iov[SOF_VIRTIO_IPC_MSG].iov_base; - reply_buf = iov[SOF_VIRTIO_IPC_REPLY].iov_base; - - /* send IPC to HW */ - ret = sbe_ipc_fwd(vbe->sdev, vm_id, ipc_buf, reply_buf, - len1, len2); - if (ret < 0) - dev_err(dev, "submit guest ipc command fail\n"); - - virtio_vq_relchain(vq, idx, len1); - virtio_vq_relchain(vq, idx + 1, len2); - } - } - - /* BE has finished the operations, now let's kick back */ - virtio_vq_endchains(vq, 1); -} - -static void handle_vq_kick(struct sof_vbe *vbe, int vq_idx) -{ - dev_dbg(vbe->sdev->dev, "vq_idx %d\n", vq_idx); - - switch (vq_idx) { - case SOF_VIRTIO_IPC_CMD_TX_VQ: - /* IPC command from FE to DSP */ - return sbe_ipc_fe_cmd_get(vbe, vq_idx); - case SOF_VIRTIO_IPC_CMD_RX_VQ: - /* IPC command reply from DSP to FE - NOT kick */ - break; - case SOF_VIRTIO_IPC_NOT_TX_VQ: - /* IPC notification reply from FE to DSP */ - return sbe_ipc_fe_not_reply_get(vbe, vq_idx); - case SOF_VIRTIO_IPC_NOT_RX_VQ: - /* IPC notification from DSP to FE - NOT kick */ - break; - default: - dev_err(vbe->sdev->dev, "idx %d is invalid\n", vq_idx); - break; - } -} - -/* - * handle_kick() is used to handle the event that vFE kick a queue - * entry to vBE. First, check this event is valid or not. If it is - * valid and needs to be handled, let's call hanle_vq_kick() - */ -static int handle_kick(int client_id, unsigned long *ioreqs_map) -{ - struct vhm_request *req; - struct sof_vbe_client *client; - struct sof_vbe *vbe; - struct snd_sof_dev *sdev = sof_virtio_get_sof(); - int i, handle; - - if (!sdev) { - pr_err("error: no BE registered for SOF!\n"); - return -EINVAL; - } - - dev_dbg(sdev->dev, "virtio audio kick handling!\n"); - - /* get the client this notification is for/from? */ - client = vbe_client_find(sdev, client_id); - if (!client) { - dev_err(sdev->dev, "Ooops! client %d not found!\n", client_id); - return -EINVAL; - } - vbe = client->vbe; +/* IDs of FEs */ +static const struct virtio_device_id *fe_id_table[] + { +}; - /* go through all vcpu for the valid request buffer */ - for (i = 0; i < client->max_vcpu; i++) { - req = &client->req_buf[i]; - handle = 0; +static struct virtio_driver sof_be_virtio_driver = { + .driver = { + .name = "sof-virtio-be", + .owner = THIS_MODULE, + }, - /* is request valid and for this client */ - if (!req->valid) - continue; - if (req->client != client->vhm_client_id) - continue; + .id_table = fe_id_table, - /* ignore if not processing state */ - if (req->processed != REQ_STATE_PROCESSING) - continue; + //const unsigned int *feature_table; + //unsigned int feature_table_size; + //const unsigned int *feature_table_legacy; + //unsigned int feature_table_size_legacy; - dev_dbg(sdev->dev, - "ioreq type %d, direction %d, addr 0x%llx, size 0x%llx, value 0x%x\n", - req->type, - req->reqs.pio_request.direction, - req->reqs.pio_request.address, - req->reqs.pio_request.size, - req->reqs.pio_request.value); + validate = sof_virtio_validate, + probe = sof_virtio_probe, + remove = sof_virtio_remove, - if (req->reqs.pio_request.direction == REQUEST_READ) { - /* - * currently we handle kick only, - * so read will return 0 - */ - req->reqs.pio_request.value = 0; - } else { - req->reqs.pio_request.value >= 0 ? - (handle = 1) : (handle = 0); - } +#ifdef CONFIG_PM + freeze = sof_virtio_freeze, + restore = sof_virtio_restore, +#endif +}; - req->processed = REQ_STATE_SUCCESS; - - /* - * notify hypervisor this event request is finished. - * virtio driver is ready to handle this event. - */ - acrn_ioreq_complete_request(client->vhm_client_id, i); - - /* handle VQ kick if needed */ - if (handle) - handle_vq_kick(vbe, req->reqs.pio_request.value); - } - - return 0; -} - -/* - * register vhm client with virtio. - * vhm use the client to handle the io access from FE - */ -int sof_vbe_register_client(struct sof_vbe *vbe) +/* this will be called by sof core when core is ready */ +int sof_virtio_register(struct snd_sof_dev *sdev) { - struct virtio_dev_info *dev_info = &vbe->dev_info; - struct snd_sof_dev *sdev = vbe->sdev; - struct vm_info info; - struct sof_vbe_client *client; - unsigned int vmid; int ret; - /* - * vbs core has mechanism to manage the client - * there is no need to handle this in the special BE driver - * let's use the vbs core client management later - */ - client = devm_kzalloc(sdev->dev, sizeof(*client), GFP_KERNEL); - if (!client) - return -EINVAL; - - client->vbe = vbe; - - vmid = dev_info->_ctx.vmid; - client->vhm_client_id = acrn_ioreq_create_client(vmid, handle_kick, - "sof_vbe kick init\n"); - if (client->vhm_client_id < 0) { - dev_err(sdev->dev, "failed to create client of acrn ioreq!\n"); - return client->vhm_client_id; - } - - ret = acrn_ioreq_add_iorange(client->vhm_client_id, REQ_PORTIO, - dev_info->io_range_start, - dev_info->io_range_start + - dev_info->io_range_len - 1); - if (ret < 0) { - dev_err(sdev->dev, "failed to add iorange to acrn ioreq!\n"); - goto err; - } - - /* - * setup the vm information, such as max_vcpu and max_gfn - * BE need this information to handle the vqs - */ - ret = vhm_get_vm_info(vmid, &info); - if (ret < 0) { - dev_err(sdev->dev, "failed in vhm_get_vm_info!\n"); - goto err; - } - client->max_vcpu = info.max_vcpu; - - /* - * Setup the reqbuf for this client. The reqbuf is ready in - * acrn system now. - */ - client->req_buf = acrn_ioreq_get_reqbuf(client->vhm_client_id); - if (!client->req_buf) { - dev_err(sdev->dev, "failed in acrn_ioreq_get_reqbuf!\n"); - goto err; - } - - /* just attach once as vhm will kick kthread */ - acrn_ioreq_attach_client(client->vhm_client_id, 0); - - /* complete client init and add to list */ - list_add(&client->list, &vbe->client_list); - - return 0; -err: - acrn_ioreq_destroy_client(client->vhm_client_id); - return -EINVAL; -} - -/* register SOF audio BE with virtio/acrn */ -int sof_vbe_register(struct snd_sof_dev *sdev, struct sof_vbe **svbe) -{ - struct sof_vbe *vbe; - struct virtio_vq_info *vqs; - int i; - - vbe = devm_kzalloc(sdev->dev, sizeof(*vbe), GFP_KERNEL); - if (!vbe) - return -ENOMEM; - - INIT_LIST_HEAD(&vbe->client_list); - INIT_LIST_HEAD(&vbe->posn_list); - spin_lock_init(&vbe->posn_lock); - vbe->sdev = sdev; - - /* - * We currently only support one VM. The comp_id range will be - * dynamically assigned when multiple VMs are supported. - */ - vbe->comp_id_begin = SOF_VIRTIO_MAX_GOS_COMPS; - vbe->comp_id_end = vbe->comp_id_begin + SOF_VIRTIO_MAX_GOS_COMPS; - - vqs = vbe->vqs; - for (i = 0; i < SOF_VIRTIO_NUM_OF_VQS; i++) { - vqs[i].dev = &vbe->dev_info; - - /* - * currently relies on VHM to kick us, - * thus vq_notify not used - */ - vqs[i].vq_notify = NULL; - } - - /* link dev and vqs */ - vbe->dev_info.vqs = vqs; - - virtio_dev_init(&vbe->dev_info, vqs, SOF_VIRTIO_NUM_OF_VQS); - - *svbe = vbe; - - return 0; + ret = register_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ + return ret; } -int sof_vbe_update_guest_posn(struct snd_sof_dev *sdev, - struct sof_ipc_stream_posn *posn) +/* called by sof core when driver is removed */ +void sof_virtio_unregister(struct snd_sof_dev *sdev) { - struct sof_vbe *vbe = sbe_comp_id_to_vbe(sdev, posn->comp_id); - struct virtio_vq_info *vq = &vbe->vqs[SOF_VIRTIO_IPC_NOT_RX_VQ]; - struct vbs_sof_posn *entry; - unsigned long flags; - bool ret, endchain; - - /* posn update for SOS */ - if (!vbe) - return 0; - - /* - * let's try to get a notification RX vq available buffer - * If there is an available buffer, let's notify immediately - */ - ret = sbe_fill_posn_vqbuf(vbe, vq, posn, &endchain); - if (ret) { - if (endchain) - virtio_vq_endchains(vq, 1); - return 0; - } - - spin_lock_irqsave(&vbe->posn_lock, flags); - - /* - * Notification RX vq buffer is not available. Let's save the posn - * update msg. And send the msg when vq buffer is available. - */ - entry = kzalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) { - spin_unlock_irqrestore(&vbe->posn_lock, flags); - return -ENOMEM; - } - - memcpy(&entry->pos, posn, sizeof(struct vbs_sof_posn)); - list_add_tail(&entry->list, &vbe->posn_list); - spin_unlock_irqrestore(&vbe->posn_lock, flags); - - if (endchain) - virtio_vq_endchains(vq, 1); - - return 0; + unregister_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ } diff --git a/sound/soc/sof/virtio-be.h b/sound/soc/sof/virtio-be.h deleted file mode 100644 index 78beb3a69dcf50..00000000000000 --- a/sound/soc/sof/virtio-be.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Author: Libin Yang - */ - -#ifndef __SOUND_SOC_SOF_VIRTIO_BE_H -#define __SOUND_SOC_SOF_VIRTIO_BE_H - -#include - -struct sof_vbe; - -/* Virtio Backend */ -struct sof_vbe { - struct snd_sof_dev *sdev; - - struct virtio_dev_info dev_info; - struct virtio_vq_info vqs[SOF_VIRTIO_NUM_OF_VQS]; - - int vm_id; /* vm id number */ - - /* the comp_ids for this vm audio */ - int comp_id_begin; - int comp_id_end; - - spinlock_t posn_lock; /* lock for position update */ - - struct list_head client_list; - struct list_head posn_list; - - struct list_head list; -}; - -struct sof_vbe_client { - struct sof_vbe *vbe; - int vhm_client_id; - int max_vcpu; - struct vhm_request *req_buf; - struct list_head list; -}; - -struct snd_sof_dev *sof_virtio_get_sof(void); -int sof_vbe_register(struct snd_sof_dev *sdev, struct sof_vbe **svbe); -int sof_vbe_register_client(struct sof_vbe *vbe); - -#endif /* __SOUND_SOC_SOF_VIRTIO_BE_H */ diff --git a/sound/soc/sof/virtio-fe.c b/sound/soc/sof/virtio-fe.c index d04c2857fbb3d0..1019e65876b923 100644 --- a/sound/soc/sof/virtio-fe.c +++ b/sound/soc/sof/virtio-fe.c @@ -5,8 +5,7 @@ * * Copyright(c) 2017 Intel Corporation. All rights reserved. * - * Author: Libin Yang - * Luo Xionghu + * Author: Luo Xionghu * Liam Girdwood */ @@ -20,429 +19,112 @@ * The virtIO message Q will use the *exact* same IPC structures as we currently * use in the mailbox. * + * Guest OS SOF core -> SOF FE -> virtIO Q -> SOF BE -> + * System OS SOF core -> DSP + * * The mailbox IO and TX/RX msg functions below will do IO on the virt IO Q. */ +#include +#include +#include #include -#include #include #include -#include +#include +#include +#include #include -#include #include #include -#include -#include "virtio-fe.h" -#include "ops.h" #include "sof-priv.h" -#include "intel/hda.h" - -static const char *const sof_vq_names[SOF_VIRTIO_NUM_OF_VQS] = { - SOF_VIRTIO_IPC_CMD_TX_VQ_NAME, - SOF_VIRTIO_IPC_CMD_RX_VQ_NAME, - SOF_VIRTIO_IPC_NOT_TX_VQ_NAME, - SOF_VIRTIO_IPC_NOT_RX_VQ_NAME, -}; - -static const struct sof_dev_desc virt_desc = { - .nocodec_fw_filename = NULL, - .nocodec_tplg_filename = "intel/sof-apl-nocodec.tplg", - .resindex_lpe_base = -1, - .resindex_pcicfg_base = -1, - .resindex_imr_base = -1, - .irqindex_host_ipc = -1, - .resindex_dma_base = -1, -}; - +#include "ops.h" +#include "intel.h" /* * IPC Firmware ready. */ -static int vfe_is_ready(struct snd_sof_dev *sdev) -{ - /* is message still pending */ - if (sdev->vfe->msg) - return 0; - - /* ready for next message */ - return 1; -} - -static int vfe_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) -{ - return 0; -}; - -/* used to send IPC to BE */ -static int vfe_send_msg(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) -{ - struct sof_vfe *vfe; - struct scatterlist sgs[2]; - int ret = 0; - - vfe = sdev->vfe; - - sg_init_table(sgs, 2); - - sg_set_buf(&sgs[SOF_VIRTIO_IPC_MSG], - msg->msg_data, msg->msg_size); - sg_set_buf(&sgs[SOF_VIRTIO_IPC_REPLY], - msg->reply_data, msg->reply_size); - - vfe->msg = msg; - - ret = virtqueue_add_outbuf(vfe->ipc_cmd_tx_vq, sgs, 2, - msg->msg_data, GFP_KERNEL); - if (ret < 0) - dev_err(sdev->dev, "error: could not send IPC %d\n", ret); - - virtqueue_kick(vfe->ipc_cmd_tx_vq); - - return ret; -} - -/* get IPC reply from BE */ -static int vfe_get_reply(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) -{ - struct sof_vfe *vfe = sdev->vfe; - - vfe->msg = NULL; - return 0; -} - -/* get stream message from virtio */ -static int vfe_get_stream_message(struct snd_sof_dev *sdev) -{ - struct sof_vfe *vfe = sdev->vfe; - void *buf = NULL; - unsigned int buflen = 0; - - buf = virtqueue_get_buf(vfe->ipc_not_rx_vq, &buflen); - if (unlikely(!buf)) { - dev_err(sdev->dev, "error rx not from virtio:%d!\n", buflen); - return -ENOMEM; - } - - return 0; -} - -/* tell DSP we have processed notification */ -static int vfe_cmd_done(struct snd_sof_dev *sdev, int dir) +static int virtio_fe_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) { + /* not needed for FE ? */ return 0; } -/* Send the IPC message completed. This means vBE has received the cmd */ -static void vfe_cmd_tx_done(struct virtqueue *vq) -{ - struct snd_sof_ipc_msg *msg; - struct sof_vfe *vfe; - - vfe = vq->vdev->priv; - msg = vfe->msg; - msg->complete = true; - wake_up(&msg->waitq); -} - -static void vfe_cmd_handle_rx(struct virtqueue *vq) -{ -} - -static void vfe_not_tx_done(struct virtqueue *vq) -{ -} - -static void vfe_posn_update(struct work_struct *work) -{ - struct sof_ipc_stream_posn *posn = NULL; - struct sof_vfe *vfe = - container_of(work, struct sof_vfe, posn_update_work); - struct snd_sof_pcm *spcm; - struct scatterlist sg; - struct snd_sof_dev *sdev; - struct virtqueue *vq; - unsigned int buflen = 0; - int direction; - - vq = vfe->ipc_not_rx_vq; - sdev = vfe->sdev; - - /* virtio protects and make sure no re-entry */ - while ((posn = virtqueue_get_buf(vq, &buflen)) != NULL) { - spcm = snd_sof_find_spcm_comp(sdev, posn->comp_id, &direction); - if (!spcm) { - dev_err(sdev->dev, - "err: period elapsed for unused component %d\n", - posn->comp_id); - - /* kick back the empty posn buffer immediately */ - sg_init_one(&sg, posn, sizeof(*posn)); - virtqueue_add_inbuf(vq, &sg, 1, posn, GFP_KERNEL); - virtqueue_kick(vq); - continue; - } - - /* - * The position update requirement is valid. - * Let's update the position now. - */ - memcpy(&spcm->stream[direction].posn, posn, sizeof(*posn)); - snd_pcm_period_elapsed(spcm->stream[direction].substream); - - /* kick back the empty posn buffer immediately */ - sg_init_one(&sg, posn, sizeof(*posn)); - virtqueue_add_inbuf(vq, &sg, 1, posn, GFP_KERNEL); - virtqueue_kick(vq); - } -} - /* - * handle the pos_update, receive the posn and send to up layer, then - * resend the buffer to BE + * IPC Mailbox IO */ -static void vfe_not_handle_rx(struct virtqueue *vq) -{ - struct sof_vfe *vfe; - vfe = vq->vdev->priv; - - schedule_work(&vfe->posn_update_work); -} - -static struct sof_virtio_priv *sof_vfe_init(struct virtio_device *vdev) +static void virtio_fe_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) { - struct device *dev; - struct snd_soc_acpi_mach *mach; - struct snd_sof_pdata *sof_pdata; - struct sof_virtio_priv *priv; - int ret; - - dev = &vdev->dev; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return NULL; - - sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); - if (!sof_pdata) - return NULL; - - sof_pdata->drv_name = "sof-nocodec"; - sof_pdata->is_vfe = 1; /* This is audio vFE device */ - mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - if (!mach) - return NULL; - - ret = sof_nocodec_setup(dev, sof_pdata, mach, &virt_desc, - &snd_sof_vfe_ops); - if (ret < 0) - return NULL; - - mach->asoc_plat_name = "sof-vfe"; - mach->pdata = &snd_sof_vfe_ops; - - /* - * FIXME:currently, we use the guest local tplg file loading for easy - * debug, should swich to service request later. - */ - - sof_pdata->id = vdev->id.device; - sof_pdata->name = dev_name(&vdev->dev); - sof_pdata->machine = mach; - sof_pdata->desc = &virt_desc; - sof_pdata->dev = dev; - sof_pdata->vfe = vdev->priv; - - /* register machine driver */ - sof_pdata->pdev_mach = - platform_device_register_data(dev, mach->drv_name, -1, - sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(sof_pdata->pdev_mach)) { - pr_debug("creating sof machine driver failed\n"); - return NULL; - } - - dev_dbg(dev, "created machine %s\n", - dev_name(&sof_pdata->pdev_mach->dev)); - - dev_set_drvdata(dev, priv); - - priv->sof_pdata = sof_pdata; - - /* register PCM and DAI driver */ - priv->pdev_pcm = - platform_device_register_data(dev, "sof-audio", -1, - sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(priv->pdev_pcm)) { - dev_err(dev, "Cannot register device sof-audio. Error %d\n", - (int)PTR_ERR(priv->pdev_pcm)); - platform_device_unregister(sof_pdata->pdev_mach); - return NULL; - } - - return priv; + /* write data to message Q buffer before sending message */ } -static void sof_vfe_deinit(struct virtio_device *vdev) +static void virtio_fe_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) { - struct sof_vfe *vfe = vdev->priv; - struct sof_virtio_priv *priv = vfe->priv; - struct snd_sof_pdata *sof_pdata = priv->sof_pdata; - - platform_device_unregister(priv->pdev_pcm); - platform_device_unregister(sof_pdata->pdev_mach); + /* read data from message Q buffer after receiving message */ } -/* Probe and remove. */ -static int vfe_probe(struct virtio_device *vdev) +static int virtio_fe_tx_busy(struct snd_sof_dev *sdev) { - struct virtqueue *vqs[SOF_VIRTIO_NUM_OF_VQS]; - struct device *dev; - struct scatterlist sg; - struct sof_vfe *vfe; - int ret; - - /* the processing callback number must be the same as the vqueues.*/ - vq_callback_t *cbs[SOF_VIRTIO_NUM_OF_VQS] = { - vfe_cmd_tx_done, - vfe_cmd_handle_rx, - vfe_not_tx_done, - vfe_not_handle_rx - }; - - dev = &vdev->dev; - - dev->coherent_dma_mask = DMA_BIT_MASK(64); - dev->dma_mask = &dev->coherent_dma_mask; - vfe = devm_kzalloc(dev, sizeof(*vfe), GFP_KERNEL); - if (!vfe) - return -ENOMEM; - - vdev->priv = vfe; - - INIT_WORK(&vfe->posn_update_work, vfe_posn_update); - - /* create virt queue for vfe to send/receive IPC message. */ - ret = virtio_find_vqs(vdev, SOF_VIRTIO_NUM_OF_VQS, - vqs, cbs, sof_vq_names, NULL); - if (ret) { - dev_err(dev, "error: find vqs fail with %d\n", ret); - return ret; - } - - /* virtques */ - vfe->ipc_cmd_tx_vq = vqs[SOF_VIRTIO_IPC_CMD_TX_VQ]; - vfe->ipc_cmd_rx_vq = vqs[SOF_VIRTIO_IPC_CMD_RX_VQ]; - vfe->ipc_not_tx_vq = vqs[SOF_VIRTIO_IPC_NOT_TX_VQ]; - vfe->ipc_not_rx_vq = vqs[SOF_VIRTIO_IPC_NOT_RX_VQ]; - - virtio_device_ready(vdev); - - vfe->posn = kmalloc(sizeof(*vfe->posn), GFP_KERNEL); - sg_init_one(&sg, vfe->posn, sizeof(struct sof_ipc_stream_posn)); - if (vfe->ipc_not_rx_vq) { - ret = virtqueue_add_inbuf(vfe->ipc_not_rx_vq, - &sg, 1, vfe->posn, GFP_KERNEL); - } - virtqueue_kick(vfe->ipc_not_rx_vq); - - /* - * add the SOF related functions here, to load the - * topology, generate the components, and send IPC - */ - vfe->priv = sof_vfe_init(vdev); - - return ret; + /* return 1 if tx message Q is busy */ } -static void vfe_remove(struct virtio_device *vdev) +static int virtio_fe_tx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) { - /* free virtio resurces and unregister device */ - struct sof_vfe *vfe = vdev->priv; - - vdev->config->reset(vdev); - vdev->config->del_vqs(vdev); - cancel_work_sync(&vfe->posn_update_work); - kfree(vfe->posn); - - /* unregister the devices of SOF */ - sof_vfe_deinit(vdev); + /* write msg to the virtio queue message for BE */ - return; + return 0; } -static void virtaudio_config_changed(struct virtio_device *vdev) +static int virtio_fe_rx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) { + /* read the virtio queue message from BE and copy to msg */ + return 0; } -const struct virtio_device_id id_table[] = { - {VIRTIO_ID_AUDIO, VIRTIO_DEV_ANY_ID}, - {0}, -}; - /* - * TODO: There still need a shutdown to handle the case the UOS - * is poweroff, restart. + * Probe and remove. */ -static struct virtio_driver vfe_audio_driver = { - .feature_table = NULL, - .feature_table_size = 0, - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = vfe_probe, - .remove = vfe_remove, - .config_changed = virtaudio_config_changed, -}; - -static int vfe_register(struct snd_sof_dev *sdev) +static int virtio_fe_probe(struct snd_sof_dev *sdev) { - return 0; + /* register virtio device */ + + /* conenct virt queues to BE */ } -static int vfe_unregister(struct snd_sof_dev *sdev) +static int virtio_fe_remove(struct snd_sof_dev *sdev) { - return 0; + /* free virtio resurces and unregister device */ } -#define SKL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) +/* baytrail ops */ +struct snd_sof_dsp_ops snd_sof_virtio_fe_ops = { + /* device init */ + .probe = virtio_fe_probe, + .remove = virtio_fe_remove, -struct snd_soc_dai_driver virtio_dai[] = { - { - .name = "SSP4 Pin", - .playback = SOF_DAI_STREAM("ssp4 Tx", 1, 8, - SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), - .capture = SOF_DAI_STREAM("ssp4 Rx", 1, 8, - SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), - }, -}; + /* mailbox */ + .mailbox_read = virtio_fe_mailbox_read, + .mailbox_write = virtio_fe_mailbox_write, -/* virtio fe ops */ -struct snd_sof_dsp_ops snd_sof_vfe_ops = { - /* device init */ - .probe = vfe_register, - .remove = vfe_unregister, + /* ipc */ + .tx_msg = virtio_fe_tx_msg, + .rx_msg = virtio_fe_rx_msg, + .fw_ready = virtio_fe_fw_ready, + .tx_busy = virtio_fe_tx_busy, - /* IPC */ - .send_msg = vfe_send_msg, - .get_reply = vfe_get_reply, - .is_ready = vfe_is_ready, - .fw_ready = vfe_fw_ready, - .cmd_done = vfe_cmd_done, + /* module loading */ +// .load_module = snd_sof_parse_module_memcpy, - /* DAI drivers */ - .drv = virtio_dai, - .num_drv = 1, + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, }; -EXPORT_SYMBOL(snd_sof_vfe_ops); -module_virtio_driver(vfe_audio_driver); +EXPORT_SYMBOL(snd_sof_virtio_fe_ops); -MODULE_DEVICE_TABLE(virtio, id_table); -MODULE_DESCRIPTION("Sound Open Firmware Virtio FE"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/virtio-fe.h b/sound/soc/sof/virtio-fe.h deleted file mode 100644 index 3e38944f7a3ab3..00000000000000 --- a/sound/soc/sof/virtio-fe.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Author: Libin Yang - */ - -#ifndef __SOUND_SOC_SOF_VIRTIO_FE_H -#define __SOUND_SOC_SOF_VIRTIO_FE_H - -#include -#include -#include -#include -#include -#include - -struct sof_virtio_priv { - struct snd_sof_pdata *sof_pdata; - struct platform_device *pdev_pcm; -}; - -struct snd_sof_dsp_ops snd_sof_vfe_ops; - -#endif /* __SOUND_SOC_SOF_VIRTIO_FE_H */ diff --git a/sound/soc/sof/virtio-miscdev.c b/sound/soc/sof/virtio-miscdev.c deleted file mode 100644 index 571526c964d1f3..00000000000000 --- a/sound/soc/sof/virtio-miscdev.c +++ /dev/null @@ -1,252 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2017 Intel Corporation. All rights reserved. - * - * Author: Libin Yang - * Luo Xionghu - * Liam Girdwood - */ - -#include -#include -#include -#include -#include - -#include "sof-priv.h" -#include "virtio-be.h" -#include "virtio-miscdev.h" -#include - -/* - * This module registers a device node /dev/vbs_k_audio, - * that handle the communication between Device Model and - * the virtio backend service. The device model can - * control the backend to : set the status, set the vq account - * and etc. The config of the DM and VBS must be accordance. - */ - -static struct virtio_miscdev *virtio_audio; - -static struct virtio_miscdev *get_virtio_audio(void) -{ - return virtio_audio; -} - -struct snd_sof_dev *sof_virtio_get_sof(void) -{ - struct virtio_miscdev *vaudio = get_virtio_audio(); - - if (vaudio) - return (struct snd_sof_dev *)vaudio->priv; - - return NULL; -} - -static int sof_virtio_open(struct file *f, void *data) -{ - struct snd_sof_dev *sdev = (struct snd_sof_dev *)data; - struct sof_vbe *vbe; - int ret; - - ret = sof_vbe_register(sdev, &vbe); - if (ret) - return ret; - - /* - * link to sdev->vbe_list - * Maybe virtio_miscdev managing the list is more reasonable. - * Let's use sdev to manage the FE audios now. - */ - list_add(&vbe->list, &sdev->vbe_list); - f->private_data = vbe; - - return 0; -} - -static long sof_virtio_ioctl(struct file *f, void *data, unsigned int ioctl, - unsigned long arg) -{ - struct sof_vbe *vbe = f->private_data; - void __user *argp = (void __user *)arg; - int ret; - - switch (ioctl) { - case VBS_SET_DEV: - ret = virtio_dev_ioctl(&vbe->dev_info, ioctl, argp); - if (!ret) - vbe->vm_id = vbe->dev_info._ctx.vmid; - break; - case VBS_SET_VQ: - ret = virtio_vqs_ioctl(&vbe->dev_info, ioctl, argp); - if (ret) - return ret; - - /* - * Maybe we should move sof_register_vhm_client() - * in VBS_SET_DEV - */ - ret = sof_vbe_register_client(vbe); - if (ret) - return ret; - /* - * TODO: load tplg and send to FE here - * - * The better method is FE driver send FE-tplg id - * and request FE-tplg. - * Then BE loads the corresponding tplg based on - * the FE-tplg id and send to FE driver. - */ - break; - default: - return -ENOIOCTLCMD; - } - - return ret; -} - -static int sof_virtio_release(struct file *f, void *data) -{ - struct sof_vbe *vbe = f->private_data; - - list_del(&vbe->list); - devm_kfree(vbe->sdev->dev, vbe); - f->private_data = NULL; - return 0; -} - -static int vbs_audio_open(struct inode *inode, struct file *f) -{ - struct virtio_miscdev *vaudio = get_virtio_audio(); - - if (!vaudio) - return -ENODEV; /* This should never happen */ - - dev_dbg(vaudio->dev, "virtio audio open\n"); - if (vaudio->open) - return vaudio->open(f, virtio_audio->priv); - - return 0; -} - -static long vbs_audio_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) -{ - struct virtio_miscdev *vaudio = get_virtio_audio(); - - if (!vaudio) - return -ENODEV; /* This should never happen */ - - dev_dbg(vaudio->dev, "virtio audio ioctl\n"); - if (vaudio->ioctl) - return vaudio->ioctl(f, vaudio->priv, ioctl, arg); - else - return -ENXIO; -} - -static int vbs_audio_release(struct inode *inode, struct file *f) -{ - struct virtio_miscdev *vaudio = get_virtio_audio(); - - if (!vaudio) - return -ENODEV; /* This should never happen */ - - dev_dbg(vaudio->dev, "release virtio audio\n"); - - if (vaudio->release) - vaudio->release(f, vaudio->priv); - - return 0; -} - -static const struct file_operations vbs_audio_fops = { - .owner = THIS_MODULE, - .release = vbs_audio_release, - .unlocked_ioctl = vbs_audio_ioctl, - .open = vbs_audio_open, - .llseek = noop_llseek, -}; - -static struct miscdevice vbs_audio_k = { - .minor = MISC_DYNAMIC_MINOR, - .name = "vbs_k_audio", - .fops = &vbs_audio_fops, -}; - -static int audio_virtio_miscdev_register(struct device *dev, void *data, - struct virtio_miscdev **va) -{ - struct virtio_miscdev *vaudio; - int ret; - - ret = misc_register(&vbs_audio_k); - if (ret) { - dev_err(dev, "misc device register failed %d\n", ret); - return ret; - } - - vaudio = kzalloc(sizeof(*vaudio), GFP_KERNEL); - if (!vaudio) { - misc_deregister(&vbs_audio_k); - return -ENOMEM; - } - - vaudio->priv = data; - vaudio->dev = dev; - virtio_audio = vaudio; - *va = vaudio; - - return 0; -} - -static int audio_virtio_miscdev_unregister(void) -{ - if (virtio_audio) { - misc_deregister(&vbs_audio_k); - kfree(virtio_audio); - virtio_audio = NULL; - } - - return 0; -} - -/** - * sof_virtio_miscdev_register() - init the virtio be audio driver - * @sdev: the snd_sof_dev of sof core - * - * This function registers the misc device, which will be used - * by the user space to communicate with the audio driver. - * - * Return: 0 for success or negative value for err - */ -int sof_virtio_miscdev_register(struct snd_sof_dev *sdev) -{ - struct virtio_miscdev *vaudio; - int ret; - - ret = audio_virtio_miscdev_register(sdev->dev, sdev, &vaudio); - if (ret) - return ret; - - vaudio->open = sof_virtio_open; - vaudio->ioctl = sof_virtio_ioctl; - vaudio->release = sof_virtio_release; - - return 0; -} -EXPORT_SYMBOL(sof_virtio_miscdev_register); - -/** - * sof_virtio_miscdev_unregister() - release the virtio be audio driver - * - * This function deregisters the misc device, and free virtio_miscdev - * - */ -int sof_virtio_miscdev_unregister(void) -{ - return audio_virtio_miscdev_unregister(); -} -EXPORT_SYMBOL(sof_virtio_miscdev_unregister); diff --git a/sound/soc/sof/virtio-miscdev.h b/sound/soc/sof/virtio-miscdev.h deleted file mode 100644 index cce0ac7876cbea..00000000000000 --- a/sound/soc/sof/virtio-miscdev.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Author: Libin Yang - * Liam Girdwood - */ - -#ifndef __SOUND_SOF_VIRTIO_MISCDEV_H -#define __SOUND_SOF_VIRTIO_MISCDEV_H - -struct virtio_miscdev { - struct device *dev; - int (*open)(struct file *f, void *data); - long (*ioctl)(struct file *f, void *data, unsigned int ioctl, - unsigned long arg); - int (*release)(struct file *f, void *data); - void *priv; -}; - -#endif /* __SOUND_SOF_VIRTIO_MISCDEV_H */ From a9f7fed1626b940a90792f94e917bb6745d13fbb Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Wed, 11 Jul 2018 16:05:36 +0530 Subject: [PATCH 284/298] ASoC: Intel: Boards: Add GLK Realtek Maxim I2S machine driver Patch adds Geminilake I2S machine driver which uses following codecs: RT5682 and MAX98357A. Signed-off-by: Naveen Manohar Signed-off-by: Harsha Priya Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 8452112baac67c3235d15de67fb190d29bbba98f) --- sound/soc/intel/boards/Kconfig | 14 + sound/soc/intel/boards/glk_rt5682_max98357a.c | 643 ++++++++++++++++++ 2 files changed, 657 insertions(+) create mode 100644 sound/soc/intel/boards/glk_rt5682_max98357a.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index c52fbbb0712e55..f486031f5ff2c5 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -355,6 +355,20 @@ config SND_SOC_INTEL_CNL_RT274_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH + tristate "GLK with RT5682 and MAX98357A in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5682 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Geminilake platforms + with RT5682 + MAX98357A I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c new file mode 100644 index 00000000000000..c4b94e2617c59b --- /dev/null +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation. + +/* + * Intel Geminilake I2S Machine Driver with MAX98357A & RT5682 Codecs + * + * Modified from: + * Intel Apollolake I2S Machine driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../skylake/skl.h" +#include "../../codecs/rt5682.h" +#include "../../codecs/hdac_hdmi.h" + +/* The platform clock outputs 19.2Mhz clock to codec as I2S MCLK */ +#define GLK_PLAT_CLK_FREQ 19200000 +#define RT5682_PLL_FREQ (48000 * 512) +#define GLK_REALTEK_CODEC_DAI "rt5682-aif1" +#define GLK_MAXIM_CODEC_DAI "HiFi" +#define MAXIM_DEV0_NAME "MX98357A:00" +#define DUAL_CHANNEL 2 +#define QUAD_CHANNEL 4 +#define NAME_SIZE 32 + +static struct snd_soc_jack geminilake_hdmi[3]; + +struct glk_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct glk_card_private { + struct snd_soc_jack geminilake_headset; + struct list_head hdmi_pcm_list; +}; + +enum { + GLK_DPCM_AUDIO_PB = 0, + GLK_DPCM_AUDIO_CP, + GLK_DPCM_AUDIO_HS_PB, + GLK_DPCM_AUDIO_ECHO_REF_CP, + GLK_DPCM_AUDIO_REF_CP, + GLK_DPCM_AUDIO_DMIC_CP, + GLK_DPCM_AUDIO_HDMI1_PB, + GLK_DPCM_AUDIO_HDMI2_PB, + GLK_DPCM_AUDIO_HDMI3_PB, +}; + +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 snd_soc_dai *codec_dai; + int ret = 0; + + codec_dai = snd_soc_card_get_codec_dai(card, GLK_REALTEK_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); + if (ret) + dev_err(card->dev, "failed to stop sysclk: %d\n", ret); + } else if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, + GLK_PLAT_CLK_FREQ, RT5682_PLL_FREQ); + if (ret < 0) { + dev_err(card->dev, "can't set codec pll: %d\n", ret); + return ret; + } + } + + if (ret) + dev_err(card->dev, "failed to start internal clk: %d\n", ret); + + return ret; +} + +static const struct snd_kcontrol_new geminilake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget geminilake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + 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 geminilake_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* speaker */ + { "Spk", NULL, "Speaker" }, + + /* other jacks */ + { "Headset Mic", NULL, "Platform Clock" }, + { "IN1P", NULL, "Headset Mic" }, + + /* digital mics */ + { "DMic", NULL, "SoC DMIC" }, + + /* CODEC BE connections */ + { "HiFi Playback", NULL, "ssp1 Tx" }, + { "ssp1 Tx", NULL, "codec0_out" }, + + { "AIF1 Playback", NULL, "ssp2 Tx" }, + { "ssp2 Tx", NULL, "codec1_out" }, + + { "codec0_in", NULL, "ssp2 Rx" }, + { "ssp2 Rx", NULL, "AIF1 Capture" }, + + { "HDMI1", NULL, "hif5-0 Output" }, + { "HDMI2", NULL, "hif6-0 Output" }, + { "HDMI2", NULL, "hif7-0 Output" }, + + { "hifi3", NULL, "iDisp3 Tx" }, + { "iDisp3 Tx", NULL, "iDisp3_out" }, + { "hifi2", NULL, "iDisp2 Tx" }, + { "iDisp2 Tx", NULL, "iDisp2_out" }, + { "hifi1", NULL, "iDisp1 Tx" }, + { "iDisp1 Tx", NULL, "iDisp1_out" }, + + /* DMIC */ + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, +}; + +static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = DUAL_CHANNEL; + + /* set SSP to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_jack *jack; + int ret; + + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, + &ctx->geminilake_headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &ctx->geminilake_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +}; + +static int geminilake_rt5682_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + /* Set valid bitmask & configuration for I2S in 24 bit */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, 24); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + + return ret; +} + +static struct snd_soc_ops geminilake_rt5682_ops = { + .hw_params = geminilake_rt5682_hw_params, +}; + +static int geminilake_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct glk_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = GLK_DPCM_AUDIO_HDMI1_PB + dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int geminilake_rt5682_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_dapm_context *dapm; + int ret; + + dapm = snd_soc_component_get_dapm(component); + ret = snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + if (ret) { + dev_err(rtd->dev, "Ref Cap ignore suspend failed %d\n", ret); + return ret; + } + + return ret; +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static unsigned int channels_quad[] = { + QUAD_CHANNEL, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels_quad = { + .count = ARRAY_SIZE(channels_quad), + .list = channels_quad, + .mask = 0, +}; + +static int geminilake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* + * set BE channel constraint as user FE channels + */ + channels->min = channels->max = 4; + + return 0; +} + +static int geminilake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_min = runtime->hw.channels_max = QUAD_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels_quad); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static const struct snd_soc_ops geminilake_dmic_ops = { + .startup = geminilake_dmic_startup, +}; + +static const unsigned int rates_16000[] = { + 16000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static int geminilake_refcap_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +}; + +static const struct snd_soc_ops geminilake_refcap_ops = { + .startup = geminilake_refcap_startup, +}; + +/* geminilake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link geminilake_dais[] = { + /* Front End DAI links */ + [GLK_DPCM_AUDIO_PB] = { + .name = "Glk Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = geminilake_rt5682_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + [GLK_DPCM_AUDIO_CP] = { + .name = "Glk Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + [GLK_DPCM_AUDIO_HS_PB] = { + .name = "Glk Audio Headset Playback", + .stream_name = "Headset Audio", + .cpu_dai_name = "System Pin2", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dpcm_playback = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_ECHO_REF_CP] = { + .name = "Glk Audio Echo Reference cap", + .stream_name = "Echoreference Capture", + .cpu_dai_name = "Echoref Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = NULL, + .capture_only = 1, + .nonatomic = 1, + }, + [GLK_DPCM_AUDIO_REF_CP] = { + .name = "Glk Audio Reference cap", + .stream_name = "Refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &geminilake_refcap_ops, + }, + [GLK_DPCM_AUDIO_DMIC_CP] = { + .name = "Glk Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &geminilake_dmic_ops, + }, + [GLK_DPCM_AUDIO_HDMI1_PB] = { + .name = "Glk HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_HDMI2_PB] = { + .name = "Glk HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_HDMI3_PB] = { + .name = "Glk HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + /* Back End DAI links */ + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 0, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = MAXIM_DEV0_NAME, + .codec_dai_name = GLK_MAXIM_CODEC_DAI, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = geminilake_ssp_fixup, + .dpcm_playback = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .id = 1, + .cpu_dai_name = "SSP2 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "i2c-10EC5682:00", + .codec_dai_name = GLK_REALTEK_CODEC_DAI, + .init = geminilake_rt5682_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = geminilake_ssp_fixup, + .ops = &geminilake_rt5682_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .be_hw_params_fixup = geminilake_dmic_fixup, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int glk_card_late_probe(struct snd_soc_card *card) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + char jack_name[NAME_SIZE]; + struct glk_hdmi_pcm *pcm; + int err = 0; + int i = 0; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &geminilake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &geminilake_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + +/* geminilake audio machine driver for SPT + RT5682 */ +static struct snd_soc_card glk_audio_card_rt5682_m98357a = { + .name = "glkrt5682max", + .owner = THIS_MODULE, + .dai_link = geminilake_dais, + .num_links = ARRAY_SIZE(geminilake_dais), + .controls = geminilake_controls, + .num_controls = ARRAY_SIZE(geminilake_controls), + .dapm_widgets = geminilake_widgets, + .num_dapm_widgets = ARRAY_SIZE(geminilake_widgets), + .dapm_routes = geminilake_map, + .num_dapm_routes = ARRAY_SIZE(geminilake_map), + .fully_routed = true, + .late_probe = glk_card_late_probe, +}; + +static int geminilake_audio_probe(struct platform_device *pdev) +{ + struct glk_card_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + glk_audio_card_rt5682_m98357a.dev = &pdev->dev; + snd_soc_card_set_drvdata(&glk_audio_card_rt5682_m98357a, ctx); + + return devm_snd_soc_register_card(&pdev->dev, + &glk_audio_card_rt5682_m98357a); +} + +static const struct platform_device_id glk_board_ids[] = { + { + .name = "glk_rt5682_max98357a", + .driver_data = + (kernel_ulong_t)&glk_audio_card_rt5682_m98357a, + }, + { } +}; + +static struct platform_driver geminilake_audio = { + .probe = geminilake_audio_probe, + .driver = { + .name = "glk_rt5682_max98357a", + .pm = &snd_soc_pm_ops, + }, + .id_table = glk_board_ids, +}; +module_platform_driver(geminilake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Geminilake Audio Machine driver-RT5682 & MAX98357A in I2S mode"); +MODULE_AUTHOR("Naveen Manohar "); +MODULE_AUTHOR("Harsha Priya "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:glk_rt5682_max98357a"); From cc28c37314fd7bde159403f5143ca70d3b7f62cd Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Mon, 18 Jun 2018 13:29:38 -0500 Subject: [PATCH 285/298] ASoC: Intel: common: Add Geminilake Dialog+Maxim machine driver entry This patch adds da7219_max98357a machine driver entry into machine table Signed-off-by: Naveen Manohar Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 65a33883c778befcb85ef45285763fd8ac1b2ba3) --- sound/soc/intel/common/soc-acpi-intel-glk-match.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index 305875af71caee..9d32d4f2485853 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -33,6 +33,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .sof_tplg_filename = "intel/sof-glk-da7219.tplg", .asoc_plat_name = "0000:00:0e.0", }, + { + .id = "DLGS7219", + .drv_name = "glk_da7219_max98357a", + .fw_filename = "intel/dsp_fw_glk.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &glk_codecs, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); From 5d6bc7c410dee1585708470e5c1d848ff2a3e8d5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 24 Oct 2018 17:03:30 -0500 Subject: [PATCH 286/298] ASoC: Intel: boards: reorder Kconfig Align with upstream code Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index f486031f5ff2c5..5844158db69bd3 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -339,21 +339,10 @@ config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH SKL/KBL/BXT/APL with iDisp, HDA audio codecs. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_HDA -if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_CANNONLAKE - -config SND_SOC_INTEL_CNL_RT274_MACH - tristate "Cannonlake with RT274 I2S mode" - depends on MFD_INTEL_LPSS && I2C && ACPI - select SND_SOC_RT274 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC machine driver for Cannonlake platform - with RT274 I2S audio codec. - Say Y or m if you have such a device. This is a recommended option. - If unsure select "N". +if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_GEMINILAKE config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH tristate "GLK with RT5682 and MAX98357A in I2S Mode" @@ -369,6 +358,22 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_INTEL_SKYLAKE +endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_GEMINILAKE + +if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_CANNONLAKE + +config SND_SOC_INTEL_CNL_RT274_MACH + tristate "Cannonlake with RT274 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT274 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Cannonlake platform + with RT274 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + +endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_CANNONLAKE endif ## SND_SOC_INTEL_MACH From ab0114e373cbe10eecbafb9fe2c70f64bd24b9e1 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 7 Jun 2018 16:37:38 +0800 Subject: [PATCH 287/298] ASoC: rt5682: add rt5682 codec driver This is the initial codec driver for rt5682. Signed-off-by: Bard Liao Signed-off-by: Mark Brown (cherry picked from commit 0ddce71c21f03fd19867c4939d3ca710f37cdf1a) --- .../devicetree/bindings/sound/rt5682.txt | 50 + include/sound/rt5682.h | 40 + sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5682.c | 2682 +++++++++++++++++ sound/soc/codecs/rt5682.h | 1324 ++++++++ 6 files changed, 4104 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/rt5682.txt create mode 100644 include/sound/rt5682.h create mode 100644 sound/soc/codecs/rt5682.c create mode 100644 sound/soc/codecs/rt5682.h diff --git a/Documentation/devicetree/bindings/sound/rt5682.txt b/Documentation/devicetree/bindings/sound/rt5682.txt new file mode 100644 index 00000000000000..312e9a129530cf --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rt5682.txt @@ -0,0 +1,50 @@ +RT5682 audio CODEC + +This device supports I2C only. + +Required properties: + +- compatible : "realtek,rt5682" or "realtek,rt5682i" + +- reg : The I2C address of the device. + +Optional properties: + +- interrupts : The CODEC's interrupt output. + +- realtek,dmic1-data-pin + 0: dmic1 is not used + 1: using GPIO2 pin as dmic1 data pin + 2: using GPIO5 pin as dmic1 data pin + +- realtek,dmic1-clk-pin + 0: using GPIO1 pin as dmic1 clock pin + 1: using GPIO3 pin as dmic1 clock pin + +- realtek,jd-src + 0: No JD is used + 1: using JD1 as JD source + +- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. + +Pins on the device (for linking into audio routes) for RT5682: + + * DMIC L1 + * DMIC R1 + * IN1P + * HPOL + * HPOR + +Example: + +rt5682 { + compatible = "realtek,rt5682i"; + reg = <0x1a>; + interrupt-parent = <&gpio>; + interrupts = ; + realtek,ldo1-en-gpios = + <&gpio TEGRA_GPIO(R, 2) GPIO_ACTIVE_HIGH>; + realtek,dmic1-data-pin = <1>; + realtek,dmic1-clk-pin = <1>; + realtek,jd-src = <1>; +}; diff --git a/include/sound/rt5682.h b/include/sound/rt5682.h new file mode 100644 index 00000000000000..0251797ab438bd --- /dev/null +++ b/include/sound/rt5682.h @@ -0,0 +1,40 @@ +/* + * linux/sound/rt5682.h -- Platform data for RT5682 + * + * Copyright 2018 Realtek Microelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_RT5682_H +#define __LINUX_SND_RT5682_H + +enum rt5682_dmic1_data_pin { + RT5682_DMIC1_NULL, + RT5682_DMIC1_DATA_GPIO2, + RT5682_DMIC1_DATA_GPIO5, +}; + +enum rt5682_dmic1_clk_pin { + RT5682_DMIC1_CLK_GPIO1, + RT5682_DMIC1_CLK_GPIO3, +}; + +enum rt5682_jd_src { + RT5682_JD_NULL, + RT5682_JD1, +}; + +struct rt5682_platform_data { + + int ldo1_en; /* GPIO for LDO1_EN */ + + enum rt5682_dmic1_data_pin dmic1_data_pin; + enum rt5682_dmic1_clk_pin dmic1_clk_pin; + enum rt5682_jd_src jd_src; +}; + +#endif + diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e997881db3c1b0..423962559ccce4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -142,6 +142,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5668 if I2C select SND_SOC_RT5670 if I2C select SND_SOC_RT5677 if I2C && SPI_MASTER + select SND_SOC_RT5682 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SIRF_AUDIO_CODEC @@ -784,6 +785,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5668=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y + default y if SND_SOC_RT5682=y default y if SND_SOC_RT1305=y default m if SND_SOC_RT5514=m default m if SND_SOC_RT5616=m @@ -797,6 +799,7 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5668=m default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m + default m if SND_SOC_RT5682=m default m if SND_SOC_RT1305=m config SND_SOC_RL6347A @@ -877,6 +880,9 @@ config SND_SOC_RT5677_SPI tristate default SND_SOC_RT5677 && SPI +config SND_SOC_RT5682 + tristate + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d5bbdf7c92dbeb..bf15641654d74a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -147,6 +147,7 @@ snd-soc-rt5668-objs := rt5668.o snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o +snd-soc-rt5682-objs := rt5682.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -408,6 +409,7 @@ obj-$(CONFIG_SND_SOC_RT5668) += snd-soc-rt5668.o obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o +obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c new file mode 100644 index 00000000000000..61a97301bcfad6 --- /dev/null +++ b/sound/soc/codecs/rt5682.c @@ -0,0 +1,2682 @@ +/* + * rt5682.c -- RT5682 ALSA SoC audio component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Bard Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5682.h" + +#define RT5682_NUM_SUPPLIES 3 + +static const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", +}; + +struct rt5682_priv { + struct snd_soc_component *component; + struct rt5682_platform_data pdata; + struct regmap *regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + + int sysclk; + int sysclk_src; + int lrck[RT5682_AIFS]; + int bclk[RT5682_AIFS]; + int master[RT5682_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + +static const struct reg_sequence patch_list[] = { + {0x01c1, 0x1000}, +}; + +static const struct reg_default rt5682_reg[] = { + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0008, 0x800f}, + {0x000b, 0x0000}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0019, 0xafaf}, + {0x001c, 0x2f2f}, + {0x001f, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0024, 0x000b}, + {0x0026, 0xc0c4}, + {0x0029, 0x8080}, + {0x002a, 0xa0a0}, + {0x002b, 0x0300}, + {0x0030, 0x0000}, + {0x003c, 0x0080}, + {0x0044, 0x0c0c}, + {0x0049, 0x0000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003f}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x0030}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006c, 0x0000}, + {0x006d, 0x2200}, + {0x006e, 0x0a10}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0073, 0x0000}, + {0x0074, 0x0000}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0100}, + {0x007e, 0x0000}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0005}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x008c, 0x0003}, + {0x008d, 0x0000}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x009a, 0x0000}, + {0x009b, 0x0000}, + {0x009c, 0x0000}, + {0x009d, 0x0000}, + {0x009e, 0x100c}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0002}, + {0x00be, 0x0000}, + {0x00c0, 0x0160}, + {0x00c1, 0x82a0}, + {0x00c2, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d2, 0x3300}, + {0x00d3, 0x2200}, + {0x00d4, 0x0000}, + {0x00d9, 0x0009}, + {0x00da, 0x0000}, + {0x00db, 0x0000}, + {0x00dc, 0x00c0}, + {0x00dd, 0x2220}, + {0x00de, 0x3131}, + {0x00df, 0x3131}, + {0x00e0, 0x3131}, + {0x00e2, 0x0000}, + {0x00e3, 0x4000}, + {0x00e4, 0x0aa0}, + {0x00e5, 0x3131}, + {0x00e6, 0x3131}, + {0x00e7, 0x3131}, + {0x00e8, 0x3131}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00f0, 0x0000}, + {0x00f1, 0x00d0}, + {0x00f2, 0x00d0}, + {0x00f6, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6530}, + {0x0100, 0xa0a0}, + {0x010b, 0x0000}, + {0x010c, 0xae00}, + {0x010d, 0xaaa0}, + {0x010e, 0x8aa2}, + {0x010f, 0x02a2}, + {0x0110, 0xc000}, + {0x0111, 0x04a2}, + {0x0112, 0x2800}, + {0x0113, 0x0000}, + {0x0117, 0x0100}, + {0x0125, 0x0410}, + {0x0132, 0x6026}, + {0x0136, 0x5555}, + {0x0138, 0x3700}, + {0x013a, 0x2000}, + {0x013b, 0x2000}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0142, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0149, 0x0000}, + {0x0150, 0x79a1}, + {0x0151, 0x0000}, + {0x0160, 0x4ec0}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0168, 0x000f}, + {0x0169, 0x0021}, + {0x0190, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0197, 0x0022}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01af, 0x0000}, + {0x01b0, 0x0400}, + {0x01b1, 0x0000}, + {0x01b2, 0x0000}, + {0x01b3, 0x0000}, + {0x01b4, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x0000}, + {0x01d7, 0x0008}, + {0x01d8, 0x0029}, + {0x01d9, 0x3333}, + {0x01da, 0x0000}, + {0x01db, 0x0004}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0x0000}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0210, 0x6297}, + {0x0211, 0xa005}, + {0x0212, 0x824c}, + {0x0213, 0xf7ff}, + {0x0214, 0xf24c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x021c, 0x0000}, + {0x0250, 0x4500}, + {0x0251, 0x40b3}, + {0x0252, 0x0000}, + {0x0253, 0x0000}, + {0x0254, 0x0000}, + {0x0255, 0x0000}, + {0x0256, 0x0000}, + {0x0257, 0x0000}, + {0x0258, 0x0000}, + {0x0259, 0x0000}, + {0x025a, 0x0005}, + {0x0270, 0x0000}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; + +static bool rt5682_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_CBJ_CTRL_2: + case RT5682_INT_ST_1: + case RT5682_4BTN_IL_CMD_1: + case RT5682_AJD1_CTRL: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_DEVICE_ID: + case RT5682_I2C_MODE: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_EFUSE_CTRL_2: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_EFUSE_CTRL_6...RT5682_EFUSE_CTRL_11: + case RT5682_HP_CALIB_STA_1...RT5682_HP_CALIB_STA_11: + return true; + default: + return false; + } +} + +static bool rt5682_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_VERSION_ID: + case RT5682_VENDOR_ID: + case RT5682_DEVICE_ID: + case RT5682_HP_CTRL_1: + case RT5682_HP_CTRL_2: + case RT5682_HPL_GAIN: + case RT5682_HPR_GAIN: + case RT5682_I2C_CTRL: + case RT5682_CBJ_BST_CTRL: + case RT5682_CBJ_CTRL_1: + case RT5682_CBJ_CTRL_2: + case RT5682_CBJ_CTRL_3: + case RT5682_CBJ_CTRL_4: + case RT5682_CBJ_CTRL_5: + case RT5682_CBJ_CTRL_6: + case RT5682_CBJ_CTRL_7: + case RT5682_DAC1_DIG_VOL: + case RT5682_STO1_ADC_DIG_VOL: + case RT5682_STO1_ADC_BOOST: + case RT5682_HP_IMP_GAIN_1: + case RT5682_HP_IMP_GAIN_2: + case RT5682_SIDETONE_CTRL: + case RT5682_STO1_ADC_MIXER: + case RT5682_AD_DA_MIXER: + case RT5682_STO1_DAC_MIXER: + case RT5682_A_DAC1_MUX: + case RT5682_DIG_INF2_DATA: + case RT5682_REC_MIXER: + case RT5682_CAL_REC: + case RT5682_ALC_BACK_GAIN: + case RT5682_PWR_DIG_1: + case RT5682_PWR_DIG_2: + case RT5682_PWR_ANLG_1: + case RT5682_PWR_ANLG_2: + case RT5682_PWR_ANLG_3: + case RT5682_PWR_MIXER: + case RT5682_PWR_VOL: + case RT5682_CLK_DET: + case RT5682_RESET_LPF_CTRL: + case RT5682_RESET_HPF_CTRL: + case RT5682_DMIC_CTRL_1: + case RT5682_I2S1_SDP: + case RT5682_I2S2_SDP: + case RT5682_ADDA_CLK_1: + case RT5682_ADDA_CLK_2: + case RT5682_I2S1_F_DIV_CTRL_1: + case RT5682_I2S1_F_DIV_CTRL_2: + case RT5682_TDM_CTRL: + case RT5682_TDM_ADDA_CTRL_1: + case RT5682_TDM_ADDA_CTRL_2: + case RT5682_DATA_SEL_CTRL_1: + case RT5682_TDM_TCON_CTRL: + case RT5682_GLB_CLK: + case RT5682_PLL_CTRL_1: + case RT5682_PLL_CTRL_2: + case RT5682_PLL_TRACK_1: + case RT5682_PLL_TRACK_2: + case RT5682_PLL_TRACK_3: + case RT5682_PLL_TRACK_4: + case RT5682_PLL_TRACK_5: + case RT5682_PLL_TRACK_6: + case RT5682_PLL_TRACK_11: + case RT5682_SDW_REF_CLK: + case RT5682_DEPOP_1: + case RT5682_DEPOP_2: + case RT5682_HP_CHARGE_PUMP_1: + case RT5682_HP_CHARGE_PUMP_2: + case RT5682_MICBIAS_1: + case RT5682_MICBIAS_2: + case RT5682_PLL_TRACK_12: + case RT5682_PLL_TRACK_14: + case RT5682_PLL2_CTRL_1: + case RT5682_PLL2_CTRL_2: + case RT5682_PLL2_CTRL_3: + case RT5682_PLL2_CTRL_4: + case RT5682_RC_CLK_CTRL: + case RT5682_I2S_M_CLK_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_2: + case RT5682_EQ_CTRL_1: + case RT5682_EQ_CTRL_2: + case RT5682_IRQ_CTRL_1: + case RT5682_IRQ_CTRL_2: + case RT5682_IRQ_CTRL_3: + case RT5682_IRQ_CTRL_4: + case RT5682_INT_ST_1: + case RT5682_GPIO_CTRL_1: + case RT5682_GPIO_CTRL_2: + case RT5682_GPIO_CTRL_3: + case RT5682_HP_AMP_DET_CTRL_1: + case RT5682_HP_AMP_DET_CTRL_2: + case RT5682_MID_HP_AMP_DET: + case RT5682_LOW_HP_AMP_DET: + case RT5682_DELAY_BUF_CTRL: + case RT5682_SV_ZCD_1: + case RT5682_SV_ZCD_2: + case RT5682_IL_CMD_1: + case RT5682_IL_CMD_2: + case RT5682_IL_CMD_3: + case RT5682_IL_CMD_4: + case RT5682_IL_CMD_5: + case RT5682_IL_CMD_6: + case RT5682_4BTN_IL_CMD_1: + case RT5682_4BTN_IL_CMD_2: + case RT5682_4BTN_IL_CMD_3: + case RT5682_4BTN_IL_CMD_4: + case RT5682_4BTN_IL_CMD_5: + case RT5682_4BTN_IL_CMD_6: + case RT5682_4BTN_IL_CMD_7: + case RT5682_ADC_STO1_HP_CTRL_1: + case RT5682_ADC_STO1_HP_CTRL_2: + case RT5682_AJD1_CTRL: + case RT5682_JD1_THD: + case RT5682_JD2_THD: + case RT5682_JD_CTRL_1: + case RT5682_DUMMY_1: + case RT5682_DUMMY_2: + case RT5682_DUMMY_3: + case RT5682_DAC_ADC_DIG_VOL1: + case RT5682_BIAS_CUR_CTRL_2: + case RT5682_BIAS_CUR_CTRL_3: + case RT5682_BIAS_CUR_CTRL_4: + case RT5682_BIAS_CUR_CTRL_5: + case RT5682_BIAS_CUR_CTRL_6: + case RT5682_BIAS_CUR_CTRL_7: + case RT5682_BIAS_CUR_CTRL_8: + case RT5682_BIAS_CUR_CTRL_9: + case RT5682_BIAS_CUR_CTRL_10: + case RT5682_VREF_REC_OP_FB_CAP_CTRL: + case RT5682_CHARGE_PUMP_1: + case RT5682_DIG_IN_CTRL_1: + case RT5682_PAD_DRIVING_CTRL: + case RT5682_SOFT_RAMP_DEPOP: + case RT5682_CHOP_DAC: + case RT5682_CHOP_ADC: + case RT5682_CALIB_ADC_CTRL: + case RT5682_VOL_TEST: + case RT5682_SPKVDD_DET_STA: + case RT5682_TEST_MODE_CTRL_1: + case RT5682_TEST_MODE_CTRL_2: + case RT5682_TEST_MODE_CTRL_3: + case RT5682_TEST_MODE_CTRL_4: + case RT5682_TEST_MODE_CTRL_5: + case RT5682_PLL1_INTERNAL: + case RT5682_PLL2_INTERNAL: + case RT5682_STO_NG2_CTRL_1: + case RT5682_STO_NG2_CTRL_2: + case RT5682_STO_NG2_CTRL_3: + case RT5682_STO_NG2_CTRL_4: + case RT5682_STO_NG2_CTRL_5: + case RT5682_STO_NG2_CTRL_6: + case RT5682_STO_NG2_CTRL_7: + case RT5682_STO_NG2_CTRL_8: + case RT5682_STO_NG2_CTRL_9: + case RT5682_STO_NG2_CTRL_10: + case RT5682_STO1_DAC_SIL_DET: + case RT5682_SIL_PSV_CTRL1: + case RT5682_SIL_PSV_CTRL2: + case RT5682_SIL_PSV_CTRL3: + case RT5682_SIL_PSV_CTRL4: + case RT5682_SIL_PSV_CTRL5: + case RT5682_HP_IMP_SENS_CTRL_01: + case RT5682_HP_IMP_SENS_CTRL_02: + case RT5682_HP_IMP_SENS_CTRL_03: + case RT5682_HP_IMP_SENS_CTRL_04: + case RT5682_HP_IMP_SENS_CTRL_05: + case RT5682_HP_IMP_SENS_CTRL_06: + case RT5682_HP_IMP_SENS_CTRL_07: + case RT5682_HP_IMP_SENS_CTRL_08: + case RT5682_HP_IMP_SENS_CTRL_09: + case RT5682_HP_IMP_SENS_CTRL_10: + case RT5682_HP_IMP_SENS_CTRL_11: + case RT5682_HP_IMP_SENS_CTRL_12: + case RT5682_HP_IMP_SENS_CTRL_13: + case RT5682_HP_IMP_SENS_CTRL_14: + case RT5682_HP_IMP_SENS_CTRL_15: + case RT5682_HP_IMP_SENS_CTRL_16: + case RT5682_HP_IMP_SENS_CTRL_17: + case RT5682_HP_IMP_SENS_CTRL_18: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_HP_IMP_SENS_CTRL_20: + case RT5682_HP_IMP_SENS_CTRL_21: + case RT5682_HP_IMP_SENS_CTRL_22: + case RT5682_HP_IMP_SENS_CTRL_23: + case RT5682_HP_IMP_SENS_CTRL_24: + case RT5682_HP_IMP_SENS_CTRL_25: + case RT5682_HP_IMP_SENS_CTRL_26: + case RT5682_HP_IMP_SENS_CTRL_27: + case RT5682_HP_IMP_SENS_CTRL_28: + case RT5682_HP_IMP_SENS_CTRL_29: + case RT5682_HP_IMP_SENS_CTRL_30: + case RT5682_HP_IMP_SENS_CTRL_31: + case RT5682_HP_IMP_SENS_CTRL_32: + case RT5682_HP_IMP_SENS_CTRL_33: + case RT5682_HP_IMP_SENS_CTRL_34: + case RT5682_HP_IMP_SENS_CTRL_35: + case RT5682_HP_IMP_SENS_CTRL_36: + case RT5682_HP_IMP_SENS_CTRL_37: + case RT5682_HP_IMP_SENS_CTRL_38: + case RT5682_HP_IMP_SENS_CTRL_39: + case RT5682_HP_IMP_SENS_CTRL_40: + case RT5682_HP_IMP_SENS_CTRL_41: + case RT5682_HP_IMP_SENS_CTRL_42: + case RT5682_HP_IMP_SENS_CTRL_43: + case RT5682_HP_LOGIC_CTRL_1: + case RT5682_HP_LOGIC_CTRL_2: + case RT5682_HP_LOGIC_CTRL_3: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_HP_CALIB_CTRL_2: + case RT5682_HP_CALIB_CTRL_3: + case RT5682_HP_CALIB_CTRL_4: + case RT5682_HP_CALIB_CTRL_5: + case RT5682_HP_CALIB_CTRL_6: + case RT5682_HP_CALIB_CTRL_7: + case RT5682_HP_CALIB_CTRL_9: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_HP_CALIB_CTRL_11: + case RT5682_HP_CALIB_STA_1: + case RT5682_HP_CALIB_STA_2: + case RT5682_HP_CALIB_STA_3: + case RT5682_HP_CALIB_STA_4: + case RT5682_HP_CALIB_STA_5: + case RT5682_HP_CALIB_STA_6: + case RT5682_HP_CALIB_STA_7: + case RT5682_HP_CALIB_STA_8: + case RT5682_HP_CALIB_STA_9: + case RT5682_HP_CALIB_STA_10: + case RT5682_HP_CALIB_STA_11: + case RT5682_SAR_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_3: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_5: + case RT5682_SAR_IL_CMD_6: + case RT5682_SAR_IL_CMD_7: + case RT5682_SAR_IL_CMD_8: + case RT5682_SAR_IL_CMD_9: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_SAR_IL_CMD_12: + case RT5682_SAR_IL_CMD_13: + case RT5682_EFUSE_CTRL_1: + case RT5682_EFUSE_CTRL_2: + case RT5682_EFUSE_CTRL_3: + case RT5682_EFUSE_CTRL_4: + case RT5682_EFUSE_CTRL_5: + case RT5682_EFUSE_CTRL_6: + case RT5682_EFUSE_CTRL_7: + case RT5682_EFUSE_CTRL_8: + case RT5682_EFUSE_CTRL_9: + case RT5682_EFUSE_CTRL_10: + case RT5682_EFUSE_CTRL_11: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_DRC1_CTRL_0: + case RT5682_DRC1_CTRL_1: + case RT5682_DRC1_CTRL_2: + case RT5682_DRC1_CTRL_3: + case RT5682_DRC1_CTRL_4: + case RT5682_DRC1_CTRL_5: + case RT5682_DRC1_CTRL_6: + case RT5682_DRC1_HARD_LMT_CTRL_1: + case RT5682_DRC1_HARD_LMT_CTRL_2: + case RT5682_DRC1_PRIV_1: + case RT5682_DRC1_PRIV_2: + case RT5682_DRC1_PRIV_3: + case RT5682_DRC1_PRIV_4: + case RT5682_DRC1_PRIV_5: + case RT5682_DRC1_PRIV_6: + case RT5682_DRC1_PRIV_7: + case RT5682_DRC1_PRIV_8: + case RT5682_EQ_AUTO_RCV_CTRL1: + case RT5682_EQ_AUTO_RCV_CTRL2: + case RT5682_EQ_AUTO_RCV_CTRL3: + case RT5682_EQ_AUTO_RCV_CTRL4: + case RT5682_EQ_AUTO_RCV_CTRL5: + case RT5682_EQ_AUTO_RCV_CTRL6: + case RT5682_EQ_AUTO_RCV_CTRL7: + case RT5682_EQ_AUTO_RCV_CTRL8: + case RT5682_EQ_AUTO_RCV_CTRL9: + case RT5682_EQ_AUTO_RCV_CTRL10: + case RT5682_EQ_AUTO_RCV_CTRL11: + case RT5682_EQ_AUTO_RCV_CTRL12: + case RT5682_EQ_AUTO_RCV_CTRL13: + case RT5682_ADC_L_EQ_LPF1_A1: + case RT5682_R_EQ_LPF1_A1: + case RT5682_L_EQ_LPF1_H0: + case RT5682_R_EQ_LPF1_H0: + case RT5682_L_EQ_BPF1_A1: + case RT5682_R_EQ_BPF1_A1: + case RT5682_L_EQ_BPF1_A2: + case RT5682_R_EQ_BPF1_A2: + case RT5682_L_EQ_BPF1_H0: + case RT5682_R_EQ_BPF1_H0: + case RT5682_L_EQ_BPF2_A1: + case RT5682_R_EQ_BPF2_A1: + case RT5682_L_EQ_BPF2_A2: + case RT5682_R_EQ_BPF2_A2: + case RT5682_L_EQ_BPF2_H0: + case RT5682_R_EQ_BPF2_H0: + case RT5682_L_EQ_BPF3_A1: + case RT5682_R_EQ_BPF3_A1: + case RT5682_L_EQ_BPF3_A2: + case RT5682_R_EQ_BPF3_A2: + case RT5682_L_EQ_BPF3_H0: + case RT5682_R_EQ_BPF3_H0: + case RT5682_L_EQ_BPF4_A1: + case RT5682_R_EQ_BPF4_A1: + case RT5682_L_EQ_BPF4_A2: + case RT5682_R_EQ_BPF4_A2: + case RT5682_L_EQ_BPF4_H0: + case RT5682_R_EQ_BPF4_H0: + case RT5682_L_EQ_HPF1_A1: + case RT5682_R_EQ_HPF1_A1: + case RT5682_L_EQ_HPF1_H0: + case RT5682_R_EQ_HPF1_H0: + case RT5682_L_EQ_PRE_VOL: + case RT5682_R_EQ_PRE_VOL: + case RT5682_L_EQ_POST_VOL: + case RT5682_R_EQ_POST_VOL: + case RT5682_I2C_MODE: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5682_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682_if2_adc_enum, + RT5682_DIG_INF2_DATA, RT5682_IF2_ADC_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_01_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC1_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_23_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC2_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_45_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC3_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_67_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC4_SEL_SFT, rt5682_data_select); + +static const struct snd_kcontrol_new rt5682_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682_if2_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum); + +static void rt5682_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT5682_RESET, 0); + regmap_write(regmap, RT5682_I2C_MODE, 1); +} +/** + * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the component driver will turn on + * ASRC for these filters if ASRC is selected as their clock source. + */ +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + + switch (clk_src) { + case RT5682_CLK_SEL_SYS: + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5682_DA_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_2, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + if (filter_mask & RT5682_AD_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_3, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_sel_asrc_clk_src); + +static int rt5682_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read32(component, RT5682_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5682_4BTN_IL_CMD_1, val); + pr_debug("%s btn_type=%x\n", __func__, btn_type); + + return btn_type; +} + +static void rt5682_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_BTN); + snd_soc_component_write(component, RT5682_IL_CMD_1, 0x0040); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK, + RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR); + snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_EN); + } else { + snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK, RT5682_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_RST); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_TYPE); + } +} + +/** + * rt5682_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5682_headset_detect(struct snd_soc_component *component, + int jack_insert) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int val, count; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH); + + count = 0; + val = snd_soc_component_read32(component, RT5682_CBJ_CTRL_2) + & RT5682_JACK_TYPE_MASK; + while (val == 0 && count < 50) { + usleep_range(10000, 15000); + val = snd_soc_component_read32(component, + RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; + count++; + } + + switch (val) { + case 0x1: + case 0x2: + rt5682->jack_type = SND_JACK_HEADSET; + rt5682_enable_push_button_irq(component, true); + break; + default: + rt5682->jack_type = SND_JACK_HEADPHONE; + } + + } else { + rt5682_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + + rt5682->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type); + return rt5682->jack_type; +} + +static irqreturn_t rt5682_irq(int irq, void *data) +{ + struct rt5682_priv *rt5682 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5682_jd_check_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv, + jd_check_work.work); + + if (snd_soc_component_read32(rt5682->component, RT5682_AJD1_CTRL) + & RT5682_JDH_RS_MASK) { + /* jack out */ + rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); + + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + schedule_delayed_work(&rt5682->jd_check_work, 500); + } +} + +static int rt5682_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + switch (rt5682->pdata.jd_src) { + case RT5682_JD1: + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5682_CBJ_CTRL_1, 0xd042); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_3, + RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_IRQ | RT5682_POW_JDH | + RT5682_POW_ANA, RT5682_POW_IRQ | + RT5682_POW_JDH | RT5682_POW_ANA); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH | RT5682_PWR_JDL, + RT5682_PWR_JDH | RT5682_PWR_JDL); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, + RT5682_JD1_EN | RT5682_JD1_POL_NOR); + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + break; + + case RT5682_JD_NULL: + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + + rt5682->hs_jack = hs_jack; + + return 0; +} + +static void rt5682_jack_detect_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = + container_of(work, struct rt5682_priv, jack_detect_work.work); + int val, btn_type; + + while (!rt5682->component) + usleep_range(10000, 15000); + + while (!rt5682->component->card->instantiated) + usleep_range(10000, 15000); + + mutex_lock(&rt5682->calibrate_mutex); + + val = snd_soc_component_read32(rt5682->component, RT5682_AJD1_CTRL) + & RT5682_JDH_RS_MASK; + if (!val) { + /* jack in */ + if (rt5682->jack_type == 0) { + /* jack was out, report jack type */ + rt5682->jack_type = + rt5682_headset_detect(rt5682->component, 1); + } else { + /* jack is already in, report button event */ + rt5682->jack_type = SND_JACK_HEADSET; + btn_type = rt5682_button_detect(rt5682->component); + /** + * rt5682 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5682->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5682->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5682->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5682->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5682->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); + } + + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5682->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5682->jd_check_work); + + mutex_unlock(&rt5682->calibrate_mutex); +} + +static const struct snd_kcontrol_new rt5682_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5682_HPL_GAIN, + RT5682_HPR_GAIN, RT5682_G_HP_SFT, 15, 1, hp_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, + RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 175, 0, dac_vol_tlv), + + /* IN Boost Volume */ + SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL, + RT5682_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, RT5682_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682_STO1_ADC_BOOST, + RT5682_STO1_ADC_L_BST_SFT, RT5682_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + + +static int rt5682_div_sel(struct rt5682_priv *rt5682, + int target, const int div[], int size) +{ + int i; + + if (rt5682->sysclk < target) { + pr_err("sysclk rate %d is too low\n", + rt5682->sysclk); + return 0; + } + + for (i = 0; i < size - 1; i++) { + pr_info("div[%d]=%d\n", i, div[i]); + if (target * div[i] == rt5682->sysclk) + return i; + if (target * div[i + 1] > rt5682->sysclk) { + pr_err("can't find div for sysclk %d\n", + rt5682->sysclk); + return i; + } + } + + if (target * div[i] < rt5682->sysclk) + pr_err("sysclk rate %d is too high\n", + rt5682->sysclk); + + return size - 1; + +} + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int idx = -EINVAL; + static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; + + idx = rt5682_div_sel(rt5682, 1500000, div, ARRAY_SIZE(div)); + + snd_soc_component_update_bits(component, RT5682_DMIC_CTRL_1, + RT5682_DMIC_CLK_MASK, idx << RT5682_DMIC_CLK_SFT); + + return 0; +} + +static int set_filter_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int ref, val, reg, sft, mask, idx = -EINVAL; + static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48}; + + val = snd_soc_component_read32(component, RT5682_GPIO_CTRL_1) & + RT5682_GP4_PIN_MASK; + if (w->shift == RT5682_PWR_ADC_S1F_BIT && + val == RT5682_GP4_PIN_ADCDAT2) + ref = 256 * rt5682->lrck[RT5682_AIF2]; + else + ref = 256 * rt5682->lrck[RT5682_AIF1]; + + idx = rt5682_div_sel(rt5682, ref, div_f, ARRAY_SIZE(div_f)); + + if (w->shift == RT5682_PWR_ADC_S1F_BIT) { + reg = RT5682_PLL_TRACK_3; + sft = RT5682_ADC_OSR_SFT; + mask = RT5682_ADC_OSR_MASK; + } else { + reg = RT5682_PLL_TRACK_2; + sft = RT5682_DAC_OSR_SFT; + mask = RT5682_DAC_OSR_MASK; + } + + snd_soc_component_update_bits(component, reg, + RT5682_FILTER_CLK_DIV_MASK, idx << RT5682_FILTER_CLK_DIV_SFT); + + /* select over sample rate */ + for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) { + if (rt5682->sysclk <= 12288000 * div_o[idx]) + break; + } + + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1, + mask, idx << sft); + + return 0; +} + +static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read32(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5682_ADC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_3; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + case RT5682_DAC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_2; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read32(component, reg) >> shift) & 0xf; + switch (val) { + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5682_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5682_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5682_REC_MIXER, + RT5682_M_CBJ_RM1_L_SFT, 1, 1), +}; + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5682_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1L_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1R_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5682_sto1_adc_src[] = { + "ADC1 L", "ADC1 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcl_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCL_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcr_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCR_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5682_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2L_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2R_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682_sto1_adc2r_enum); + +/* MX-79 [6:4] I2S1 ADC data location */ +static const unsigned int rt5682_if1_adc_slot_values[] = { + 0, + 2, + 4, + 6, +}; + +static const char * const rt5682_if1_adc_slot_src[] = { + "Slot 0", "Slot 2", "Slot 4", "Slot 6" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_if1_adc_slot_enum, + RT5682_TDM_CTRL, RT5682_TDM_ADC_LCA_SFT, RT5682_TDM_ADC_LCA_MASK, + rt5682_if1_adc_slot_src, rt5682_if1_adc_slot_values); + +static const struct snd_kcontrol_new rt5682_if1_adc_slot_mux = + SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682_if1_adc_slot_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2B [4], MX-2B [0]*/ +static const char * const rt5682_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_l1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACL1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_r1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACR1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682_alg_dac_r1_enum); + +/* Out Switch */ +static const struct snd_kcontrol_new hpol_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_L_MUTE_SFT, 1, 1); +static const struct snd_kcontrol_new hpor_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_R_MUTE_SFT, 1, 1); + +static int rt5682_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, + RT5682_HP_LOGIC_CTRL_2, 0x0012); + snd_soc_component_write(component, + RT5682_HP_CTRL_2, 0x6000); + snd_soc_component_update_bits(component, RT5682_STO_NG2_CTRL_1, + RT5682_NG2_EN_MASK, RT5682_NG2_EN); + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x60); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x0); + snd_soc_component_write(component, + RT5682_HP_CTRL_2, 0x0000); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(150); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5655_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, 0); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, + RT5682_PWR_FV1); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, + RT5682_PWR_FV2); + break; + + default: + break; + } + break; + + default: + return 0; + } + + return 0; +} + +static const unsigned int rt5682_adcdat_pin_values[] = { + 1, + 3, +}; + +static const char * const rt5682_adcdat_pin_select[] = { + "ADCDAT1", + "ADCDAT2", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_adcdat_pin_enum, + RT5682_GPIO_CTRL_1, RT5682_GP4_PIN_SFT, RT5682_GP4_PIN_MASK, + rt5682_adcdat_pin_select, rt5682_adcdat_pin_values); + +static const struct snd_kcontrol_new rt5682_adcdat_pin_ctrl = + SOC_DAPM_ENUM("ADCDAT", rt5682_adcdat_pin_enum); + +static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5682_PWR_ANLG_3, RT5682_PWR_LDO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5682_PWR_ANLG_3, RT5682_PWR_PLL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2B_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2F_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5682_PWR_ANLG_1, RT5682_PWR_VREF2_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_AD_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DA_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DMIC_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682_PWR_ANLG_2, RT5682_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682_PWR_ANLG_2, RT5682_PWR_MB2_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682_rec1_l_mix, + ARRAY_SIZE(rt5682_rec1_l_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682_PWR_ANLG_2, + RT5682_PWR_RM1_L_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682_CHOP_ADC, + RT5682_CKGEN_ADC1_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcr_mux), + SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_adc_slot_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_ADC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, 1, rt5682_sto1_adc_l_mix, + ARRAY_SIZE(rt5682_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL, + RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix, + ARRAY_SIZE(rt5682_sto1_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5682_PWR_DIG_1, RT5682_PWR_I2S1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5682_PWR_DIG_1, RT5682_PWR_I2S2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if2_adc_swap_mux), + + SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, + &rt5682_adcdat_pin_ctrl), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5682_dac_l_mix, ARRAY_SIZE(rt5682_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5682_dac_r_mix, ARRAY_SIZE(rt5682_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_r1_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_DAC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_l_mix, ARRAY_SIZE(rt5682_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_r_mix, ARRAY_SIZE(rt5682_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_R1_BIT, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5682_CHOP_DAC, + RT5682_CKGEN_DAC1_SFT, 0, NULL, 0), + + /* HPO */ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("HP Amp L", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1, + RT5682_PUMP_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1, + RT5682_CAPLESS_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0, + &hpol_switch), + SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0, + &hpor_switch), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682_CLK_DET, + RT5682_SYS_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682_CLK_DET, + RT5682_PLL1_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5682_CLK_DET, + RT5682_PLL2_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5682_CLK_DET, + RT5682_POW_CLK_DET_SFT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + +}; + +static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"ADC STO1 ASRC", NULL, "CLKDET"}, + {"DAC STO1 ASRC", NULL, "DA ASRC"}, + {"DAC STO1 ASRC", NULL, "CLKDET"}, + + /*Vref*/ + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS1", NULL, "Vref2"}, + {"MICBIAS2", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref2"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + + {"IN1P", NULL, "LDO2"}, + + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "CBJ Power"}, + {"CBJ Power", NULL, "Vref2"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC CLK", NULL, "DMIC ASRC"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + + {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, + {"IF1_ADC Mux", NULL, "I2S1"}, + {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "ADCDAT Mux"}, + {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, + {"AIF2TX", NULL, "ADCDAT Mux"}, + + {"IF1 DAC1 L", NULL, "AIF1RX"}, + {"IF1 DAC1 L", NULL, "I2S1"}, + {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, + {"IF1 DAC1 R", NULL, "AIF1RX"}, + {"IF1 DAC1 R", NULL, "I2S1"}, + {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "HP Amp L"}, + {"HP Amp", NULL, "HP Amp R"}, + {"HP Amp", NULL, "Capless"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref2"}, + {"HPOL Playback", "Switch", "HP Amp"}, + {"HPOR Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPOL Playback"}, + {"HPOR", NULL, "HPOR Playback"}, +}; + +static int rt5682_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int cl, val = 0; + + if (tx_mask || rx_mask) + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, RT5682_TDM_EN); + else + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, 0); + + switch (slots) { + case 4: + val |= RT5682_TDM_TX_CH_4; + val |= RT5682_TDM_RX_CH_4; + break; + case 6: + val |= RT5682_TDM_TX_CH_6; + val |= RT5682_TDM_RX_CH_6; + break; + case 8: + val |= RT5682_TDM_TX_CH_8; + val |= RT5682_TDM_RX_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_CTRL, + RT5682_TDM_TX_CH_MASK | RT5682_TDM_RX_CH_MASK, val); + + switch (slot_width) { + case 8: + if (tx_mask || rx_mask) + return -EINVAL; + cl = RT5682_I2S1_TX_CHL_8 | RT5682_I2S1_RX_CHL_8; + break; + case 16: + val = RT5682_TDM_CL_16; + cl = RT5682_I2S1_TX_CHL_16 | RT5682_I2S1_RX_CHL_16; + break; + case 20: + val = RT5682_TDM_CL_20; + cl = RT5682_I2S1_TX_CHL_20 | RT5682_I2S1_RX_CHL_20; + break; + case 24: + val = RT5682_TDM_CL_24; + cl = RT5682_I2S1_TX_CHL_24 | RT5682_I2S1_RX_CHL_24; + break; + case 32: + val = RT5682_TDM_CL_32; + cl = RT5682_I2S1_TX_CHL_32 | RT5682_I2S1_RX_CHL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_CL_MASK, val); + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_TX_CHL_MASK | RT5682_I2S1_RX_CHL_MASK, cl); + + return 0; +} + + +static int rt5682_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 rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int len_1 = 0, len_2 = 0; + int pre_div, frame_size; + + rt5682->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5682->sysclk, rt5682->lrck[dai->id]); + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5682->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + len_1 |= RT5682_I2S1_DL_20; + len_2 |= RT5682_I2S2_DL_20; + break; + case 24: + len_1 |= RT5682_I2S1_DL_24; + len_2 |= RT5682_I2S2_DL_24; + break; + case 32: + len_1 |= RT5682_I2S1_DL_32; + len_2 |= RT5682_I2S2_DL_24; + break; + case 8: + len_1 |= RT5682_I2S2_DL_8; + len_2 |= RT5682_I2S2_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_DL_MASK, len_1); + if (rt5682->master[RT5682_AIF1]) { + snd_soc_component_update_bits(component, + RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK, + pre_div << RT5682_I2S_M_DIV_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_DIS); + break; + case RT5682_AIF2: + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_DL_MASK, len_2); + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_M_PD_MASK, + pre_div << RT5682_I2S2_M_PD_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_DIS); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, tdm_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5682->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rt5682->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5682_I2S_BP_INV; + tdm_ctrl |= RT5682_TDM_S_BP_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_LP_INV | RT5682_TDM_M_BP_INV; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_IB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_BP_INV | RT5682_TDM_S_LP_INV | + RT5682_TDM_M_BP_INV | RT5682_TDM_M_LP_INV; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5682_I2S_DF_LEFT; + tdm_ctrl |= RT5682_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5682_I2S_DF_PCM_A; + tdm_ctrl |= RT5682_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5682_I2S_DF_PCM_B; + tdm_ctrl |= RT5682_TDM_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_MS_MASK | RT5682_TDM_S_BP_MASK | + RT5682_TDM_DF_MASK | RT5682_TDM_M_BP_MASK | + RT5682_TDM_M_LP_MASK | RT5682_TDM_S_LP_MASK, + tdm_ctrl | rt5682->master[dai->id]); + break; + case RT5682_AIF2: + if (rt5682->master[dai->id] == 0) + reg_val |= RT5682_I2S2_MS_S; + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_MS_MASK | RT5682_I2S_BP_MASK | + RT5682_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5682_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, src = 0; + + if (freq == rt5682->sysclk && clk_id == rt5682->sysclk_src) + return 0; + + switch (clk_id) { + case RT5682_SCLK_S_MCLK: + reg_val |= RT5682_SCLK_SRC_MCLK; + src = RT5682_CLK_SRC_MCLK; + break; + case RT5682_SCLK_S_PLL1: + reg_val |= RT5682_SCLK_SRC_PLL1; + src = RT5682_CLK_SRC_PLL1; + break; + case RT5682_SCLK_S_PLL2: + reg_val |= RT5682_SCLK_SRC_PLL2; + src = RT5682_CLK_SRC_PLL2; + break; + case RT5682_SCLK_S_RCCLK: + reg_val |= RT5682_SCLK_SRC_RCCLK; + src = RT5682_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, reg_val); + + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_SRC_MASK, + src << RT5682_I2S2_SRC_SFT); + } + + rt5682->sysclk = freq; + rt5682->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5682_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5682->pll_src && freq_in == rt5682->pll_in && + freq_out == rt5682->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5682->pll_in = 0; + rt5682->pll_out = 0; + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, RT5682_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5682_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_MCLK); + break; + case RT5682_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5682_PLL_CTRL_1, + pll_code.n_code << RT5682_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5682_PLL_CTRL_2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT | + pll_code.m_bp << RT5682_PLL_M_BP_SFT | RT5682_PLL_RST); + + rt5682->pll_in = freq_in; + rt5682->pll_out = freq_out; + rt5682->pll_src = source; + + return 0; +} + +static int rt5682_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->bclk[dai->id] = ratio; + + switch (ratio) { + case 64: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_32); + break; + default: + dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB | RT5682_PWR_BG, + RT5682_PWR_MB | RT5682_PWR_BG); + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, RT5682_PWR_MB); + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL, RT5682_DIG_GATE_CTRL); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, 0); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB | RT5682_PWR_BG, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5682_probe(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->component = component; + + return 0; +} + +static void rt5682_remove(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682_reset(rt5682->regmap); +} + +#ifdef CONFIG_PM +static int rt5682_suspend(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + return 0; +} + +static int rt5682_resume(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5682->regmap, false); + regcache_sync(rt5682->regmap); + + return 0; +} +#else +#define rt5682_suspend NULL +#define rt5682_resume NULL +#endif + +#define RT5682_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5682_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5682_aif1_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_tdm_slot = rt5682_set_tdm_slot, +}; + +static const struct snd_soc_dai_ops rt5682_aif2_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_bclk_ratio = rt5682_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5682_dai[] = { + { + .name = "rt5682-aif1", + .id = RT5682_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif1_dai_ops, + }, + { + .name = "rt5682-aif2", + .id = RT5682_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif2_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5682 = { + .probe = rt5682_probe, + .remove = rt5682_remove, + .suspend = rt5682_suspend, + .resume = rt5682_resume, + .set_bias_level = rt5682_set_bias_level, + .controls = rt5682_snd_controls, + .num_controls = ARRAY_SIZE(rt5682_snd_controls), + .dapm_widgets = rt5682_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5682_dapm_widgets), + .dapm_routes = rt5682_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5682_dapm_routes), + .set_sysclk = rt5682_set_component_sysclk, + .set_pll = rt5682_set_component_pll, + .set_jack = rt5682_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config rt5682_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682_I2C_MODE, + .volatile_reg = rt5682_volatile_register, + .readable_reg = rt5682_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5682_reg, + .num_reg_defaults = ARRAY_SIZE(rt5682_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5682_i2c_id[] = { + {"rt5682", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id); + +static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) +{ + + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5682->pdata.dmic1_data_pin); + device_property_read_u32(dev, "realtek,dmic1-clk-pin", + &rt5682->pdata.dmic1_clk_pin); + device_property_read_u32(dev, "realtek,jd-src", + &rt5682->pdata.jd_src); + + rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, + "realtek,ldo1-en-gpios", 0); + + return 0; +} + +static void rt5682_calibrate(struct rt5682_priv *rt5682) +{ + int value, count; + + mutex_lock(&rt5682->calibrate_mutex); + + rt5682_reset(rt5682->regmap); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2bf); + usleep_range(15000, 20000); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2bf); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8001); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_write(rt5682->regmap, RT5682_STO1_DAC_MIXER, 0x2080); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x4040); + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0069); + regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); + regmap_write(rt5682->regmap, RT5682_HP_CTRL_2, 0x6000); + regmap_write(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, 0x0f26); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7f05); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_9, 0x000f); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8d01); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); + regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1); + regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311); + regmap_write(rt5682->regmap, RT5682_RESET_HPF_CTRL, 0x0000); + regmap_write(rt5682->regmap, RT5682_ADC_STO1_HP_CTRL_1, 0x3320); + + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00); + + for (count = 0; count < 60; count++) { + regmap_read(rt5682->regmap, RT5682_HP_CALIB_STA_1, &value); + if (!(value & 0x8000)) + break; + + usleep_range(10000, 10005); + } + + if (count >= 60) + pr_err("HP Calibration Failure\n"); + + /* restore settings */ + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); + + mutex_unlock(&rt5682->calibrate_mutex); + +} + +static int rt5682_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5682_priv *rt5682; + int i, ret; + unsigned int val; + + rt5682 = devm_kzalloc(&i2c->dev, sizeof(struct rt5682_priv), + GFP_KERNEL); + + if (rt5682 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5682); + + if (pdata) + rt5682->pdata = *pdata; + else + rt5682_parse_dt(rt5682, &i2c->dev); + + rt5682->regmap = devm_regmap_init_i2c(i2c, &rt5682_regmap); + if (IS_ERR(rt5682->regmap)) { + ret = PTR_ERR(rt5682->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5682->supplies); i++) + rt5682->supplies[i].supply = rt5682_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + if (gpio_is_valid(rt5682->pdata.ldo1_en)) { + if (devm_gpio_request_one(&i2c->dev, rt5682->pdata.ldo1_en, + GPIOF_OUT_INIT_HIGH, "rt5682")) + dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + } + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1); + usleep_range(10000, 15000); + + regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); + if (val != DEVICE_ID) { + pr_err("Device with ID register %x is not rt5682\n", val); + return -ENODEV; + } + + rt5682_reset(rt5682->regmap); + + rt5682_calibrate(rt5682); + + ret = regmap_register_patch(rt5682->regmap, patch_list, + ARRAY_SIZE(patch_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000); + + /* DMIC pin*/ + if (rt5682->pdata.dmic1_data_pin != RT5682_DMIC1_NULL) { + switch (rt5682->pdata.dmic1_data_pin) { + case RT5682_DMIC1_DATA_GPIO2: /* share with LRCK2 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO2); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP2_PIN_MASK, RT5682_GP2_PIN_DMIC_SDA); + break; + + case RT5682_DMIC1_DATA_GPIO5: /* share with DACDAT1 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP5_PIN_MASK, RT5682_GP5_PIN_DMIC_SDA); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n"); + break; + } + + switch (rt5682->pdata.dmic1_clk_pin) { + case RT5682_DMIC1_CLK_GPIO1: /* share with IRQ */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_DMIC_CLK); + break; + + case RT5682_DMIC1_CLK_GPIO3: /* share with BCLK2 */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP3_PIN_MASK, RT5682_GP3_PIN_DMIC_CLK); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n"); + break; + } + } + + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK, + RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK, + RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + + INIT_DELAYED_WORK(&rt5682->jack_detect_work, + rt5682_jack_detect_handler); + INIT_DELAYED_WORK(&rt5682->jd_check_work, + rt5682_jd_check_handler); + + mutex_init(&rt5682->calibrate_mutex); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5682_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5682", rt5682); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + } + + return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5682, + rt5682_dai, ARRAY_SIZE(rt5682_dai)); +} + +static int rt5682_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_component(&i2c->dev); + + return 0; +} + +static void rt5682_i2c_shutdown(struct i2c_client *client) +{ + struct rt5682_priv *rt5682 = i2c_get_clientdata(client); + + rt5682_reset(rt5682->regmap); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5682_of_match[] = { + {.compatible = "realtek,rt5682i"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5682_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5682_acpi_match[] = { + {"10EC5682", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match); +#endif + +static struct i2c_driver rt5682_i2c_driver = { + .driver = { + .name = "rt5682", + .of_match_table = of_match_ptr(rt5682_of_match), + .acpi_match_table = ACPI_PTR(rt5682_acpi_match), + }, + .probe = rt5682_i2c_probe, + .remove = rt5682_i2c_remove, + .shutdown = rt5682_i2c_shutdown, + .id_table = rt5682_i2c_id, +}; +module_i2c_driver(rt5682_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5682 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h new file mode 100644 index 00000000000000..8068140ebe3f19 --- /dev/null +++ b/sound/soc/codecs/rt5682.h @@ -0,0 +1,1324 @@ +/* + * rt5682.h -- RT5682/RT5658 ALSA SoC audio driver + * + * Copyright 2018 Realtek Microelectronics + * Author: Bard Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT5682_H__ +#define __RT5682_H__ + +#include + +#define DEVICE_ID 0x6530 + +/* Info */ +#define RT5682_RESET 0x0000 +#define RT5682_VERSION_ID 0x00fd +#define RT5682_VENDOR_ID 0x00fe +#define RT5682_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5682_HP_CTRL_1 0x0002 +#define RT5682_HP_CTRL_2 0x0003 +#define RT5682_HPL_GAIN 0x0005 +#define RT5682_HPR_GAIN 0x0006 + +#define RT5682_I2C_CTRL 0x0008 + +/* I/O - Input */ +#define RT5682_CBJ_BST_CTRL 0x000b +#define RT5682_CBJ_CTRL_1 0x0010 +#define RT5682_CBJ_CTRL_2 0x0011 +#define RT5682_CBJ_CTRL_3 0x0012 +#define RT5682_CBJ_CTRL_4 0x0013 +#define RT5682_CBJ_CTRL_5 0x0014 +#define RT5682_CBJ_CTRL_6 0x0015 +#define RT5682_CBJ_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5682_DAC1_DIG_VOL 0x0019 +#define RT5682_STO1_ADC_DIG_VOL 0x001c +#define RT5682_STO1_ADC_BOOST 0x001f +#define RT5682_HP_IMP_GAIN_1 0x0022 +#define RT5682_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5682_SIDETONE_CTRL 0x0024 +#define RT5682_STO1_ADC_MIXER 0x0026 +#define RT5682_AD_DA_MIXER 0x0029 +#define RT5682_STO1_DAC_MIXER 0x002a +#define RT5682_A_DAC1_MUX 0x002b +#define RT5682_DIG_INF2_DATA 0x0030 +/* Mixer - ADC */ +#define RT5682_REC_MIXER 0x003c +#define RT5682_CAL_REC 0x0044 +#define RT5682_ALC_BACK_GAIN 0x0049 +/* Power */ +#define RT5682_PWR_DIG_1 0x0061 +#define RT5682_PWR_DIG_2 0x0062 +#define RT5682_PWR_ANLG_1 0x0063 +#define RT5682_PWR_ANLG_2 0x0064 +#define RT5682_PWR_ANLG_3 0x0065 +#define RT5682_PWR_MIXER 0x0066 +#define RT5682_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5682_CLK_DET 0x006b +/* Filter Auto Reset */ +#define RT5682_RESET_LPF_CTRL 0x006c +#define RT5682_RESET_HPF_CTRL 0x006d +/* DMIC */ +#define RT5682_DMIC_CTRL_1 0x006e +/* Format - ADC/DAC */ +#define RT5682_I2S1_SDP 0x0070 +#define RT5682_I2S2_SDP 0x0071 +#define RT5682_ADDA_CLK_1 0x0073 +#define RT5682_ADDA_CLK_2 0x0074 +#define RT5682_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5682_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5682_TDM_CTRL 0x0079 +#define RT5682_TDM_ADDA_CTRL_1 0x007a +#define RT5682_TDM_ADDA_CTRL_2 0x007b +#define RT5682_DATA_SEL_CTRL_1 0x007c +#define RT5682_TDM_TCON_CTRL 0x007e +/* Function - Analog */ +#define RT5682_GLB_CLK 0x0080 +#define RT5682_PLL_CTRL_1 0x0081 +#define RT5682_PLL_CTRL_2 0x0082 +#define RT5682_PLL_TRACK_1 0x0083 +#define RT5682_PLL_TRACK_2 0x0084 +#define RT5682_PLL_TRACK_3 0x0085 +#define RT5682_PLL_TRACK_4 0x0086 +#define RT5682_PLL_TRACK_5 0x0087 +#define RT5682_PLL_TRACK_6 0x0088 +#define RT5682_PLL_TRACK_11 0x008c +#define RT5682_SDW_REF_CLK 0x008d +#define RT5682_DEPOP_1 0x008e +#define RT5682_DEPOP_2 0x008f +#define RT5682_HP_CHARGE_PUMP_1 0x0091 +#define RT5682_HP_CHARGE_PUMP_2 0x0092 +#define RT5682_MICBIAS_1 0x0093 +#define RT5682_MICBIAS_2 0x0094 +#define RT5682_PLL_TRACK_12 0x0098 +#define RT5682_PLL_TRACK_14 0x009a +#define RT5682_PLL2_CTRL_1 0x009b +#define RT5682_PLL2_CTRL_2 0x009c +#define RT5682_PLL2_CTRL_3 0x009d +#define RT5682_PLL2_CTRL_4 0x009e +#define RT5682_RC_CLK_CTRL 0x009f +#define RT5682_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5682_I2S2_F_DIV_CTRL_1 0x00a3 +#define RT5682_I2S2_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5682_EQ_CTRL_1 0x00ae +#define RT5682_EQ_CTRL_2 0x00af +#define RT5682_IRQ_CTRL_1 0x00b6 +#define RT5682_IRQ_CTRL_2 0x00b7 +#define RT5682_IRQ_CTRL_3 0x00b8 +#define RT5682_IRQ_CTRL_4 0x00b9 +#define RT5682_INT_ST_1 0x00be +#define RT5682_GPIO_CTRL_1 0x00c0 +#define RT5682_GPIO_CTRL_2 0x00c1 +#define RT5682_GPIO_CTRL_3 0x00c2 +#define RT5682_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5682_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5682_MID_HP_AMP_DET 0x00d2 +#define RT5682_LOW_HP_AMP_DET 0x00d3 +#define RT5682_DELAY_BUF_CTRL 0x00d4 +#define RT5682_SV_ZCD_1 0x00d9 +#define RT5682_SV_ZCD_2 0x00da +#define RT5682_IL_CMD_1 0x00db +#define RT5682_IL_CMD_2 0x00dc +#define RT5682_IL_CMD_3 0x00dd +#define RT5682_IL_CMD_4 0x00de +#define RT5682_IL_CMD_5 0x00df +#define RT5682_IL_CMD_6 0x00e0 +#define RT5682_4BTN_IL_CMD_1 0x00e2 +#define RT5682_4BTN_IL_CMD_2 0x00e3 +#define RT5682_4BTN_IL_CMD_3 0x00e4 +#define RT5682_4BTN_IL_CMD_4 0x00e5 +#define RT5682_4BTN_IL_CMD_5 0x00e6 +#define RT5682_4BTN_IL_CMD_6 0x00e7 +#define RT5682_4BTN_IL_CMD_7 0x00e8 + +#define RT5682_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5682_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5682_AJD1_CTRL 0x00f0 +#define RT5682_JD1_THD 0x00f1 +#define RT5682_JD2_THD 0x00f2 +#define RT5682_JD_CTRL_1 0x00f6 +/* General Control */ +#define RT5682_DUMMY_1 0x00fa +#define RT5682_DUMMY_2 0x00fb +#define RT5682_DUMMY_3 0x00fc + +#define RT5682_DAC_ADC_DIG_VOL1 0x0100 +#define RT5682_BIAS_CUR_CTRL_2 0x010b +#define RT5682_BIAS_CUR_CTRL_3 0x010c +#define RT5682_BIAS_CUR_CTRL_4 0x010d +#define RT5682_BIAS_CUR_CTRL_5 0x010e +#define RT5682_BIAS_CUR_CTRL_6 0x010f +#define RT5682_BIAS_CUR_CTRL_7 0x0110 +#define RT5682_BIAS_CUR_CTRL_8 0x0111 +#define RT5682_BIAS_CUR_CTRL_9 0x0112 +#define RT5682_BIAS_CUR_CTRL_10 0x0113 +#define RT5682_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5682_CHARGE_PUMP_1 0x0125 +#define RT5682_DIG_IN_CTRL_1 0x0132 +#define RT5682_PAD_DRIVING_CTRL 0x0136 +#define RT5682_SOFT_RAMP_DEPOP 0x0138 +#define RT5682_CHOP_DAC 0x013a +#define RT5682_CHOP_ADC 0x013b +#define RT5682_CALIB_ADC_CTRL 0x013c +#define RT5682_VOL_TEST 0x013f +#define RT5682_SPKVDD_DET_STA 0x0142 +#define RT5682_TEST_MODE_CTRL_1 0x0145 +#define RT5682_TEST_MODE_CTRL_2 0x0146 +#define RT5682_TEST_MODE_CTRL_3 0x0147 +#define RT5682_TEST_MODE_CTRL_4 0x0148 +#define RT5682_TEST_MODE_CTRL_5 0x0149 +#define RT5682_PLL1_INTERNAL 0x0150 +#define RT5682_PLL2_INTERNAL 0x0151 +#define RT5682_STO_NG2_CTRL_1 0x0160 +#define RT5682_STO_NG2_CTRL_2 0x0161 +#define RT5682_STO_NG2_CTRL_3 0x0162 +#define RT5682_STO_NG2_CTRL_4 0x0163 +#define RT5682_STO_NG2_CTRL_5 0x0164 +#define RT5682_STO_NG2_CTRL_6 0x0165 +#define RT5682_STO_NG2_CTRL_7 0x0166 +#define RT5682_STO_NG2_CTRL_8 0x0167 +#define RT5682_STO_NG2_CTRL_9 0x0168 +#define RT5682_STO_NG2_CTRL_10 0x0169 +#define RT5682_STO1_DAC_SIL_DET 0x0190 +#define RT5682_SIL_PSV_CTRL1 0x0194 +#define RT5682_SIL_PSV_CTRL2 0x0195 +#define RT5682_SIL_PSV_CTRL3 0x0197 +#define RT5682_SIL_PSV_CTRL4 0x0198 +#define RT5682_SIL_PSV_CTRL5 0x0199 +#define RT5682_HP_IMP_SENS_CTRL_01 0x01af +#define RT5682_HP_IMP_SENS_CTRL_02 0x01b0 +#define RT5682_HP_IMP_SENS_CTRL_03 0x01b1 +#define RT5682_HP_IMP_SENS_CTRL_04 0x01b2 +#define RT5682_HP_IMP_SENS_CTRL_05 0x01b3 +#define RT5682_HP_IMP_SENS_CTRL_06 0x01b4 +#define RT5682_HP_IMP_SENS_CTRL_07 0x01b5 +#define RT5682_HP_IMP_SENS_CTRL_08 0x01b6 +#define RT5682_HP_IMP_SENS_CTRL_09 0x01b7 +#define RT5682_HP_IMP_SENS_CTRL_10 0x01b8 +#define RT5682_HP_IMP_SENS_CTRL_11 0x01b9 +#define RT5682_HP_IMP_SENS_CTRL_12 0x01ba +#define RT5682_HP_IMP_SENS_CTRL_13 0x01bb +#define RT5682_HP_IMP_SENS_CTRL_14 0x01bc +#define RT5682_HP_IMP_SENS_CTRL_15 0x01bd +#define RT5682_HP_IMP_SENS_CTRL_16 0x01be +#define RT5682_HP_IMP_SENS_CTRL_17 0x01bf +#define RT5682_HP_IMP_SENS_CTRL_18 0x01c0 +#define RT5682_HP_IMP_SENS_CTRL_19 0x01c1 +#define RT5682_HP_IMP_SENS_CTRL_20 0x01c2 +#define RT5682_HP_IMP_SENS_CTRL_21 0x01c3 +#define RT5682_HP_IMP_SENS_CTRL_22 0x01c4 +#define RT5682_HP_IMP_SENS_CTRL_23 0x01c5 +#define RT5682_HP_IMP_SENS_CTRL_24 0x01c6 +#define RT5682_HP_IMP_SENS_CTRL_25 0x01c7 +#define RT5682_HP_IMP_SENS_CTRL_26 0x01c8 +#define RT5682_HP_IMP_SENS_CTRL_27 0x01c9 +#define RT5682_HP_IMP_SENS_CTRL_28 0x01ca +#define RT5682_HP_IMP_SENS_CTRL_29 0x01cb +#define RT5682_HP_IMP_SENS_CTRL_30 0x01cc +#define RT5682_HP_IMP_SENS_CTRL_31 0x01cd +#define RT5682_HP_IMP_SENS_CTRL_32 0x01ce +#define RT5682_HP_IMP_SENS_CTRL_33 0x01cf +#define RT5682_HP_IMP_SENS_CTRL_34 0x01d0 +#define RT5682_HP_IMP_SENS_CTRL_35 0x01d1 +#define RT5682_HP_IMP_SENS_CTRL_36 0x01d2 +#define RT5682_HP_IMP_SENS_CTRL_37 0x01d3 +#define RT5682_HP_IMP_SENS_CTRL_38 0x01d4 +#define RT5682_HP_IMP_SENS_CTRL_39 0x01d5 +#define RT5682_HP_IMP_SENS_CTRL_40 0x01d6 +#define RT5682_HP_IMP_SENS_CTRL_41 0x01d7 +#define RT5682_HP_IMP_SENS_CTRL_42 0x01d8 +#define RT5682_HP_IMP_SENS_CTRL_43 0x01d9 +#define RT5682_HP_LOGIC_CTRL_1 0x01da +#define RT5682_HP_LOGIC_CTRL_2 0x01db +#define RT5682_HP_LOGIC_CTRL_3 0x01dc +#define RT5682_HP_CALIB_CTRL_1 0x01de +#define RT5682_HP_CALIB_CTRL_2 0x01df +#define RT5682_HP_CALIB_CTRL_3 0x01e0 +#define RT5682_HP_CALIB_CTRL_4 0x01e1 +#define RT5682_HP_CALIB_CTRL_5 0x01e2 +#define RT5682_HP_CALIB_CTRL_6 0x01e3 +#define RT5682_HP_CALIB_CTRL_7 0x01e4 +#define RT5682_HP_CALIB_CTRL_9 0x01e6 +#define RT5682_HP_CALIB_CTRL_10 0x01e7 +#define RT5682_HP_CALIB_CTRL_11 0x01e8 +#define RT5682_HP_CALIB_STA_1 0x01ea +#define RT5682_HP_CALIB_STA_2 0x01eb +#define RT5682_HP_CALIB_STA_3 0x01ec +#define RT5682_HP_CALIB_STA_4 0x01ed +#define RT5682_HP_CALIB_STA_5 0x01ee +#define RT5682_HP_CALIB_STA_6 0x01ef +#define RT5682_HP_CALIB_STA_7 0x01f0 +#define RT5682_HP_CALIB_STA_8 0x01f1 +#define RT5682_HP_CALIB_STA_9 0x01f2 +#define RT5682_HP_CALIB_STA_10 0x01f3 +#define RT5682_HP_CALIB_STA_11 0x01f4 +#define RT5682_SAR_IL_CMD_1 0x0210 +#define RT5682_SAR_IL_CMD_2 0x0211 +#define RT5682_SAR_IL_CMD_3 0x0212 +#define RT5682_SAR_IL_CMD_4 0x0213 +#define RT5682_SAR_IL_CMD_5 0x0214 +#define RT5682_SAR_IL_CMD_6 0x0215 +#define RT5682_SAR_IL_CMD_7 0x0216 +#define RT5682_SAR_IL_CMD_8 0x0217 +#define RT5682_SAR_IL_CMD_9 0x0218 +#define RT5682_SAR_IL_CMD_10 0x0219 +#define RT5682_SAR_IL_CMD_11 0x021a +#define RT5682_SAR_IL_CMD_12 0x021b +#define RT5682_SAR_IL_CMD_13 0x021c +#define RT5682_EFUSE_CTRL_1 0x0250 +#define RT5682_EFUSE_CTRL_2 0x0251 +#define RT5682_EFUSE_CTRL_3 0x0252 +#define RT5682_EFUSE_CTRL_4 0x0253 +#define RT5682_EFUSE_CTRL_5 0x0254 +#define RT5682_EFUSE_CTRL_6 0x0255 +#define RT5682_EFUSE_CTRL_7 0x0256 +#define RT5682_EFUSE_CTRL_8 0x0257 +#define RT5682_EFUSE_CTRL_9 0x0258 +#define RT5682_EFUSE_CTRL_10 0x0259 +#define RT5682_EFUSE_CTRL_11 0x025a +#define RT5682_JD_TOP_VC_VTRL 0x0270 +#define RT5682_DRC1_CTRL_0 0x02ff +#define RT5682_DRC1_CTRL_1 0x0300 +#define RT5682_DRC1_CTRL_2 0x0301 +#define RT5682_DRC1_CTRL_3 0x0302 +#define RT5682_DRC1_CTRL_4 0x0303 +#define RT5682_DRC1_CTRL_5 0x0304 +#define RT5682_DRC1_CTRL_6 0x0305 +#define RT5682_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5682_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5682_DRC1_PRIV_1 0x0310 +#define RT5682_DRC1_PRIV_2 0x0311 +#define RT5682_DRC1_PRIV_3 0x0312 +#define RT5682_DRC1_PRIV_4 0x0313 +#define RT5682_DRC1_PRIV_5 0x0314 +#define RT5682_DRC1_PRIV_6 0x0315 +#define RT5682_DRC1_PRIV_7 0x0316 +#define RT5682_DRC1_PRIV_8 0x0317 +#define RT5682_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5682_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5682_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5682_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5682_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5682_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5682_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5682_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5682_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5682_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5682_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5682_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5682_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5682_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5682_R_EQ_LPF1_A1 0x03d1 +#define RT5682_L_EQ_LPF1_H0 0x03d2 +#define RT5682_R_EQ_LPF1_H0 0x03d3 +#define RT5682_L_EQ_BPF1_A1 0x03d4 +#define RT5682_R_EQ_BPF1_A1 0x03d5 +#define RT5682_L_EQ_BPF1_A2 0x03d6 +#define RT5682_R_EQ_BPF1_A2 0x03d7 +#define RT5682_L_EQ_BPF1_H0 0x03d8 +#define RT5682_R_EQ_BPF1_H0 0x03d9 +#define RT5682_L_EQ_BPF2_A1 0x03da +#define RT5682_R_EQ_BPF2_A1 0x03db +#define RT5682_L_EQ_BPF2_A2 0x03dc +#define RT5682_R_EQ_BPF2_A2 0x03dd +#define RT5682_L_EQ_BPF2_H0 0x03de +#define RT5682_R_EQ_BPF2_H0 0x03df +#define RT5682_L_EQ_BPF3_A1 0x03e0 +#define RT5682_R_EQ_BPF3_A1 0x03e1 +#define RT5682_L_EQ_BPF3_A2 0x03e2 +#define RT5682_R_EQ_BPF3_A2 0x03e3 +#define RT5682_L_EQ_BPF3_H0 0x03e4 +#define RT5682_R_EQ_BPF3_H0 0x03e5 +#define RT5682_L_EQ_BPF4_A1 0x03e6 +#define RT5682_R_EQ_BPF4_A1 0x03e7 +#define RT5682_L_EQ_BPF4_A2 0x03e8 +#define RT5682_R_EQ_BPF4_A2 0x03e9 +#define RT5682_L_EQ_BPF4_H0 0x03ea +#define RT5682_R_EQ_BPF4_H0 0x03eb +#define RT5682_L_EQ_HPF1_A1 0x03ec +#define RT5682_R_EQ_HPF1_A1 0x03ed +#define RT5682_L_EQ_HPF1_H0 0x03ee +#define RT5682_R_EQ_HPF1_H0 0x03ef +#define RT5682_L_EQ_PRE_VOL 0x03f0 +#define RT5682_R_EQ_PRE_VOL 0x03f1 +#define RT5682_L_EQ_POST_VOL 0x03f2 +#define RT5682_R_EQ_POST_VOL 0x03f3 +#define RT5682_I2C_MODE 0xffff + + +/* global definition */ +#define RT5682_L_MUTE (0x1 << 15) +#define RT5682_L_MUTE_SFT 15 +#define RT5682_VOL_L_MUTE (0x1 << 14) +#define RT5682_VOL_L_SFT 14 +#define RT5682_R_MUTE (0x1 << 7) +#define RT5682_R_MUTE_SFT 7 +#define RT5682_VOL_R_MUTE (0x1 << 6) +#define RT5682_VOL_R_SFT 6 +#define RT5682_L_VOL_MASK (0x3f << 8) +#define RT5682_L_VOL_SFT 8 +#define RT5682_R_VOL_MASK (0x3f) +#define RT5682_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5682_G_HP (0xf << 8) +#define RT5682_G_HP_SFT 8 +#define RT5682_G_STO_DA_DMIX (0xf) +#define RT5682_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5682_BST_CBJ_MASK (0xf << 8) +#define RT5682_BST_CBJ_SFT 8 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5682_EMB_JD_EN (0x1 << 15) +#define RT5682_EMB_JD_EN_SFT 15 +#define RT5682_EMB_JD_RST (0x1 << 14) +#define RT5682_JD_MODE (0x1 << 13) +#define RT5682_JD_MODE_SFT 13 +#define RT5682_DET_TYPE (0x1 << 12) +#define RT5682_DET_TYPE_SFT 12 +#define RT5682_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5682_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5682_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5682_EXT_JD_DIG (0x1 << 9) +#define RT5682_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5682_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5682_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5682_FAST_OFF_MASK (0x1 << 7) +#define RT5682_FAST_OFF_EN (0x1 << 7) +#define RT5682_FAST_OFF_DIS (0x0 << 7) +#define RT5682_VREF_POW_MASK (0x1 << 6) +#define RT5682_VREF_POW_FSM (0x0 << 6) +#define RT5682_VREF_POW_REG (0x1 << 6) +#define RT5682_MB1_PATH_MASK (0x1 << 5) +#define RT5682_CTRL_MB1_REG (0x1 << 5) +#define RT5682_CTRL_MB1_FSM (0x0 << 5) +#define RT5682_MB2_PATH_MASK (0x1 << 4) +#define RT5682_CTRL_MB2_REG (0x1 << 4) +#define RT5682_CTRL_MB2_FSM (0x0 << 4) +#define RT5682_TRIG_JD_MASK (0x1 << 3) +#define RT5682_TRIG_JD_HIGH (0x1 << 3) +#define RT5682_TRIG_JD_LOW (0x0 << 3) +#define RT5682_MIC_CAP_MASK (0x1 << 1) +#define RT5682_MIC_CAP_HS (0x1 << 1) +#define RT5682_MIC_CAP_HP (0x0 << 1) +#define RT5682_MIC_CAP_SRC_MASK (0x1) +#define RT5682_MIC_CAP_SRC_REG (0x1) +#define RT5682_MIC_CAP_SRC_ANA (0x0) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5682_EXT_JD_SRC (0x7 << 4) +#define RT5682_EXT_JD_SRC_SFT 4 +#define RT5682_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5682_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5682_EXT_JD_SRC_JDH (0x2 << 4) +#define RT5682_EXT_JD_SRC_JDL (0x3 << 4) +#define RT5682_EXT_JD_SRC_MANUAL (0x4 << 4) +#define RT5682_JACK_TYPE_MASK (0x3) + +/* Combo Jack and Type Detection Control 3 (0x0012) */ +#define RT5682_CBJ_IN_BUF_EN (0x1 << 7) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5682_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5682_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5682_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5682_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5682_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5682_CBJ_JD_TEST_MODE (0x1 << 6) + +/* DAC1 Digital Volume (0x0019) */ +#define RT5682_DAC_L1_VOL_MASK (0xff << 8) +#define RT5682_DAC_L1_VOL_SFT 8 +#define RT5682_DAC_R1_VOL_MASK (0xff) +#define RT5682_DAC_R1_VOL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5682_ADC_L_VOL_MASK (0x7f << 8) +#define RT5682_ADC_L_VOL_SFT 8 +#define RT5682_ADC_R_VOL_MASK (0x7f) +#define RT5682_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5682_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5682_STO1_ADC_L_BST_SFT 14 +#define RT5682_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5682_STO1_ADC_R_BST_SFT 12 + +/* Sidetone Control (0x0024) */ +#define RT5682_ST_SRC_SEL (0x1 << 8) +#define RT5682_ST_SRC_SFT 8 +#define RT5682_ST_EN_MASK (0x1 << 6) +#define RT5682_ST_DIS (0x0 << 6) +#define RT5682_ST_EN (0x1 << 6) +#define RT5682_ST_EN_SFT 6 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5682_M_STO1_ADC_L1 (0x1 << 15) +#define RT5682_M_STO1_ADC_L1_SFT 15 +#define RT5682_M_STO1_ADC_L2 (0x1 << 14) +#define RT5682_M_STO1_ADC_L2_SFT 14 +#define RT5682_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5682_STO1_ADC1L_SRC_SFT 13 +#define RT5682_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5682_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5682_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5682_STO1_ADC2L_SRC_SFT 12 +#define RT5682_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5682_STO1_ADCL_SRC_SFT 10 +#define RT5682_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5682_STO1_DD_L_SRC_SFT 9 +#define RT5682_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_SFT 8 +#define RT5682_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5682_M_STO1_ADC_R1 (0x1 << 7) +#define RT5682_M_STO1_ADC_R1_SFT 7 +#define RT5682_M_STO1_ADC_R2 (0x1 << 6) +#define RT5682_M_STO1_ADC_R2_SFT 6 +#define RT5682_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5682_STO1_ADC1R_SRC_SFT 5 +#define RT5682_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5682_STO1_ADC2R_SRC_SFT 4 +#define RT5682_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5682_STO1_ADCR_SRC_SFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5682_M_ADCMIX_L (0x1 << 15) +#define RT5682_M_ADCMIX_L_SFT 15 +#define RT5682_M_DAC1_L (0x1 << 14) +#define RT5682_M_DAC1_L_SFT 14 +#define RT5682_DAC1_R_SEL_MASK (0x1 << 10) +#define RT5682_DAC1_R_SEL_SFT 10 +#define RT5682_DAC1_L_SEL_MASK (0x1 << 8) +#define RT5682_DAC1_L_SEL_SFT 8 +#define RT5682_M_ADCMIX_R (0x1 << 7) +#define RT5682_M_ADCMIX_R_SFT 7 +#define RT5682_M_DAC1_R (0x1 << 6) +#define RT5682_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5682_M_DAC_L1_STO_L (0x1 << 15) +#define RT5682_M_DAC_L1_STO_L_SFT 15 +#define RT5682_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5682_G_DAC_L1_STO_L_SFT 14 +#define RT5682_M_DAC_R1_STO_L (0x1 << 13) +#define RT5682_M_DAC_R1_STO_L_SFT 13 +#define RT5682_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5682_G_DAC_R1_STO_L_SFT 12 +#define RT5682_M_DAC_L1_STO_R (0x1 << 7) +#define RT5682_M_DAC_L1_STO_R_SFT 7 +#define RT5682_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5682_G_DAC_L1_STO_R_SFT 6 +#define RT5682_M_DAC_R1_STO_R (0x1 << 5) +#define RT5682_M_DAC_R1_STO_R_SFT 5 +#define RT5682_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5682_G_DAC_R1_STO_R_SFT 4 + +/* Analog DAC1 Input Source Control (0x002b) */ +#define RT5682_M_ST_STO_L (0x1 << 9) +#define RT5682_M_ST_STO_L_SFT 9 +#define RT5682_M_ST_STO_R (0x1 << 8) +#define RT5682_M_ST_STO_R_SFT 8 +#define RT5682_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5682_A_DACL1_SFT 4 +#define RT5682_DAC_R1_SRC_MASK (0x3) +#define RT5682_A_DACR1_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5682_IF2_ADC_SEL_MASK (0x3 << 0) +#define RT5682_IF2_ADC_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5682_G_CBJ_RM1_L (0x7 << 10) +#define RT5682_G_CBJ_RM1_L_SFT 10 +#define RT5682_M_CBJ_RM1_L (0x1 << 7) +#define RT5682_M_CBJ_RM1_L_SFT 7 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5682_PWR_I2S1 (0x1 << 15) +#define RT5682_PWR_I2S1_BIT 15 +#define RT5682_PWR_I2S2 (0x1 << 14) +#define RT5682_PWR_I2S2_BIT 14 +#define RT5682_PWR_DAC_L1 (0x1 << 11) +#define RT5682_PWR_DAC_L1_BIT 11 +#define RT5682_PWR_DAC_R1 (0x1 << 10) +#define RT5682_PWR_DAC_R1_BIT 10 +#define RT5682_PWR_LDO (0x1 << 8) +#define RT5682_PWR_LDO_BIT 8 +#define RT5682_PWR_ADC_L1 (0x1 << 4) +#define RT5682_PWR_ADC_L1_BIT 4 +#define RT5682_PWR_ADC_R1 (0x1 << 3) +#define RT5682_PWR_ADC_R1_BIT 3 +#define RT5682_DIG_GATE_CTRL (0x1 << 0) +#define RT5682_DIG_GATE_CTRL_SFT 0 + + +/* Power Management for Digital 2 (0x0062) */ +#define RT5682_PWR_ADC_S1F (0x1 << 15) +#define RT5682_PWR_ADC_S1F_BIT 15 +#define RT5682_PWR_DAC_S1F (0x1 << 10) +#define RT5682_PWR_DAC_S1F_BIT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5682_PWR_VREF1 (0x1 << 15) +#define RT5682_PWR_VREF1_BIT 15 +#define RT5682_PWR_FV1 (0x1 << 14) +#define RT5682_PWR_FV1_BIT 14 +#define RT5682_PWR_VREF2 (0x1 << 13) +#define RT5682_PWR_VREF2_BIT 13 +#define RT5682_PWR_FV2 (0x1 << 12) +#define RT5682_PWR_FV2_BIT 12 +#define RT5682_LDO1_DBG_MASK (0x3 << 10) +#define RT5682_PWR_MB (0x1 << 9) +#define RT5682_PWR_MB_BIT 9 +#define RT5682_PWR_BG (0x1 << 7) +#define RT5682_PWR_BG_BIT 7 +#define RT5682_LDO1_BYPASS_MASK (0x1 << 6) +#define RT5682_LDO1_BYPASS (0x1 << 6) +#define RT5682_LDO1_NOT_BYPASS (0x0 << 6) +#define RT5682_PWR_MA_BIT 6 +#define RT5682_LDO1_DVO_MASK (0x3 << 4) +#define RT5682_LDO1_DVO_09 (0x0 << 4) +#define RT5682_LDO1_DVO_10 (0x1 << 4) +#define RT5682_LDO1_DVO_12 (0x2 << 4) +#define RT5682_LDO1_DVO_14 (0x3 << 4) +#define RT5682_HP_DRIVER_MASK (0x3 << 2) +#define RT5682_HP_DRIVER_1X (0x0 << 2) +#define RT5682_HP_DRIVER_3X (0x1 << 2) +#define RT5682_HP_DRIVER_5X (0x3 << 2) +#define RT5682_PWR_HA_L (0x1 << 1) +#define RT5682_PWR_HA_L_BIT 1 +#define RT5682_PWR_HA_R (0x1 << 0) +#define RT5682_PWR_HA_R_BIT 0 + +/* Power Management for Analog 2 (0x0064) */ +#define RT5682_PWR_MB1 (0x1 << 11) +#define RT5682_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5682_PWR_MB1_BIT 11 +#define RT5682_PWR_MB2 (0x1 << 10) +#define RT5682_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5682_PWR_MB2_BIT 10 +#define RT5682_PWR_JDH (0x1 << 3) +#define RT5682_PWR_JDH_BIT 3 +#define RT5682_PWR_JDL (0x1 << 2) +#define RT5682_PWR_JDL_BIT 2 +#define RT5682_PWR_RM1_L (0x1 << 1) +#define RT5682_PWR_RM1_L_BIT 1 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5682_PWR_CBJ (0x1 << 9) +#define RT5682_PWR_CBJ_BIT 9 +#define RT5682_PWR_PLL (0x1 << 6) +#define RT5682_PWR_PLL_BIT 6 +#define RT5682_PWR_PLL2B (0x1 << 5) +#define RT5682_PWR_PLL2B_BIT 5 +#define RT5682_PWR_PLL2F (0x1 << 4) +#define RT5682_PWR_PLL2F_BIT 4 +#define RT5682_PWR_LDO2 (0x1 << 2) +#define RT5682_PWR_LDO2_BIT 2 +#define RT5682_PWR_DET_SPKVDD (0x1 << 1) +#define RT5682_PWR_DET_SPKVDD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5682_PWR_STO1_DAC_L (0x1 << 5) +#define RT5682_PWR_STO1_DAC_L_BIT 5 +#define RT5682_PWR_STO1_DAC_R (0x1 << 4) +#define RT5682_PWR_STO1_DAC_R_BIT 4 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5682_SYS_CLK_DET (0x1 << 15) +#define RT5682_SYS_CLK_DET_SFT 15 +#define RT5682_PLL1_CLK_DET (0x1 << 14) +#define RT5682_PLL1_CLK_DET_SFT 14 +#define RT5682_PLL2_CLK_DET (0x1 << 13) +#define RT5682_PLL2_CLK_DET_SFT 13 +#define RT5682_POW_CLK_DET2_SFT 8 +#define RT5682_POW_CLK_DET_SFT 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5682_DMIC_1_EN_MASK (0x1 << 15) +#define RT5682_DMIC_1_EN_SFT 15 +#define RT5682_DMIC_1_DIS (0x0 << 15) +#define RT5682_DMIC_1_EN (0x1 << 15) +#define RT5682_DMIC_1_DP_MASK (0x3 << 4) +#define RT5682_DMIC_1_DP_SFT 4 +#define RT5682_DMIC_1_DP_GPIO2 (0x0 << 4) +#define RT5682_DMIC_1_DP_GPIO5 (0x1 << 4) +#define RT5682_DMIC_CLK_MASK (0xf << 0) +#define RT5682_DMIC_CLK_SFT 0 + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5682_SEL_ADCDAT_MASK (0x1 << 15) +#define RT5682_SEL_ADCDAT_OUT (0x0 << 15) +#define RT5682_SEL_ADCDAT_IN (0x1 << 15) +#define RT5682_SEL_ADCDAT_SFT 15 +#define RT5682_I2S1_TX_CHL_MASK (0x7 << 12) +#define RT5682_I2S1_TX_CHL_SFT 12 +#define RT5682_I2S1_TX_CHL_16 (0x0 << 12) +#define RT5682_I2S1_TX_CHL_20 (0x1 << 12) +#define RT5682_I2S1_TX_CHL_24 (0x2 << 12) +#define RT5682_I2S1_TX_CHL_32 (0x3 << 12) +#define RT5682_I2S1_TX_CHL_8 (0x4 << 12) +#define RT5682_I2S1_RX_CHL_MASK (0x7 << 8) +#define RT5682_I2S1_RX_CHL_SFT 8 +#define RT5682_I2S1_RX_CHL_16 (0x0 << 8) +#define RT5682_I2S1_RX_CHL_20 (0x1 << 8) +#define RT5682_I2S1_RX_CHL_24 (0x2 << 8) +#define RT5682_I2S1_RX_CHL_32 (0x3 << 8) +#define RT5682_I2S1_RX_CHL_8 (0x4 << 8) +#define RT5682_I2S1_MONO_MASK (0x1 << 7) +#define RT5682_I2S1_MONO_EN (0x1 << 7) +#define RT5682_I2S1_MONO_DIS (0x0 << 7) +#define RT5682_I2S2_MONO_MASK (0x1 << 6) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S1_DL_MASK (0x7 << 4) +#define RT5682_I2S1_DL_SFT 4 +#define RT5682_I2S1_DL_16 (0x0 << 4) +#define RT5682_I2S1_DL_20 (0x1 << 4) +#define RT5682_I2S1_DL_24 (0x2 << 4) +#define RT5682_I2S1_DL_32 (0x3 << 4) +#define RT5682_I2S1_DL_8 (0x4 << 4) + +/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */ +#define RT5682_I2S2_MS_MASK (0x1 << 15) +#define RT5682_I2S2_MS_SFT 15 +#define RT5682_I2S2_MS_M (0x0 << 15) +#define RT5682_I2S2_MS_S (0x1 << 15) +#define RT5682_I2S2_PIN_CFG_MASK (0x1 << 14) +#define RT5682_I2S2_PIN_CFG_SFT 14 +#define RT5682_I2S2_CLK_SEL_MASK (0x1 << 11) +#define RT5682_I2S2_CLK_SEL_SFT 11 +#define RT5682_I2S2_OUT_MASK (0x1 << 9) +#define RT5682_I2S2_OUT_SFT 9 +#define RT5682_I2S2_OUT_UM (0x0 << 9) +#define RT5682_I2S2_OUT_M (0x1 << 9) +#define RT5682_I2S_BP_MASK (0x1 << 8) +#define RT5682_I2S_BP_SFT 8 +#define RT5682_I2S_BP_NOR (0x0 << 8) +#define RT5682_I2S_BP_INV (0x1 << 8) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S2_DL_MASK (0x3 << 4) +#define RT5682_I2S2_DL_SFT 4 +#define RT5682_I2S2_DL_16 (0x0 << 4) +#define RT5682_I2S2_DL_20 (0x1 << 4) +#define RT5682_I2S2_DL_24 (0x2 << 4) +#define RT5682_I2S2_DL_8 (0x3 << 4) +#define RT5682_I2S_DF_MASK (0x7) +#define RT5682_I2S_DF_SFT 0 +#define RT5682_I2S_DF_I2S (0x0) +#define RT5682_I2S_DF_LEFT (0x1) +#define RT5682_I2S_DF_PCM_A (0x2) +#define RT5682_I2S_DF_PCM_B (0x3) +#define RT5682_I2S_DF_PCM_A_N (0x6) +#define RT5682_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5682_ADC_OSR_MASK (0xf << 12) +#define RT5682_ADC_OSR_SFT 12 +#define RT5682_ADC_OSR_D_1 (0x0 << 12) +#define RT5682_ADC_OSR_D_2 (0x1 << 12) +#define RT5682_ADC_OSR_D_4 (0x2 << 12) +#define RT5682_ADC_OSR_D_6 (0x3 << 12) +#define RT5682_ADC_OSR_D_8 (0x4 << 12) +#define RT5682_ADC_OSR_D_12 (0x5 << 12) +#define RT5682_ADC_OSR_D_16 (0x6 << 12) +#define RT5682_ADC_OSR_D_24 (0x7 << 12) +#define RT5682_ADC_OSR_D_32 (0x8 << 12) +#define RT5682_ADC_OSR_D_48 (0x9 << 12) +#define RT5682_I2S_M_DIV_MASK (0xf << 12) +#define RT5682_I2S_M_DIV_SFT 8 +#define RT5682_I2S_M_D_1 (0x0 << 8) +#define RT5682_I2S_M_D_2 (0x1 << 8) +#define RT5682_I2S_M_D_3 (0x2 << 8) +#define RT5682_I2S_M_D_4 (0x3 << 8) +#define RT5682_I2S_M_D_6 (0x4 << 8) +#define RT5682_I2S_M_D_8 (0x5 << 8) +#define RT5682_I2S_M_D_12 (0x6 << 8) +#define RT5682_I2S_M_D_16 (0x7 << 8) +#define RT5682_I2S_M_D_24 (0x8 << 8) +#define RT5682_I2S_M_D_32 (0x9 << 8) +#define RT5682_I2S_M_D_48 (0x10 << 8) +#define RT5682_I2S_CLK_SRC_MASK (0x7 << 4) +#define RT5682_I2S_CLK_SRC_SFT 4 +#define RT5682_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5682_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5682_I2S_CLK_SRC_PLL2 (0x2 << 4) +#define RT5682_I2S_CLK_SRC_SDW (0x3 << 4) +#define RT5682_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */ +#define RT5682_DAC_OSR_MASK (0xf << 0) +#define RT5682_DAC_OSR_SFT 0 +#define RT5682_DAC_OSR_D_1 (0x0 << 0) +#define RT5682_DAC_OSR_D_2 (0x1 << 0) +#define RT5682_DAC_OSR_D_4 (0x2 << 0) +#define RT5682_DAC_OSR_D_6 (0x3 << 0) +#define RT5682_DAC_OSR_D_8 (0x4 << 0) +#define RT5682_DAC_OSR_D_12 (0x5 << 0) +#define RT5682_DAC_OSR_D_16 (0x6 << 0) +#define RT5682_DAC_OSR_D_24 (0x7 << 0) +#define RT5682_DAC_OSR_D_32 (0x8 << 0) +#define RT5682_DAC_OSR_D_48 (0x9 << 0) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5682_I2S2_BCLK_MS2_MASK (0x1 << 11) +#define RT5682_I2S2_BCLK_MS2_SFT 11 +#define RT5682_I2S2_BCLK_MS2_32 (0x0 << 11) +#define RT5682_I2S2_BCLK_MS2_64 (0x1 << 11) + + +/* TDM control 1 (0x0079) */ +#define RT5682_TDM_TX_CH_MASK (0x3 << 12) +#define RT5682_TDM_TX_CH_2 (0x0 << 12) +#define RT5682_TDM_TX_CH_4 (0x1 << 12) +#define RT5682_TDM_TX_CH_6 (0x2 << 12) +#define RT5682_TDM_TX_CH_8 (0x3 << 12) +#define RT5682_TDM_RX_CH_MASK (0x3 << 8) +#define RT5682_TDM_RX_CH_2 (0x0 << 8) +#define RT5682_TDM_RX_CH_4 (0x1 << 8) +#define RT5682_TDM_RX_CH_6 (0x2 << 8) +#define RT5682_TDM_RX_CH_8 (0x3 << 8) +#define RT5682_TDM_ADC_LCA_MASK (0xf << 4) +#define RT5682_TDM_ADC_LCA_SFT 4 +#define RT5682_TDM_ADC_DL_SFT 0 + +/* TDM control 2 (0x007a) */ +#define RT5682_IF1_ADC1_SEL_SFT 14 +#define RT5682_IF1_ADC2_SEL_SFT 12 +#define RT5682_IF1_ADC3_SEL_SFT 10 +#define RT5682_IF1_ADC4_SEL_SFT 8 +#define RT5682_TDM_ADC_SEL_SFT 4 + +/* TDM control 3 (0x007b) */ +#define RT5682_TDM_EN (0x1 << 7) + +/* TDM/I2S control (0x007e) */ +#define RT5682_TDM_S_BP_MASK (0x1 << 15) +#define RT5682_TDM_S_BP_SFT 15 +#define RT5682_TDM_S_BP_NOR (0x0 << 15) +#define RT5682_TDM_S_BP_INV (0x1 << 15) +#define RT5682_TDM_S_LP_MASK (0x1 << 14) +#define RT5682_TDM_S_LP_SFT 14 +#define RT5682_TDM_S_LP_NOR (0x0 << 14) +#define RT5682_TDM_S_LP_INV (0x1 << 14) +#define RT5682_TDM_DF_MASK (0x7 << 11) +#define RT5682_TDM_DF_SFT 11 +#define RT5682_TDM_DF_I2S (0x0 << 11) +#define RT5682_TDM_DF_LEFT (0x1 << 11) +#define RT5682_TDM_DF_PCM_A (0x2 << 11) +#define RT5682_TDM_DF_PCM_B (0x3 << 11) +#define RT5682_TDM_DF_PCM_A_N (0x6 << 11) +#define RT5682_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5682_TDM_CL_MASK (0x3 << 4) +#define RT5682_TDM_CL_16 (0x0 << 4) +#define RT5682_TDM_CL_20 (0x1 << 4) +#define RT5682_TDM_CL_24 (0x2 << 4) +#define RT5682_TDM_CL_32 (0x3 << 4) +#define RT5682_TDM_M_BP_MASK (0x1 << 2) +#define RT5682_TDM_M_BP_SFT 2 +#define RT5682_TDM_M_BP_NOR (0x0 << 2) +#define RT5682_TDM_M_BP_INV (0x1 << 2) +#define RT5682_TDM_M_LP_MASK (0x1 << 1) +#define RT5682_TDM_M_LP_SFT 1 +#define RT5682_TDM_M_LP_NOR (0x0 << 1) +#define RT5682_TDM_M_LP_INV (0x1 << 1) +#define RT5682_TDM_MS_MASK (0x1 << 0) +#define RT5682_TDM_MS_SFT 0 +#define RT5682_TDM_MS_M (0x0 << 0) +#define RT5682_TDM_MS_S (0x1 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5682_SCLK_SRC_MASK (0x7 << 13) +#define RT5682_SCLK_SRC_SFT 13 +#define RT5682_SCLK_SRC_MCLK (0x0 << 13) +#define RT5682_SCLK_SRC_PLL1 (0x1 << 13) +#define RT5682_SCLK_SRC_PLL2 (0x2 << 13) +#define RT5682_SCLK_SRC_SDW (0x3 << 13) +#define RT5682_SCLK_SRC_RCCLK (0x4 << 13) +#define RT5682_PLL1_SRC_MASK (0x3 << 10) +#define RT5682_PLL1_SRC_SFT 10 +#define RT5682_PLL1_SRC_MCLK (0x0 << 10) +#define RT5682_PLL1_SRC_BCLK1 (0x1 << 10) +#define RT5682_PLL1_SRC_SDW (0x2 << 10) +#define RT5682_PLL1_SRC_RC (0x3 << 10) +#define RT5682_PLL2_SRC_MASK (0x3 << 8) +#define RT5682_PLL2_SRC_SFT 8 +#define RT5682_PLL2_SRC_MCLK (0x0 << 8) +#define RT5682_PLL2_SRC_BCLK1 (0x1 << 8) +#define RT5682_PLL2_SRC_SDW (0x2 << 8) +#define RT5682_PLL2_SRC_RC (0x3 << 8) + + + +#define RT5682_PLL_INP_MAX 40000000 +#define RT5682_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5682_PLL_N_MAX 0x001ff +#define RT5682_PLL_N_MASK (RT5682_PLL_N_MAX << 7) +#define RT5682_PLL_N_SFT 7 +#define RT5682_PLL_K_MAX 0x001f +#define RT5682_PLL_K_MASK (RT5682_PLL_K_MAX) +#define RT5682_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5682_PLL_M_MAX 0x00f +#define RT5682_PLL_M_MASK (RT5682_PLL_M_MAX << 12) +#define RT5682_PLL_M_SFT 12 +#define RT5682_PLL_M_BP (0x1 << 11) +#define RT5682_PLL_M_BP_SFT 11 +#define RT5682_PLL_K_BP (0x1 << 10) +#define RT5682_PLL_K_BP_SFT 10 +#define RT5682_PLL_RST (0x1 << 1) + +/* PLL tracking mode 1 (0x0083) */ +#define RT5682_DA_ASRC_MASK (0x1 << 13) +#define RT5682_DA_ASRC_SFT 13 +#define RT5682_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5682_DAC_STO1_ASRC_SFT 12 +#define RT5682_AD_ASRC_MASK (0x1 << 8) +#define RT5682_AD_ASRC_SFT 8 +#define RT5682_AD_ASRC_SEL_MASK (0x1 << 4) +#define RT5682_AD_ASRC_SEL_SFT 4 +#define RT5682_DMIC_ASRC_MASK (0x1 << 3) +#define RT5682_DMIC_ASRC_SFT 3 +#define RT5682_ADC_STO1_ASRC_MASK (0x1 << 2) +#define RT5682_ADC_STO1_ASRC_SFT 2 +#define RT5682_DA_ASRC_SEL_MASK (0x1 << 0) +#define RT5682_DA_ASRC_SEL_SFT 0 + +/* PLL tracking mode 2 3 (0x0084)(0x0085)*/ +#define RT5682_FILTER_CLK_SEL_MASK (0x7 << 12) +#define RT5682_FILTER_CLK_SEL_SFT 12 +#define RT5682_FILTER_CLK_DIV_MASK (0xf << 8) +#define RT5682_FILTER_CLK_DIV_SFT 8 + +/* ASRC Control 4 (0x0086) */ +#define RT5682_ASRCIN_FTK_N1_MASK (0x3 << 14) +#define RT5682_ASRCIN_FTK_N1_SFT 14 +#define RT5682_ASRCIN_FTK_N2_MASK (0x3 << 12) +#define RT5682_ASRCIN_FTK_N2_SFT 12 +#define RT5682_ASRCIN_FTK_M1_MASK (0x7 << 8) +#define RT5682_ASRCIN_FTK_M1_SFT 8 +#define RT5682_ASRCIN_FTK_M2_MASK (0x7 << 4) +#define RT5682_ASRCIN_FTK_M2_SFT 4 + +/* SoundWire reference clk (0x008d) */ +#define RT5682_PLL2_OUT_MASK (0x1 << 8) +#define RT5682_PLL2_OUT_98M (0x0 << 8) +#define RT5682_PLL2_OUT_49M (0x1 << 8) +#define RT5682_SDW_REF_2_MASK (0xf << 4) +#define RT5682_SDW_REF_2_SFT 4 +#define RT5682_SDW_REF_2_48K (0x0 << 4) +#define RT5682_SDW_REF_2_96K (0x1 << 4) +#define RT5682_SDW_REF_2_192K (0x2 << 4) +#define RT5682_SDW_REF_2_32K (0x3 << 4) +#define RT5682_SDW_REF_2_24K (0x4 << 4) +#define RT5682_SDW_REF_2_16K (0x5 << 4) +#define RT5682_SDW_REF_2_12K (0x6 << 4) +#define RT5682_SDW_REF_2_8K (0x7 << 4) +#define RT5682_SDW_REF_2_44K (0x8 << 4) +#define RT5682_SDW_REF_2_88K (0x9 << 4) +#define RT5682_SDW_REF_2_176K (0xa << 4) +#define RT5682_SDW_REF_2_353K (0xb << 4) +#define RT5682_SDW_REF_2_22K (0xc << 4) +#define RT5682_SDW_REF_2_384K (0xd << 4) +#define RT5682_SDW_REF_2_11K (0xe << 4) +#define RT5682_SDW_REF_1_MASK (0xf << 0) +#define RT5682_SDW_REF_1_SFT 0 +#define RT5682_SDW_REF_1_48K (0x0 << 0) +#define RT5682_SDW_REF_1_96K (0x1 << 0) +#define RT5682_SDW_REF_1_192K (0x2 << 0) +#define RT5682_SDW_REF_1_32K (0x3 << 0) +#define RT5682_SDW_REF_1_24K (0x4 << 0) +#define RT5682_SDW_REF_1_16K (0x5 << 0) +#define RT5682_SDW_REF_1_12K (0x6 << 0) +#define RT5682_SDW_REF_1_8K (0x7 << 0) +#define RT5682_SDW_REF_1_44K (0x8 << 0) +#define RT5682_SDW_REF_1_88K (0x9 << 0) +#define RT5682_SDW_REF_1_176K (0xa << 0) +#define RT5682_SDW_REF_1_353K (0xb << 0) +#define RT5682_SDW_REF_1_22K (0xc << 0) +#define RT5682_SDW_REF_1_384K (0xd << 0) +#define RT5682_SDW_REF_1_11K (0xe << 0) + +/* Depop Mode Control 1 (0x008e) */ +#define RT5682_PUMP_EN (0x1 << 3) +#define RT5682_PUMP_EN_SFT 3 +#define RT5682_CAPLESS_EN (0x1 << 0) +#define RT5682_CAPLESS_EN_SFT 0 + +/* Depop Mode Control 2 (0x8f) */ +#define RT5682_RAMP_MASK (0x1 << 12) +#define RT5682_RAMP_SFT 12 +#define RT5682_RAMP_DIS (0x0 << 12) +#define RT5682_RAMP_EN (0x1 << 12) +#define RT5682_BPS_MASK (0x1 << 11) +#define RT5682_BPS_SFT 11 +#define RT5682_BPS_DIS (0x0 << 11) +#define RT5682_BPS_EN (0x1 << 11) +#define RT5682_FAST_UPDN_MASK (0x1 << 10) +#define RT5682_FAST_UPDN_SFT 10 +#define RT5682_FAST_UPDN_DIS (0x0 << 10) +#define RT5682_FAST_UPDN_EN (0x1 << 10) +#define RT5682_VLO_MASK (0x1 << 7) +#define RT5682_VLO_SFT 7 +#define RT5682_VLO_3V (0x0 << 7) +#define RT5682_VLO_33V (0x1 << 7) + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5682_OSW_L_MASK (0x1 << 11) +#define RT5682_OSW_L_SFT 11 +#define RT5682_OSW_L_DIS (0x0 << 11) +#define RT5682_OSW_L_EN (0x1 << 11) +#define RT5682_OSW_R_MASK (0x1 << 10) +#define RT5682_OSW_R_SFT 10 +#define RT5682_OSW_R_DIS (0x0 << 10) +#define RT5682_OSW_R_EN (0x1 << 10) +#define RT5682_PM_HP_MASK (0x3 << 8) +#define RT5682_PM_HP_SFT 8 +#define RT5682_PM_HP_LV (0x0 << 8) +#define RT5682_PM_HP_MV (0x1 << 8) +#define RT5682_PM_HP_HV (0x2 << 8) +#define RT5682_IB_HP_MASK (0x3 << 6) +#define RT5682_IB_HP_SFT 6 +#define RT5682_IB_HP_125IL (0x0 << 6) +#define RT5682_IB_HP_25IL (0x1 << 6) +#define RT5682_IB_HP_5IL (0x2 << 6) +#define RT5682_IB_HP_1IL (0x3 << 6) + +/* Micbias Control1 (0x93) */ +#define RT5682_MIC1_OV_MASK (0x3 << 14) +#define RT5682_MIC1_OV_SFT 14 +#define RT5682_MIC1_OV_2V7 (0x0 << 14) +#define RT5682_MIC1_OV_2V4 (0x1 << 14) +#define RT5682_MIC1_OV_2V25 (0x3 << 14) +#define RT5682_MIC1_OV_1V8 (0x4 << 14) +#define RT5682_MIC1_CLK_MASK (0x1 << 13) +#define RT5682_MIC1_CLK_SFT 13 +#define RT5682_MIC1_CLK_DIS (0x0 << 13) +#define RT5682_MIC1_CLK_EN (0x1 << 13) +#define RT5682_MIC1_OVCD_MASK (0x1 << 12) +#define RT5682_MIC1_OVCD_SFT 12 +#define RT5682_MIC1_OVCD_DIS (0x0 << 12) +#define RT5682_MIC1_OVCD_EN (0x1 << 12) +#define RT5682_MIC1_OVTH_MASK (0x3 << 10) +#define RT5682_MIC1_OVTH_SFT 10 +#define RT5682_MIC1_OVTH_768UA (0x0 << 10) +#define RT5682_MIC1_OVTH_960UA (0x1 << 10) +#define RT5682_MIC1_OVTH_1152UA (0x2 << 10) +#define RT5682_MIC1_OVTH_1960UA (0x3 << 10) +#define RT5682_MIC2_OV_MASK (0x3 << 8) +#define RT5682_MIC2_OV_SFT 8 +#define RT5682_MIC2_OV_2V7 (0x0 << 8) +#define RT5682_MIC2_OV_2V4 (0x1 << 8) +#define RT5682_MIC2_OV_2V25 (0x3 << 8) +#define RT5682_MIC2_OV_1V8 (0x4 << 8) +#define RT5682_MIC2_CLK_MASK (0x1 << 7) +#define RT5682_MIC2_CLK_SFT 7 +#define RT5682_MIC2_CLK_DIS (0x0 << 7) +#define RT5682_MIC2_CLK_EN (0x1 << 7) +#define RT5682_MIC2_OVTH_MASK (0x3 << 4) +#define RT5682_MIC2_OVTH_SFT 4 +#define RT5682_MIC2_OVTH_768UA (0x0 << 4) +#define RT5682_MIC2_OVTH_960UA (0x1 << 4) +#define RT5682_MIC2_OVTH_1152UA (0x2 << 4) +#define RT5682_MIC2_OVTH_1960UA (0x3 << 4) +#define RT5682_PWR_MB_MASK (0x1 << 3) +#define RT5682_PWR_MB_SFT 3 +#define RT5682_PWR_MB_PD (0x0 << 3) +#define RT5682_PWR_MB_PU (0x1 << 3) + +/* Micbias Control2 (0x0094) */ +#define RT5682_PWR_CLK25M_MASK (0x1 << 9) +#define RT5682_PWR_CLK25M_SFT 9 +#define RT5682_PWR_CLK25M_PD (0x0 << 9) +#define RT5682_PWR_CLK25M_PU (0x1 << 9) +#define RT5682_PWR_CLK1M_MASK (0x1 << 8) +#define RT5682_PWR_CLK1M_SFT 8 +#define RT5682_PWR_CLK1M_PD (0x0 << 8) +#define RT5682_PWR_CLK1M_PU (0x1 << 8) + +/* RC Clock Control (0x009f) */ +#define RT5682_POW_IRQ (0x1 << 15) +#define RT5682_POW_JDH (0x1 << 14) +#define RT5682_POW_JDL (0x1 << 13) +#define RT5682_POW_ANA (0x1 << 12) + +/* I2S Master Mode Clock Control 1 (0x00a0) */ +#define RT5682_CLK_SRC_MCLK (0x0) +#define RT5682_CLK_SRC_PLL1 (0x1) +#define RT5682_CLK_SRC_PLL2 (0x2) +#define RT5682_CLK_SRC_SDW (0x3) +#define RT5682_CLK_SRC_RCCLK (0x4) +#define RT5682_I2S_PD_1 (0x0) +#define RT5682_I2S_PD_2 (0x1) +#define RT5682_I2S_PD_3 (0x2) +#define RT5682_I2S_PD_4 (0x3) +#define RT5682_I2S_PD_6 (0x4) +#define RT5682_I2S_PD_8 (0x5) +#define RT5682_I2S_PD_12 (0x6) +#define RT5682_I2S_PD_16 (0x7) +#define RT5682_I2S_PD_24 (0x8) +#define RT5682_I2S_PD_32 (0x9) +#define RT5682_I2S_PD_48 (0xa) +#define RT5682_I2S2_SRC_MASK (0x3 << 4) +#define RT5682_I2S2_SRC_SFT 4 +#define RT5682_I2S2_M_PD_MASK (0xf << 0) +#define RT5682_I2S2_M_PD_SFT 0 + +/* IRQ Control 1 (0x00b6) */ +#define RT5682_JD1_PULSE_EN_MASK (0x1 << 10) +#define RT5682_JD1_PULSE_EN_SFT 10 +#define RT5682_JD1_PULSE_DIS (0x0 << 10) +#define RT5682_JD1_PULSE_EN (0x1 << 10) + +/* IRQ Control 2 (0x00b7) */ +#define RT5682_JD1_EN_MASK (0x1 << 15) +#define RT5682_JD1_EN_SFT 15 +#define RT5682_JD1_DIS (0x0 << 15) +#define RT5682_JD1_EN (0x1 << 15) +#define RT5682_JD1_POL_MASK (0x1 << 13) +#define RT5682_JD1_POL_NOR (0x0 << 13) +#define RT5682_JD1_POL_INV (0x1 << 13) + +/* IRQ Control 3 (0x00b8) */ +#define RT5682_IL_IRQ_MASK (0x1 << 7) +#define RT5682_IL_IRQ_DIS (0x0 << 7) +#define RT5682_IL_IRQ_EN (0x1 << 7) + +/* GPIO Control 1 (0x00c0) */ +#define RT5682_GP1_PIN_MASK (0x3 << 14) +#define RT5682_GP1_PIN_SFT 14 +#define RT5682_GP1_PIN_GPIO1 (0x0 << 14) +#define RT5682_GP1_PIN_IRQ (0x1 << 14) +#define RT5682_GP1_PIN_DMIC_CLK (0x2 << 14) +#define RT5682_GP2_PIN_MASK (0x3 << 12) +#define RT5682_GP2_PIN_SFT 12 +#define RT5682_GP2_PIN_GPIO2 (0x0 << 12) +#define RT5682_GP2_PIN_LRCK2 (0x1 << 12) +#define RT5682_GP2_PIN_DMIC_SDA (0x2 << 12) +#define RT5682_GP3_PIN_MASK (0x3 << 10) +#define RT5682_GP3_PIN_SFT 10 +#define RT5682_GP3_PIN_GPIO3 (0x0 << 10) +#define RT5682_GP3_PIN_BCLK2 (0x1 << 10) +#define RT5682_GP3_PIN_DMIC_CLK (0x2 << 10) +#define RT5682_GP4_PIN_MASK (0x3 << 8) +#define RT5682_GP4_PIN_SFT 8 +#define RT5682_GP4_PIN_GPIO4 (0x0 << 8) +#define RT5682_GP4_PIN_ADCDAT1 (0x1 << 8) +#define RT5682_GP4_PIN_DMIC_CLK (0x2 << 8) +#define RT5682_GP4_PIN_ADCDAT2 (0x3 << 8) +#define RT5682_GP5_PIN_MASK (0x3 << 6) +#define RT5682_GP5_PIN_SFT 6 +#define RT5682_GP5_PIN_GPIO5 (0x0 << 6) +#define RT5682_GP5_PIN_DACDAT1 (0x1 << 6) +#define RT5682_GP5_PIN_DMIC_SDA (0x2 << 6) +#define RT5682_GP6_PIN_MASK (0x1 << 5) +#define RT5682_GP6_PIN_SFT 5 +#define RT5682_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5682_GP6_PIN_LRCK1 (0x1 << 5) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5682_GP1_PF_MASK (0x1 << 15) +#define RT5682_GP1_PF_IN (0x0 << 15) +#define RT5682_GP1_PF_OUT (0x1 << 15) +#define RT5682_GP1_OUT_MASK (0x1 << 14) +#define RT5682_GP1_OUT_L (0x0 << 14) +#define RT5682_GP1_OUT_H (0x1 << 14) +#define RT5682_GP2_PF_MASK (0x1 << 13) +#define RT5682_GP2_PF_IN (0x0 << 13) +#define RT5682_GP2_PF_OUT (0x1 << 13) +#define RT5682_GP2_OUT_MASK (0x1 << 12) +#define RT5682_GP2_OUT_L (0x0 << 12) +#define RT5682_GP2_OUT_H (0x1 << 12) +#define RT5682_GP3_PF_MASK (0x1 << 11) +#define RT5682_GP3_PF_IN (0x0 << 11) +#define RT5682_GP3_PF_OUT (0x1 << 11) +#define RT5682_GP3_OUT_MASK (0x1 << 10) +#define RT5682_GP3_OUT_L (0x0 << 10) +#define RT5682_GP3_OUT_H (0x1 << 10) +#define RT5682_GP4_PF_MASK (0x1 << 9) +#define RT5682_GP4_PF_IN (0x0 << 9) +#define RT5682_GP4_PF_OUT (0x1 << 9) +#define RT5682_GP4_OUT_MASK (0x1 << 8) +#define RT5682_GP4_OUT_L (0x0 << 8) +#define RT5682_GP4_OUT_H (0x1 << 8) +#define RT5682_GP5_PF_MASK (0x1 << 7) +#define RT5682_GP5_PF_IN (0x0 << 7) +#define RT5682_GP5_PF_OUT (0x1 << 7) +#define RT5682_GP5_OUT_MASK (0x1 << 6) +#define RT5682_GP5_OUT_L (0x0 << 6) +#define RT5682_GP5_OUT_H (0x1 << 6) +#define RT5682_GP6_PF_MASK (0x1 << 5) +#define RT5682_GP6_PF_IN (0x0 << 5) +#define RT5682_GP6_PF_OUT (0x1 << 5) +#define RT5682_GP6_OUT_MASK (0x1 << 4) +#define RT5682_GP6_OUT_L (0x0 << 4) +#define RT5682_GP6_OUT_H (0x1 << 4) + + +/* GPIO Status (0x00c2) */ +#define RT5682_GP6_STA (0x1 << 6) +#define RT5682_GP5_STA (0x1 << 5) +#define RT5682_GP4_STA (0x1 << 4) +#define RT5682_GP3_STA (0x1 << 3) +#define RT5682_GP2_STA (0x1 << 2) +#define RT5682_GP1_STA (0x1 << 1) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5682_SV_MASK (0x1 << 15) +#define RT5682_SV_SFT 15 +#define RT5682_SV_DIS (0x0 << 15) +#define RT5682_SV_EN (0x1 << 15) +#define RT5682_ZCD_MASK (0x1 << 10) +#define RT5682_ZCD_SFT 10 +#define RT5682_ZCD_PD (0x0 << 10) +#define RT5682_ZCD_PU (0x1 << 10) +#define RT5682_SV_DLY_MASK (0xf) +#define RT5682_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5682_ZCD_BST1_CBJ_MASK (0x1 << 7) +#define RT5682_ZCD_BST1_CBJ_SFT 7 +#define RT5682_ZCD_BST1_CBJ_DIS (0x0 << 7) +#define RT5682_ZCD_BST1_CBJ_EN (0x1 << 7) +#define RT5682_ZCD_RECMIX_MASK (0x1) +#define RT5682_ZCD_RECMIX_SFT 0 +#define RT5682_ZCD_RECMIX_DIS (0x0) +#define RT5682_ZCD_RECMIX_EN (0x1) + +/* 4 Button Inline Command Control 2 (0x00e3) */ +#define RT5682_4BTN_IL_MASK (0x1 << 15) +#define RT5682_4BTN_IL_EN (0x1 << 15) +#define RT5682_4BTN_IL_DIS (0x0 << 15) +#define RT5682_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5682_4BTN_IL_NOR (0x1 << 14) +#define RT5682_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control (0x00f0) */ +#define RT5682_JDH_RS_MASK (0x1 << 4) +#define RT5682_JDH_NO_PLUG (0x1 << 4) +#define RT5682_JDH_PLUG (0x0 << 4) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5682_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5682_CKXEN_DAC1_SFT 13 +#define RT5682_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5682_CKGEN_DAC1_SFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5682_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5682_CKXEN_ADC1_SFT 13 +#define RT5682_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5682_CKGEN_ADC1_SFT 12 + +/* Volume test (0x013f)*/ +#define RT5682_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5682_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5682_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5682_AD2DA_LB_MASK (0x1 << 10) +#define RT5682_AD2DA_LB_SFT 10 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5682_NG2_EN_MASK (0x1 << 15) +#define RT5682_NG2_EN (0x1 << 15) +#define RT5682_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5682_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5682_DEB_80_MS (0x0 << 4) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5682_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5682_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5682_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5682_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5682_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5682_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5682_SAR_BUTDET_RST (0x0 << 13) +#define RT5682_SAR_POW_MASK (0x1 << 12) +#define RT5682_SAR_POW_EN (0x1 << 12) +#define RT5682_SAR_POW_DIS (0x0 << 12) +#define RT5682_SAR_RST_MASK (0x1 << 11) +#define RT5682_SAR_RST_NORMAL (0x1 << 11) +#define RT5682_SAR_RST (0x0 << 11) +#define RT5682_SAR_BYPASS_MASK (0x1 << 10) +#define RT5682_SAR_BYPASS_EN (0x1 << 10) +#define RT5682_SAR_BYPASS_DIS (0x0 << 10) +#define RT5682_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5682_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5682_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5682_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5682_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5682_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5682_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5682_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5682_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5682_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5682_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* SAR ADC Inline Command Control 13 (0x021c) */ +#define RT5682_SAR_SOUR_MASK (0x3f) +#define RT5682_SAR_SOUR_BTN (0x3f) +#define RT5682_SAR_SOUR_TYPE (0x0) + + +/* System Clock Source */ +enum { + RT5682_SCLK_S_MCLK, + RT5682_SCLK_S_PLL1, + RT5682_SCLK_S_PLL2, + RT5682_SCLK_S_RCCLK, +}; + +/* PLL Source */ +enum { + RT5682_PLL1_S_MCLK, + RT5682_PLL1_S_BCLK1, + RT5682_PLL1_S_RCCLK, +}; + +enum { + RT5682_AIF1, + RT5682_AIF2, + RT5682_AIFS +}; + +/* filter mask */ +enum { + RT5682_DA_STEREO1_FILTER = 0x1, + RT5682_AD_STEREO1_FILTER = (0x1 << 1), +}; + +enum { + RT5682_CLK_SEL_SYS, + RT5682_CLK_SEL_I2S1_ASRC, + RT5682_CLK_SEL_I2S2_ASRC, +}; + +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5682_H__ */ From 37b41026153a9ade8bbdecf2dcf6975e4aa82372 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 22 Jun 2018 02:23:24 +0000 Subject: [PATCH 288/298] ASoC: rt5682: use devm_snd_soc_register_component() Using devm_snd_soc_register_component() and drop all of the code related to .remove hook. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown (cherry picked from commit a98ec93d7e378db21a6cdbc6d55feaba9fb4b999) --- sound/soc/codecs/rt5682.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 61a97301bcfad6..baad177908abf9 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2630,17 +2630,11 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, } - return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5682, + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5682, rt5682_dai, ARRAY_SIZE(rt5682_dai)); } -static int rt5682_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_component(&i2c->dev); - - return 0; -} - static void rt5682_i2c_shutdown(struct i2c_client *client) { struct rt5682_priv *rt5682 = i2c_get_clientdata(client); @@ -2671,7 +2665,6 @@ static struct i2c_driver rt5682_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5682_acpi_match), }, .probe = rt5682_i2c_probe, - .remove = rt5682_i2c_remove, .shutdown = rt5682_i2c_shutdown, .id_table = rt5682_i2c_id, }; From 6a8f6218a76d5851c39806022aff82d732f0702d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 3 Jul 2018 13:07:25 +0800 Subject: [PATCH 289/298] ASoC: rt5682: add button detection mode control We are currently using power saving mode for button detection. However, it will impact the headset recording performance. This patch will switch button detection to normal mode in capture and switch to power saving mode in the end of capture. Signed-off-by: Bard Liao Signed-off-by: Mark Brown (cherry picked from commit 2daf3d9962c5a11fb79fe17bae03125df5d60236) --- sound/soc/codecs/rt5682.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index baad177908abf9..640d400ca01359 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -857,6 +857,8 @@ static int rt5682_button_detect(struct snd_soc_component *component) btn_type = val & 0xfff0; snd_soc_component_write(component, RT5682_4BTN_IL_CMD_1, val); pr_debug("%s btn_type=%x\n", __func__, btn_type); + snd_soc_component_update_bits(component, + RT5682_SAR_IL_CMD_2, 0x10, 0x10); return btn_type; } @@ -1645,6 +1647,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL, RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix, ARRAY_SIZE(rt5682_sto1_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("BTN Detection Mode", RT5682_SAR_IL_CMD_1, + 14, 1, NULL, 0), /* ADC PGA */ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1807,6 +1811,8 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + {"ADC Stereo1 Filter", NULL, "BTN Detection Mode"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, From f341ebc98171ce30cd659381db06324ba7d70be4 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 24 Aug 2018 10:51:51 +0800 Subject: [PATCH 290/298] ASoC: rt5682: Update calibration function New calibration sequence allows rt5682 do calibration without MCLK. Signed-off-by: Shuming Fan Signed-off-by: Mark Brown (cherry picked from commit 513792c2554bdece11d82568ea25501a555abd34) --- sound/soc/codecs/rt5682.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 640d400ca01359..0ea2759e5885f9 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2454,27 +2454,15 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2bf); usleep_range(15000, 20000); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2bf); - regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); - regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8001); - regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); - regmap_write(rt5682->regmap, RT5682_STO1_DAC_MIXER, 0x2080); - regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x4040); - regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0069); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0300); + regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x8000); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100); regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); - regmap_write(rt5682->regmap, RT5682_HP_CTRL_2, 0x6000); - regmap_write(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, 0x0f26); - regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7f05); - regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); - regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); - regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_9, 0x000f); - regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8d01); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1); - regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311); - regmap_write(rt5682->regmap, RT5682_RESET_HPF_CTRL, 0x0000); - regmap_write(rt5682->regmap, RT5682_ADC_STO1_HP_CTRL_1, 0x3320); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00); @@ -2490,7 +2478,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) pr_err("HP Calibration Failure\n"); /* restore settings */ - regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000); regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); mutex_unlock(&rt5682->calibrate_mutex); From bad7ef5f75abe31a4953ecfb51efda2319ff361f Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 24 Aug 2018 10:52:19 +0800 Subject: [PATCH 291/298] ASoC: rt5682: Change DAC/ADC volume scale The step of DAC/ADC volume scale changes from 0.375dB to 0.75dB Signed-off-by: Shuming Fan Signed-off-by: Mark Brown Cc: stable@vger.kernel.org (cherry picked from commit 7509487785d7a2bf3606cf26710f0ca29e9ca94d) --- sound/soc/codecs/rt5682.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 0ea2759e5885f9..731c6a849f69bc 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -750,8 +750,8 @@ static bool rt5682_readable_register(struct device *dev, unsigned int reg) } static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); -static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); -static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ @@ -1114,7 +1114,7 @@ static const struct snd_kcontrol_new rt5682_snd_controls[] = { /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, - RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 175, 0, dac_vol_tlv), + RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 86, 0, dac_vol_tlv), /* IN Boost Volume */ SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL, @@ -1124,7 +1124,7 @@ static const struct snd_kcontrol_new rt5682_snd_controls[] = { SOC_DOUBLE("STO1 ADC Capture Switch", RT5682_STO1_ADC_DIG_VOL, RT5682_L_MUTE_SFT, RT5682_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682_STO1_ADC_DIG_VOL, - RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 127, 0, adc_vol_tlv), + RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), /* ADC Boost Volume Control */ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682_STO1_ADC_BOOST, From 5121c8d36aa71e9ce7c5703472206a66894bac50 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 18 Sep 2018 19:51:08 +0800 Subject: [PATCH 292/298] ASoC: rt5682: Improve HP performance We change the settings while HP power-up for better performance. Signed-off-by: Shuming Fan Signed-off-by: Mark Brown (cherry picked from commit bf0fa00fd8410b377a3403adb58e32fc703e86e8) --- sound/soc/codecs/rt5682.c | 32 +++++++++++++++++++++++++++++--- sound/soc/codecs/rt5682.h | 14 ++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 731c6a849f69bc..83202e9e5abd37 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -1437,6 +1437,28 @@ static const struct snd_kcontrol_new hpor_switch = SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, RT5682_R_MUTE_SFT, 1, 1); +static int rt5682_charge_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_HV); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_LV); + break; + default: + return 0; + } + + return 0; +} + static int rt5682_hp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1449,8 +1471,6 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, RT5682_HP_LOGIC_CTRL_2, 0x0012); snd_soc_component_write(component, RT5682_HP_CTRL_2, 0x6000); - snd_soc_component_update_bits(component, RT5682_STO_NG2_CTRL_1, - RT5682_NG2_EN_MASK, RT5682_NG2_EN); snd_soc_component_update_bits(component, RT5682_DEPOP_1, 0x60, 0x60); break; @@ -1723,7 +1743,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1, RT5682_PWR_HA_R_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1, - RT5682_PUMP_EN_SFT, 0, NULL, 0), + RT5682_PUMP_EN_SFT, 0, rt5682_charge_pump_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1, RT5682_CAPLESS_EN_SFT, 0, NULL, 0), @@ -1884,6 +1905,7 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"HP Amp", NULL, "Charge Pump"}, {"HP Amp", NULL, "CLKDET SYS"}, {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref1"}, {"HP Amp", NULL, "Vref2"}, {"HPOL Playback", "Switch", "HP Amp"}, {"HPOR Playback", "Switch", "HP Amp"}, @@ -2607,6 +2629,10 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK, RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1); regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8, + RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA); + regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1, + RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ); INIT_DELAYED_WORK(&rt5682->jack_detect_work, rt5682_jack_detect_handler); diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 8068140ebe3f19..d82a8301fd745c 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1214,6 +1214,20 @@ #define RT5682_JDH_NO_PLUG (0x1 << 4) #define RT5682_JDH_PLUG (0x0 << 4) +/* Bias current control 8 (0x0111) */ +#define RT5682_HPA_CP_BIAS_CTRL_MASK (0x3 << 2) +#define RT5682_HPA_CP_BIAS_2UA (0x0 << 2) +#define RT5682_HPA_CP_BIAS_3UA (0x1 << 2) +#define RT5682_HPA_CP_BIAS_4UA (0x2 << 2) +#define RT5682_HPA_CP_BIAS_6UA (0x3 << 2) + +/* Charge Pump Internal Register1 (0x0125) */ +#define RT5682_CP_CLK_HP_MASK (0x3 << 4) +#define RT5682_CP_CLK_HP_100KHZ (0x0 << 4) +#define RT5682_CP_CLK_HP_200KHZ (0x1 << 4) +#define RT5682_CP_CLK_HP_300KHZ (0x2 << 4) +#define RT5682_CP_CLK_HP_600KHZ (0x3 << 4) + /* Chopper and Clock control for DAC (0x013a)*/ #define RT5682_CKXEN_DAC1_MASK (0x1 << 13) #define RT5682_CKXEN_DAC1_SFT 13 From 3d3ba72478717014d286f5b09a4d320ef3283236 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 18 Sep 2018 19:51:24 +0800 Subject: [PATCH 293/298] ASoC: rt5682: Remove HP volume control This patch removed Headphone Playback Volume control. Due to codec settings, we don't want the user to change HP analog gain. The user could use DAC1 Playback Volume control to change playback volume. Signed-off-by: Shuming Fan Signed-off-by: Mark Brown (cherry picked from commit 3f24f37adbc9a1059420a9c8f857e3490a4bce5e) --- sound/soc/codecs/rt5682.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 83202e9e5abd37..7213b1cfb18ac0 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -749,7 +749,6 @@ static bool rt5682_readable_register(struct device *dev, unsigned int reg) } } -static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); @@ -1108,10 +1107,6 @@ static void rt5682_jack_detect_handler(struct work_struct *work) } static const struct snd_kcontrol_new rt5682_snd_controls[] = { - /* Headphone Output Volume */ - SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5682_HPL_GAIN, - RT5682_HPR_GAIN, RT5682_G_HP_SFT, 15, 1, hp_vol_tlv), - /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 86, 0, dac_vol_tlv), From 18ad68542f9b27b9687f4516c153637148aa4c56 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 18 Sep 2018 19:50:38 +0800 Subject: [PATCH 294/298] ASoC: rt5682: Update calibration function The ADC/DAC path should open while calibration process. Signed-off-by: Shuming Fan Signed-off-by: Mark Brown (cherry picked from commit afd603e4ded0fad9e3102d514020af8494da1604) --- sound/soc/codecs/rt5682.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 7213b1cfb18ac0..2725eb72fb1b26 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2468,17 +2468,22 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) mutex_lock(&rt5682->calibrate_mutex); rt5682_reset(rt5682->regmap); - regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2bf); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af); usleep_range(15000, 20000); - regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2bf); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af); regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0300); regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x8000); regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100); + regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800); regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1); + regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00); @@ -2495,8 +2500,12 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) pr_err("HP Calibration Failure\n"); /* restore settings */ + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0x02af); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080); regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000); regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); + regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); mutex_unlock(&rt5682->calibrate_mutex); From a3a6553087153d261bc5759441bc874f39eccae2 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 18 Sep 2018 19:51:38 +0800 Subject: [PATCH 295/298] ASoC: rt5682: Fix the boost volume at the begining of playback This patch fixed the boost volume at the begining of playback while DAC volume set to lower level. Signed-off-by: Shuming Fan Signed-off-by: Mark Brown (cherry picked from commit 28b20dde5e1c943ab899549a655ac4935cffccbb) --- sound/soc/codecs/rt5682.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 2725eb72fb1b26..18099668e96062 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -68,6 +68,7 @@ struct rt5682_priv { static const struct reg_sequence patch_list[] = { {0x01c1, 0x1000}, + {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, }; static const struct reg_default rt5682_reg[] = { @@ -1468,6 +1469,8 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, RT5682_HP_CTRL_2, 0x6000); snd_soc_component_update_bits(component, RT5682_DEPOP_1, 0x60, 0x60); + snd_soc_component_update_bits(component, + RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080); break; case SND_SOC_DAPM_POST_PMD: @@ -1475,6 +1478,8 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, RT5682_DEPOP_1, 0x60, 0x0); snd_soc_component_write(component, RT5682_HP_CTRL_2, 0x0000); + snd_soc_component_update_bits(component, + RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000); break; default: From fe8404a3ba3a3e4dcbb1a48a0bb3df6a8331b467 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 18 Sep 2018 19:51:53 +0800 Subject: [PATCH 296/298] ASoC: rt5682: Minor code modification Minor code changes are: - improve the readability in patch list - add i2c remove function - regmap_register_patch changes to regmap_multi_reg_write Signed-off-by: Shuming Fan Signed-off-by: Mark Brown (cherry picked from commit 37efe23dcca3c59cee662f1c28835020bef31cc0) --- sound/soc/codecs/rt5682.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 18099668e96062..340f90497d072a 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -67,7 +67,7 @@ struct rt5682_priv { }; static const struct reg_sequence patch_list[] = { - {0x01c1, 0x1000}, + {RT5682_HP_IMP_SENS_CTRL_19, 0x1000}, {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, }; @@ -2584,7 +2584,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, rt5682_calibrate(rt5682); - ret = regmap_register_patch(rt5682->regmap, patch_list, + ret = regmap_multi_reg_write(rt5682->regmap, patch_list, ARRAY_SIZE(patch_list)); if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); @@ -2659,11 +2659,17 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, } - return devm_snd_soc_register_component(&i2c->dev, - &soc_component_dev_rt5682, + return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5682, rt5682_dai, ARRAY_SIZE(rt5682_dai)); } +static int rt5682_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_component(&i2c->dev); + + return 0; +} + static void rt5682_i2c_shutdown(struct i2c_client *client) { struct rt5682_priv *rt5682 = i2c_get_clientdata(client); @@ -2694,6 +2700,7 @@ static struct i2c_driver rt5682_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5682_acpi_match), }, .probe = rt5682_i2c_probe, + .remove = rt5682_i2c_remove, .shutdown = rt5682_i2c_shutdown, .id_table = rt5682_i2c_id, }; From 03cadf8f0f02b228baf8dfec16f300a0cd831df8 Mon Sep 17 00:00:00 2001 From: Bard liao Date: Fri, 26 Oct 2018 17:05:09 +0800 Subject: [PATCH 297/298] ASoC: Intel: common: add SOF information for APL RVP Add firmware/topology information for APL RVP Signed-off-by: Bard liao --- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index f39386e540d322..2756fa4ab55201 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -19,6 +19,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-rt298.tplg", + .asoc_plat_name = "0000:00:0e.0", }, { .id = "DLGS7219", From 7dab9e5fc4b2a74c0186c53206b4d901e8b74251 Mon Sep 17 00:00:00 2001 From: Zhu Yingjiang Date: Sun, 28 Oct 2018 14:44:10 +0800 Subject: [PATCH 298/298] ASoC:SOF remove duplicated defines there are duplicated defines HDA_DSP_ADSPIS_CL_DMA HDA_DSP_ADSPIC_CL_DMA the defines HDA_SKL_ADSP_FW_STATUS and HDA_DSP_SRAM_REG_ROM_STATUS_SKL are the same Signed-off-by: Zhu Yingjiang --- sound/soc/sof/intel/hda-loader-skl.c | 8 +------- sound/soc/sof/intel/hda.h | 3 --- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c index ca8a5bd3dd9abe..092bdd98e86960 100644 --- a/sound/soc/sof/intel/hda-loader-skl.c +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -31,12 +31,6 @@ #define HDA_SKL_WAIT_TIMEOUT 500 /* 500 msec */ #define HDA_SKL_CLDMA_MAX_BUFFER_SIZE (32 * PAGE_SIZE) -/* Intel HD Audio SRAM Window 0*/ -#define HDA_SKL_ADSP_SRAM0_BASE 0x8000 - -/* Firmware status window */ -#define HDA_SKL_ADSP_FW_STATUS HDA_SKL_ADSP_SRAM0_BASE - /* Stream Reset */ #define HDA_CL_SD_CTL_SRST_SHIFT 0 #define HDA_CL_SD_CTL_SRST(x) (((x) & 0x1) << \ @@ -532,7 +526,7 @@ static int cl_copy_fw_skl(struct snd_sof_dev *sdev) } ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, - HDA_SKL_ADSP_FW_STATUS, + HDA_DSP_SRAM_REG_ROM_STATUS_SKL, HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_FW_FW_LOADED, HDA_DSP_BASEFW_TIMEOUT); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index bcd95c1e35e881..6a31a8b312a594 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -268,9 +268,6 @@ #define HDA_DSP_ADSPCS_CPA_SHIFT 24 #define HDA_DSP_ADSPCS_CPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CPA_SHIFT) -#define HDA_DSP_ADSPIC_CL_DMA 0x2 -#define HDA_DSP_ADSPIS_CL_DMA 0x2 - /* Mask for a given core index, c = 0.. number of supported cores - 1 */ #define HDA_DSP_CORE_MASK(c) BIT(c)