Loading Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt 0 → 100644 +39 −0 Original line number Diff line number Diff line Allwinner Sony/Philips Digital Interface Format (S/PDIF) Controller The Allwinner S/PDIF audio block is a transceiver that allows the processor to receive and transmit digital audio via an coaxial cable or a fibre cable. For now only playback is supported. Required properties: - compatible : should be one of the following: - "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC - reg : Offset and length of the register set for the device. - interrupts : Contains the spdif interrupt. - dmas : Generic dma devicetree binding as described in Documentation/devicetree/bindings/dma/dma.txt. - dma-names : Two dmas have to be defined, "tx" and "rx". - clocks : Contains an entry for each entry in clock-names. - clock-names : Includes the following entries: "apb" clock for the spdif bus. "spdif" clock for spdif controller. Example: spdif: spdif@01c21000 { compatible = "allwinner,sun4i-a10-spdif"; reg = <0x01c21000 0x40>; interrupts = <13>; clocks = <&apb0_gates 1>, <&spdif_clk>; clock-names = "apb", "spdif"; dmas = <&dma 0 2>, <&dma 0 2>; dma-names = "rx", "tx"; status = "okay"; }; include/sound/soc-topology.h +10 −11 Original line number Diff line number Diff line Loading @@ -56,12 +56,6 @@ struct snd_soc_dobj_widget { unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */ }; /* dynamic PCM DAI object */ struct snd_soc_dobj_pcm_dai { struct snd_soc_tplg_pcm_dai *pd; unsigned int count; }; /* generic dynamic object - all dynamic objects belong to this struct */ struct snd_soc_dobj { enum snd_soc_dobj_type type; Loading @@ -71,7 +65,6 @@ struct snd_soc_dobj { union { struct snd_soc_dobj_control control; struct snd_soc_dobj_widget widget; struct snd_soc_dobj_pcm_dai pcm_dai; }; void *private; /* core does not touch this */ }; Loading Loading @@ -126,10 +119,16 @@ struct snd_soc_tplg_ops { int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* FE - used for any driver specific init */ int (*pcm_dai_load)(struct snd_soc_component *, struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe); int (*pcm_dai_unload)(struct snd_soc_component *, /* FE DAI - used for any driver specific init */ int (*dai_load)(struct snd_soc_component *, struct snd_soc_dai_driver *dai_drv); 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_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* callback to handle vendor bespoke data */ Loading include/sound/soc.h +1 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ #include <sound/compress_driver.h> #include <sound/control.h> #include <sound/ac97_codec.h> #include <sound/soc-topology.h> /* * Convenience kcontrol builders Loading Loading @@ -404,6 +403,7 @@ struct snd_soc_jack_zone; struct snd_soc_jack_pin; #include <sound/soc-dapm.h> #include <sound/soc-dpcm.h> #include <sound/soc-topology.h> struct snd_soc_jack_gpio; Loading sound/soc/codecs/wm8974.c +93 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,11 @@ #include "wm8974.h" struct wm8974_priv { unsigned int mclk; unsigned int fs; }; static const struct reg_default wm8974_reg_defaults[] = { { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 }, { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 }, Loading Loading @@ -379,6 +384,79 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return 0; } static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out, int *mclkdiv) { unsigned int ratio = 2 * f_in / f_out; if (ratio <= 2) { *mclkdiv = WM8974_MCLKDIV_1; ratio = 2; } else if (ratio == 3) { *mclkdiv = WM8974_MCLKDIV_1_5; } else if (ratio == 4) { *mclkdiv = WM8974_MCLKDIV_2; } else if (ratio <= 6) { *mclkdiv = WM8974_MCLKDIV_3; ratio = 6; } else if (ratio <= 8) { *mclkdiv = WM8974_MCLKDIV_4; ratio = 8; } else if (ratio <= 12) { *mclkdiv = WM8974_MCLKDIV_6; ratio = 12; } else if (ratio <= 16) { *mclkdiv = WM8974_MCLKDIV_8; ratio = 16; } else { *mclkdiv = WM8974_MCLKDIV_12; ratio = 24; } return f_out * ratio / 2; } static int wm8974_update_clocks(struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); unsigned int fs256; unsigned int fpll = 0; unsigned int f; int mclkdiv; if (!priv->mclk || !priv->fs) return 0; fs256 = 256 * priv->fs; f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv); if (f != priv->mclk) { /* The PLL performs best around 90MHz */ fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv); } wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll); wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv); return 0; } static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); if (dir != SND_SOC_CLOCK_IN) return -EINVAL; priv->mclk = freq; return wm8974_update_clocks(dai); } static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { Loading Loading @@ -441,8 +519,15 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f; u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1; int err; priv->fs = params_rate(params); err = wm8974_update_clocks(dai); if (err) return err; /* bit size */ switch (params_width(params)) { Loading Loading @@ -547,6 +632,7 @@ static const struct snd_soc_dai_ops wm8974_ops = { .set_fmt = wm8974_set_dai_fmt, .set_clkdiv = wm8974_set_dai_clkdiv, .set_pll = wm8974_set_dai_pll, .set_sysclk = wm8974_set_dai_sysclk, }; static struct snd_soc_dai_driver wm8974_dai = { Loading Loading @@ -606,9 +692,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { static int wm8974_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8974_priv *priv; struct regmap *regmap; int ret; priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; i2c_set_clientdata(i2c, priv); regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap); if (IS_ERR(regmap)) return PTR_ERR(regmap); Loading sound/soc/soc-topology.c +154 −86 Original line number Diff line number Diff line Loading @@ -223,51 +223,6 @@ static int get_widget_id(int tplg_type) return -EINVAL; } static enum snd_soc_dobj_type get_dobj_mixer_type( struct snd_soc_tplg_ctl_hdr *control_hdr) { if (control_hdr == NULL) return SND_SOC_DOBJ_NONE; switch (control_hdr->ops.info) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_CTL_STROBE: return SND_SOC_DOBJ_MIXER; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: return SND_SOC_DOBJ_ENUM; case SND_SOC_TPLG_CTL_BYTES: return SND_SOC_DOBJ_BYTES; default: return SND_SOC_DOBJ_NONE; } } static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr, struct snd_soc_tplg_ctl_hdr *control_hdr) { switch (hdr->type) { case SND_SOC_TPLG_TYPE_MIXER: return get_dobj_mixer_type(control_hdr); case SND_SOC_TPLG_TYPE_DAPM_GRAPH: case SND_SOC_TPLG_TYPE_MANIFEST: return SND_SOC_DOBJ_NONE; case SND_SOC_TPLG_TYPE_DAPM_WIDGET: return SND_SOC_DOBJ_WIDGET; case SND_SOC_TPLG_TYPE_DAI_LINK: return SND_SOC_DOBJ_DAI_LINK; case SND_SOC_TPLG_TYPE_PCM: return SND_SOC_DOBJ_PCM; case SND_SOC_TPLG_TYPE_CODEC_LINK: return SND_SOC_DOBJ_CODEC_LINK; default: return SND_SOC_DOBJ_NONE; } } static inline void soc_bind_err(struct soc_tplg *tplg, struct snd_soc_tplg_ctl_hdr *hdr, int index) { Loading Loading @@ -330,12 +285,22 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, return 0; } /* pass dynamic FEs configurations to component driver */ static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg, struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai) /* pass DAI configurations to component driver for extra intialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, struct snd_soc_dai_driver *dai_drv) { if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load) return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai); if (tplg->comp && tplg->ops && tplg->ops->dai_load) return tplg->ops->dai_load(tplg->comp, dai_drv); return 0; } /* pass link configurations to component driver for extra intialization */ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, struct snd_soc_dai_link *link) { if (tplg->comp && tplg->ops && tplg->ops->link_load) return tplg->ops->link_load(tplg->comp, link); return 0; } Loading Loading @@ -495,18 +460,39 @@ static void remove_widget(struct snd_soc_component *comp, /* widget w is freed by soc-dapm.c */ } /* remove PCM DAI configurations */ static void remove_pcm_dai(struct snd_soc_component *comp, /* remove DAI configurations */ static void remove_dai(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { struct snd_soc_dai_driver *dai_drv = container_of(dobj, struct snd_soc_dai_driver, dobj); if (pass != SOC_TPLG_PASS_PCM_DAI) return; if (dobj->ops && dobj->ops->pcm_dai_unload) dobj->ops->pcm_dai_unload(comp, dobj); if (dobj->ops && dobj->ops->dai_unload) dobj->ops->dai_unload(comp, dobj); list_del(&dobj->list); kfree(dobj); kfree(dai_drv); } /* remove link configurations */ static void remove_link(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { struct snd_soc_dai_link *link = container_of(dobj, struct snd_soc_dai_link, dobj); if (pass != SOC_TPLG_PASS_PCM_DAI) return; if (dobj->ops && dobj->ops->link_unload) dobj->ops->link_unload(comp, dobj); list_del(&dobj->list); snd_soc_remove_dai_link(comp->card, link); kfree(link); } /* bind a kcontrol to it's IO handlers */ Loading Loading @@ -1544,18 +1530,116 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) return 0; } static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, static void set_stream_info(struct snd_soc_pcm_stream *stream, struct snd_soc_tplg_stream_caps *caps) { stream->stream_name = kstrdup(caps->name, GFP_KERNEL); stream->channels_min = caps->channels_min; stream->channels_max = caps->channels_max; stream->rates = caps->rates; stream->rate_min = caps->rate_min; stream->rate_max = caps->rate_max; stream->formats = caps->formats; } static int soc_tplg_dai_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { struct snd_soc_dai_driver *dai_drv; struct snd_soc_pcm_stream *stream; struct snd_soc_tplg_stream_caps *caps; int ret; dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); if (dai_drv == NULL) return -ENOMEM; dai_drv->name = pcm->dai_name; dai_drv->id = pcm->dai_id; if (pcm->playback) { stream = &dai_drv->playback; caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; set_stream_info(stream, caps); } if (pcm->capture) { stream = &dai_drv->capture; caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; set_stream_info(stream, caps); } /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); kfree(dai_drv); return ret; } dai_drv->dobj.index = tplg->index; dai_drv->dobj.ops = tplg->ops; dai_drv->dobj.type = SND_SOC_DOBJ_PCM; list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); /* register the DAI to the component */ return snd_soc_register_dai(tplg->comp, dai_drv); } static int soc_tplg_link_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { struct snd_soc_dai_link *link; int ret; link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL); if (link == NULL) return -ENOMEM; link->name = pcm->pcm_name; link->stream_name = pcm->pcm_name; /* pass control to component driver for optional further init */ ret = soc_tplg_dai_link_load(tplg, link); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); kfree(link); return ret; } link->dobj.index = tplg->index; link->dobj.ops = tplg->ops; link->dobj.type = SND_SOC_DOBJ_DAI_LINK; list_add(&link->dobj.list, &tplg->comp->dobj_list); snd_soc_add_dai_link(tplg->comp->card, link); return 0; } /* create a FE DAI and DAI link from the PCM object */ static int soc_tplg_pcm_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { int ret; ret = soc_tplg_dai_create(tplg, pcm); if (ret < 0) return ret; return soc_tplg_link_create(tplg, pcm); } static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { struct snd_soc_tplg_pcm_dai *pcm_dai; struct snd_soc_dobj *dobj; struct snd_soc_tplg_pcm *pcm; int count = hdr->count; int ret; int i; if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) return 0; pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; pcm = (struct snd_soc_tplg_pcm *)tplg->pos; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_pcm), count, Loading @@ -1565,31 +1649,16 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, return -EINVAL; } /* create the FE DAIs and DAI links */ for (i = 0; i < count; i++) { soc_tplg_pcm_create(tplg, pcm); pcm++; } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); if (dobj == NULL) return -ENOMEM; /* Call the platform driver call back to register the dais */ ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n"); goto err; } dobj->type = get_dobj_type(hdr, NULL); dobj->pcm_dai.count = count; dobj->pcm_dai.pd = pcm_dai; dobj->ops = tplg->ops; dobj->index = tplg->index; list_add(&dobj->list, &tplg->comp->dobj_list); return 0; err: kfree(dobj); return ret; } static int soc_tplg_manifest_load(struct soc_tplg *tplg, Loading Loading @@ -1681,9 +1750,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, case SND_SOC_TPLG_TYPE_DAPM_WIDGET: return soc_tplg_dapm_widget_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_PCM: case SND_SOC_TPLG_TYPE_DAI_LINK: case SND_SOC_TPLG_TYPE_CODEC_LINK: return soc_tplg_pcm_dai_elems_load(tplg, hdr); return soc_tplg_pcm_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_MANIFEST: return soc_tplg_manifest_load(tplg, hdr); default: Loading Loading @@ -1841,9 +1908,10 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) remove_widget(comp, dobj, pass); break; case SND_SOC_DOBJ_PCM: remove_dai(comp, dobj, pass); break; case SND_SOC_DOBJ_DAI_LINK: case SND_SOC_DOBJ_CODEC_LINK: remove_pcm_dai(comp, dobj, pass); remove_link(comp, dobj, pass); break; default: dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", Loading Loading
Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt 0 → 100644 +39 −0 Original line number Diff line number Diff line Allwinner Sony/Philips Digital Interface Format (S/PDIF) Controller The Allwinner S/PDIF audio block is a transceiver that allows the processor to receive and transmit digital audio via an coaxial cable or a fibre cable. For now only playback is supported. Required properties: - compatible : should be one of the following: - "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC - reg : Offset and length of the register set for the device. - interrupts : Contains the spdif interrupt. - dmas : Generic dma devicetree binding as described in Documentation/devicetree/bindings/dma/dma.txt. - dma-names : Two dmas have to be defined, "tx" and "rx". - clocks : Contains an entry for each entry in clock-names. - clock-names : Includes the following entries: "apb" clock for the spdif bus. "spdif" clock for spdif controller. Example: spdif: spdif@01c21000 { compatible = "allwinner,sun4i-a10-spdif"; reg = <0x01c21000 0x40>; interrupts = <13>; clocks = <&apb0_gates 1>, <&spdif_clk>; clock-names = "apb", "spdif"; dmas = <&dma 0 2>, <&dma 0 2>; dma-names = "rx", "tx"; status = "okay"; };
include/sound/soc-topology.h +10 −11 Original line number Diff line number Diff line Loading @@ -56,12 +56,6 @@ struct snd_soc_dobj_widget { unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */ }; /* dynamic PCM DAI object */ struct snd_soc_dobj_pcm_dai { struct snd_soc_tplg_pcm_dai *pd; unsigned int count; }; /* generic dynamic object - all dynamic objects belong to this struct */ struct snd_soc_dobj { enum snd_soc_dobj_type type; Loading @@ -71,7 +65,6 @@ struct snd_soc_dobj { union { struct snd_soc_dobj_control control; struct snd_soc_dobj_widget widget; struct snd_soc_dobj_pcm_dai pcm_dai; }; void *private; /* core does not touch this */ }; Loading Loading @@ -126,10 +119,16 @@ struct snd_soc_tplg_ops { int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* FE - used for any driver specific init */ int (*pcm_dai_load)(struct snd_soc_component *, struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe); int (*pcm_dai_unload)(struct snd_soc_component *, /* FE DAI - used for any driver specific init */ int (*dai_load)(struct snd_soc_component *, struct snd_soc_dai_driver *dai_drv); 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_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* callback to handle vendor bespoke data */ Loading
include/sound/soc.h +1 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ #include <sound/compress_driver.h> #include <sound/control.h> #include <sound/ac97_codec.h> #include <sound/soc-topology.h> /* * Convenience kcontrol builders Loading Loading @@ -404,6 +403,7 @@ struct snd_soc_jack_zone; struct snd_soc_jack_pin; #include <sound/soc-dapm.h> #include <sound/soc-dpcm.h> #include <sound/soc-topology.h> struct snd_soc_jack_gpio; Loading
sound/soc/codecs/wm8974.c +93 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,11 @@ #include "wm8974.h" struct wm8974_priv { unsigned int mclk; unsigned int fs; }; static const struct reg_default wm8974_reg_defaults[] = { { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 }, { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 }, Loading Loading @@ -379,6 +384,79 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return 0; } static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out, int *mclkdiv) { unsigned int ratio = 2 * f_in / f_out; if (ratio <= 2) { *mclkdiv = WM8974_MCLKDIV_1; ratio = 2; } else if (ratio == 3) { *mclkdiv = WM8974_MCLKDIV_1_5; } else if (ratio == 4) { *mclkdiv = WM8974_MCLKDIV_2; } else if (ratio <= 6) { *mclkdiv = WM8974_MCLKDIV_3; ratio = 6; } else if (ratio <= 8) { *mclkdiv = WM8974_MCLKDIV_4; ratio = 8; } else if (ratio <= 12) { *mclkdiv = WM8974_MCLKDIV_6; ratio = 12; } else if (ratio <= 16) { *mclkdiv = WM8974_MCLKDIV_8; ratio = 16; } else { *mclkdiv = WM8974_MCLKDIV_12; ratio = 24; } return f_out * ratio / 2; } static int wm8974_update_clocks(struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); unsigned int fs256; unsigned int fpll = 0; unsigned int f; int mclkdiv; if (!priv->mclk || !priv->fs) return 0; fs256 = 256 * priv->fs; f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv); if (f != priv->mclk) { /* The PLL performs best around 90MHz */ fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv); } wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll); wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv); return 0; } static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); if (dir != SND_SOC_CLOCK_IN) return -EINVAL; priv->mclk = freq; return wm8974_update_clocks(dai); } static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { Loading Loading @@ -441,8 +519,15 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f; u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1; int err; priv->fs = params_rate(params); err = wm8974_update_clocks(dai); if (err) return err; /* bit size */ switch (params_width(params)) { Loading Loading @@ -547,6 +632,7 @@ static const struct snd_soc_dai_ops wm8974_ops = { .set_fmt = wm8974_set_dai_fmt, .set_clkdiv = wm8974_set_dai_clkdiv, .set_pll = wm8974_set_dai_pll, .set_sysclk = wm8974_set_dai_sysclk, }; static struct snd_soc_dai_driver wm8974_dai = { Loading Loading @@ -606,9 +692,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { static int wm8974_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8974_priv *priv; struct regmap *regmap; int ret; priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; i2c_set_clientdata(i2c, priv); regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap); if (IS_ERR(regmap)) return PTR_ERR(regmap); Loading
sound/soc/soc-topology.c +154 −86 Original line number Diff line number Diff line Loading @@ -223,51 +223,6 @@ static int get_widget_id(int tplg_type) return -EINVAL; } static enum snd_soc_dobj_type get_dobj_mixer_type( struct snd_soc_tplg_ctl_hdr *control_hdr) { if (control_hdr == NULL) return SND_SOC_DOBJ_NONE; switch (control_hdr->ops.info) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_CTL_STROBE: return SND_SOC_DOBJ_MIXER; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: return SND_SOC_DOBJ_ENUM; case SND_SOC_TPLG_CTL_BYTES: return SND_SOC_DOBJ_BYTES; default: return SND_SOC_DOBJ_NONE; } } static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr, struct snd_soc_tplg_ctl_hdr *control_hdr) { switch (hdr->type) { case SND_SOC_TPLG_TYPE_MIXER: return get_dobj_mixer_type(control_hdr); case SND_SOC_TPLG_TYPE_DAPM_GRAPH: case SND_SOC_TPLG_TYPE_MANIFEST: return SND_SOC_DOBJ_NONE; case SND_SOC_TPLG_TYPE_DAPM_WIDGET: return SND_SOC_DOBJ_WIDGET; case SND_SOC_TPLG_TYPE_DAI_LINK: return SND_SOC_DOBJ_DAI_LINK; case SND_SOC_TPLG_TYPE_PCM: return SND_SOC_DOBJ_PCM; case SND_SOC_TPLG_TYPE_CODEC_LINK: return SND_SOC_DOBJ_CODEC_LINK; default: return SND_SOC_DOBJ_NONE; } } static inline void soc_bind_err(struct soc_tplg *tplg, struct snd_soc_tplg_ctl_hdr *hdr, int index) { Loading Loading @@ -330,12 +285,22 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, return 0; } /* pass dynamic FEs configurations to component driver */ static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg, struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai) /* pass DAI configurations to component driver for extra intialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, struct snd_soc_dai_driver *dai_drv) { if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load) return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai); if (tplg->comp && tplg->ops && tplg->ops->dai_load) return tplg->ops->dai_load(tplg->comp, dai_drv); return 0; } /* pass link configurations to component driver for extra intialization */ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, struct snd_soc_dai_link *link) { if (tplg->comp && tplg->ops && tplg->ops->link_load) return tplg->ops->link_load(tplg->comp, link); return 0; } Loading Loading @@ -495,18 +460,39 @@ static void remove_widget(struct snd_soc_component *comp, /* widget w is freed by soc-dapm.c */ } /* remove PCM DAI configurations */ static void remove_pcm_dai(struct snd_soc_component *comp, /* remove DAI configurations */ static void remove_dai(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { struct snd_soc_dai_driver *dai_drv = container_of(dobj, struct snd_soc_dai_driver, dobj); if (pass != SOC_TPLG_PASS_PCM_DAI) return; if (dobj->ops && dobj->ops->pcm_dai_unload) dobj->ops->pcm_dai_unload(comp, dobj); if (dobj->ops && dobj->ops->dai_unload) dobj->ops->dai_unload(comp, dobj); list_del(&dobj->list); kfree(dobj); kfree(dai_drv); } /* remove link configurations */ static void remove_link(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { struct snd_soc_dai_link *link = container_of(dobj, struct snd_soc_dai_link, dobj); if (pass != SOC_TPLG_PASS_PCM_DAI) return; if (dobj->ops && dobj->ops->link_unload) dobj->ops->link_unload(comp, dobj); list_del(&dobj->list); snd_soc_remove_dai_link(comp->card, link); kfree(link); } /* bind a kcontrol to it's IO handlers */ Loading Loading @@ -1544,18 +1530,116 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) return 0; } static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, static void set_stream_info(struct snd_soc_pcm_stream *stream, struct snd_soc_tplg_stream_caps *caps) { stream->stream_name = kstrdup(caps->name, GFP_KERNEL); stream->channels_min = caps->channels_min; stream->channels_max = caps->channels_max; stream->rates = caps->rates; stream->rate_min = caps->rate_min; stream->rate_max = caps->rate_max; stream->formats = caps->formats; } static int soc_tplg_dai_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { struct snd_soc_dai_driver *dai_drv; struct snd_soc_pcm_stream *stream; struct snd_soc_tplg_stream_caps *caps; int ret; dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); if (dai_drv == NULL) return -ENOMEM; dai_drv->name = pcm->dai_name; dai_drv->id = pcm->dai_id; if (pcm->playback) { stream = &dai_drv->playback; caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; set_stream_info(stream, caps); } if (pcm->capture) { stream = &dai_drv->capture; caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; set_stream_info(stream, caps); } /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); kfree(dai_drv); return ret; } dai_drv->dobj.index = tplg->index; dai_drv->dobj.ops = tplg->ops; dai_drv->dobj.type = SND_SOC_DOBJ_PCM; list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); /* register the DAI to the component */ return snd_soc_register_dai(tplg->comp, dai_drv); } static int soc_tplg_link_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { struct snd_soc_dai_link *link; int ret; link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL); if (link == NULL) return -ENOMEM; link->name = pcm->pcm_name; link->stream_name = pcm->pcm_name; /* pass control to component driver for optional further init */ ret = soc_tplg_dai_link_load(tplg, link); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); kfree(link); return ret; } link->dobj.index = tplg->index; link->dobj.ops = tplg->ops; link->dobj.type = SND_SOC_DOBJ_DAI_LINK; list_add(&link->dobj.list, &tplg->comp->dobj_list); snd_soc_add_dai_link(tplg->comp->card, link); return 0; } /* create a FE DAI and DAI link from the PCM object */ static int soc_tplg_pcm_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { int ret; ret = soc_tplg_dai_create(tplg, pcm); if (ret < 0) return ret; return soc_tplg_link_create(tplg, pcm); } static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { struct snd_soc_tplg_pcm_dai *pcm_dai; struct snd_soc_dobj *dobj; struct snd_soc_tplg_pcm *pcm; int count = hdr->count; int ret; int i; if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) return 0; pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; pcm = (struct snd_soc_tplg_pcm *)tplg->pos; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_pcm), count, Loading @@ -1565,31 +1649,16 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, return -EINVAL; } /* create the FE DAIs and DAI links */ for (i = 0; i < count; i++) { soc_tplg_pcm_create(tplg, pcm); pcm++; } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); if (dobj == NULL) return -ENOMEM; /* Call the platform driver call back to register the dais */ ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n"); goto err; } dobj->type = get_dobj_type(hdr, NULL); dobj->pcm_dai.count = count; dobj->pcm_dai.pd = pcm_dai; dobj->ops = tplg->ops; dobj->index = tplg->index; list_add(&dobj->list, &tplg->comp->dobj_list); return 0; err: kfree(dobj); return ret; } static int soc_tplg_manifest_load(struct soc_tplg *tplg, Loading Loading @@ -1681,9 +1750,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, case SND_SOC_TPLG_TYPE_DAPM_WIDGET: return soc_tplg_dapm_widget_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_PCM: case SND_SOC_TPLG_TYPE_DAI_LINK: case SND_SOC_TPLG_TYPE_CODEC_LINK: return soc_tplg_pcm_dai_elems_load(tplg, hdr); return soc_tplg_pcm_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_MANIFEST: return soc_tplg_manifest_load(tplg, hdr); default: Loading Loading @@ -1841,9 +1908,10 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) remove_widget(comp, dobj, pass); break; case SND_SOC_DOBJ_PCM: remove_dai(comp, dobj, pass); break; case SND_SOC_DOBJ_DAI_LINK: case SND_SOC_DOBJ_CODEC_LINK: remove_pcm_dai(comp, dobj, pass); remove_link(comp, dobj, pass); break; default: dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", Loading