diff --git a/sound/soc/fsl/imx-pcm512x.c b/sound/soc/fsl/imx-pcm512x.c index 323dbff8be654bfc735cdaf0e329bab826ada6a3..e3914533b04840d63f49b20117fac4ddad321446 100644 --- a/sound/soc/fsl/imx-pcm512x.c +++ b/sound/soc/fsl/imx-pcm512x.c @@ -15,12 +15,12 @@ #include <linux/slab.h> #include <linux/gpio/consumer.h> #include <linux/of_device.h> -#include <linux/i2c.h> #include <linux/of_gpio.h> #include <linux/clk.h> #include <sound/soc.h> #include <sound/pcm_params.h> #include <sound/pcm.h> +#include <sound/simple_card.h> #include <sound/soc-dapm.h> #include "fsl_sai.h" @@ -30,14 +30,8 @@ #define DAC_CLK_EXT_48K 24576000UL struct imx_pcm512x_data { - struct snd_soc_dai_link dai_link[1]; - struct snd_soc_card card; - struct snd_soc_codec_conf *codec_conf; + struct asoc_simple_priv *priv; struct gpio_desc *mute_gpio; - int num_codec_conf; - unsigned int slots; - unsigned int slot_width; - unsigned int daifmt; bool dac_sclk; bool dac_pluspro; bool dac_led_status; @@ -122,7 +116,7 @@ static int imx_pcm512x_dai_init(struct snd_soc_pcm_runtime *rtd) if (data->dac_gain_limit) { ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); if (ret) - dev_warn(card->dev, "fail to set volume limit"); + dev_warn(card->dev, "failed to set volume limit\n"); } return 0; @@ -163,20 +157,21 @@ static int imx_pcm512x_set_bias_level(struct snd_soc_card *card, static unsigned long pcm512x_get_mclk_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct imx_pcm512x_data *data = snd_soc_card_get_drvdata(rtd->card); - unsigned int width = data->slots * data->slot_width; + unsigned int channels = params_channels(params); + unsigned int width = params_width(params); unsigned int rate = params_rate(params); + unsigned int ratio = channels * width; int i; for (i = 0; i < ARRAY_SIZE(fs_map); i++) { if (rate >= fs_map[i].rmin && rate <= fs_map[i].rmax) { - width = max(width, fs_map[i].wmin); - width = min(width, fs_map[i].wmax); + ratio = max(ratio, fs_map[i].wmin); + ratio = min(ratio, fs_map[i].wmax); /* Adjust SAI bclk:mclk ratio */ - width *= data->one2one_ratio ? 1 : 2; - - return rate * width; + ratio *= data->one2one_ratio ? 1 : 2; + return rate * ratio; } } @@ -187,22 +182,21 @@ static unsigned long pcm512x_get_mclk_rate(struct snd_pcm_substream *substream, static int imx_pcm512x_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_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_component *comp = codec_dai->component; struct snd_soc_card *card = rtd->card; struct imx_pcm512x_data *data = snd_soc_card_get_drvdata(card); - unsigned int sample_rate = params_rate(params); + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + unsigned int width = params_width(params); unsigned long mclk_freq; int ret, i; - data->slots = 2; - data->slot_width = params_physical_width(params); - /* set MCLK freq */ if (data->dac_pluspro && data->dac_sclk) { - if (do_div(sample_rate, 8000)) { + if (do_div(rate, 8000)) { mclk_freq = DAC_CLK_EXT_44K; imx_pcm512x_select_ext_clk(comp, DAC_CLK_EXT_44EN); ret = snd_soc_dai_set_sysclk(codec_dai, @@ -225,46 +219,24 @@ static int imx_pcm512x_hw_params(struct snd_pcm_substream *substream, mclk_freq, ret); } - ret = snd_soc_dai_set_fmt(cpu_dai, data->daifmt); - if (ret) { - dev_err(card->dev, "failed to set cpu dai fmt: %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_bclk_ratio(cpu_dai, data->slots * data->slot_width); - if (ret) { - dev_err(card->dev, "failed to set cpu dai bclk ratio\n"); - return ret; - } - - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i); - - ret = snd_soc_dai_set_fmt(codec_dai, data->daifmt); - if (ret) { - dev_err(card->dev, "failed to set codec dai[%d] fmt: %d\n", - i, ret); - return ret; - } - - ret = snd_soc_dai_set_bclk_ratio(codec_dai, - data->slots * data->slot_width); + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_bclk_ratio(codec_dai, (channels * width)); if (ret) { - dev_err(card->dev, "failed to set cpu dai bclk ratio\n"); + dev_err(card->dev, "failed to set codec dai bclk ratio\n"); return ret; } } - dev_dbg(card->dev, "mclk_freq: %lu; bclk_ratio: %d\n", mclk_freq, - data->slots * data->slot_width); + dev_dbg(card->dev, "mclk_freq: %lu, bclk ratio: %u\n", + mclk_freq, (channels * width)); - return ret; + return 0; } static int imx_pcm512x_startup(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct imx_pcm512x_data *data = snd_soc_card_get_drvdata(card); static struct snd_pcm_hw_constraint_list constraint_rates; @@ -328,180 +300,259 @@ static struct snd_soc_ops imx_pcm512x_ops = { .shutdown = imx_pcm512x_shutdown, }; -SND_SOC_DAILINK_DEFS(hifi, - DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "pcm512x-hifi")), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -static struct snd_soc_dai_link_component pcm512x_codecs[] = { - { - /* Playback */ - .dai_name = "pcm512x-hifi", - }, -}; - -static struct snd_soc_dai_link imx_pcm512x_dai[] = { - { - .name = "pcm512x-audio", - .stream_name = "audio", - .codecs = pcm512x_codecs, - .num_codecs = 1, - .ops = &imx_pcm512x_ops, - .init = imx_pcm512x_dai_init, - .ignore_pmdown_time = 1, - SND_SOC_DAILINK_REG(hifi), - }, -}; - -static int imx_pcm512x_probe(struct platform_device *pdev) +static int imx_asoc_card_parse_dt(struct snd_soc_card *card, + struct asoc_simple_priv *priv) { - struct device_node *bitclkmaster, *framemaster = NULL; - struct device_node *cpu_np, *codec_np = NULL; - struct device_node *np = pdev->dev.of_node; - struct platform_device *cpu_pdev = NULL; - struct snd_soc_dai_link_component *comp; - struct imx_pcm512x_data *data; - struct i2c_client *codec_dev; - int ret; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + struct device_node *np, *cpu_np, *codec_np = NULL; + struct snd_soc_dai_link_component *dlc; + struct asoc_simple_dai *simple_dai; + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + struct of_phandle_args args; + int ret, num_links; + + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(dev, "failed to find card model name\n"); + return ret; + } - comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL); - if (!comp) - return -ENOMEM; + if (of_property_read_bool(dev->of_node, "audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) { + dev_err(dev, "failed to parse audio-routing\n"); + return ret; + } + } - cpu_np = of_parse_phandle(np, "audio-cpu", 0); - if (!cpu_np) { - dev_err(&pdev->dev, "audio dai phandle missing or invalid\n"); - ret = -EINVAL; - goto fail; + if (of_property_read_bool(dev->of_node, "audio-widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(card, + "audio-widgets"); + if (ret) { + dev_err(dev, "failed to parse audio-widgets\n"); + return ret; + } } - codec_np = of_parse_phandle(np, "audio-codec", 0); - if (!codec_np) { - dev_err(&pdev->dev, "audio codec phandle missing or invalid\n"); - ret = -EINVAL; - goto fail; + if (of_property_read_bool(dev->of_node, "aux-devs")) { + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + if (ret) { + dev_err(dev, "failed to parse aux devs\n"); + return ret; + } } - cpu_pdev = of_find_device_by_node(cpu_np); - if (!cpu_pdev) { - dev_err(&pdev->dev, "failed to find SAI platform device\n"); - ret = -EINVAL; - goto fail; + num_links = of_get_child_count(dev->of_node); + + card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL); + if (!card->dai_link) { + dev_err(dev, "failed to allocate memory for dai_link\n"); + return -ENOMEM; } - codec_dev = of_find_i2c_device_by_node(codec_np); - if (!codec_dev || !codec_dev->dev.driver) { - dev_err(&pdev->dev, "failed to find codec device\n"); - ret = -EPROBE_DEFER; - goto fail; + simple_dai = devm_kcalloc(dev, num_links, sizeof(*simple_dai), GFP_KERNEL); + if (!simple_dai) { + dev_err(dev, "failed to allocate memory for simple_dai\n"); + return -ENOMEM; } - data->dac_gain_limit = of_property_read_bool(np, "dac,24db_digital_gain"); - data->dac_auto_mute = of_property_read_bool(np, "dac,auto_mute_amp"); - data->dac_gpio_unmute = of_property_read_bool(np, "dac,unmute_amp"); - data->dac_led_status = of_property_read_bool(np, "dac,led_status"); - data->one2one_ratio = true; + link = card->dai_link; + card->num_links = num_links; + /* pupulate dai links */ + for_each_child_of_node(dev->of_node, np) { + dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL); + if (!dlc) { + dev_err(dev, "failed to allocate memory for dlc\n"); + ret = -ENOMEM; + goto err_fail; + } - if (data->dac_auto_mute || data->dac_gpio_unmute) { - data->mute_gpio = devm_gpiod_get_optional(&pdev->dev, - "mute-amp", GPIOD_OUT_LOW); - if (IS_ERR(data->mute_gpio)) { - dev_err(&pdev->dev, "failed to get mute amp gpio\n"); - ret = PTR_ERR(data->mute_gpio); + link->cpus = &dlc[0]; + link->platforms = &dlc[1]; + link->num_cpus = 1; + link->num_platforms = 1; + + if (of_property_read_bool(np, "link-name")) { + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(dev, "failed to get dai_link name\n"); + goto fail; + } + } + + cpu_np = of_get_child_by_name(np, "cpu"); + if (!cpu_np) { + dev_err(dev, "failed to get cpu phandle missing or invalid"); + ret = -EINVAL; goto fail; } - } - if (data->dac_auto_mute && data->dac_gpio_unmute) - data->card.set_bias_level = imx_pcm512x_set_bias_level; + ret = of_parse_phandle_with_args(cpu_np, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) { + dev_err(dev, "failed to get cpu sound-dais\n"); + goto fail; + } - platform_set_drvdata(pdev, &data->card); - snd_soc_card_set_drvdata(&data->card, data); + ret = snd_soc_of_get_dai_name(cpu_np, &link->cpus->dai_name); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get cpu dai name\n"); + goto fail; + } - memcpy(data->dai_link, imx_pcm512x_dai, - sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(data->dai_link)); + link->cpus->of_node = args.np; + link->id = args.args[0]; + link->platforms->of_node = link->cpus->of_node; - data->card.owner = THIS_MODULE; - data->card.dev = &pdev->dev; - data->card.dai_link = data->dai_link; - data->card.num_links = 1; + codec_np = of_get_child_by_name(np, "codec"); + if (!codec_np) { + dev_err(dev, "failed to get codec phandle missing or invalid\n"); + goto fail; + } - ret = snd_soc_of_parse_card_name(&data->card, "model"); - if (ret) { - dev_err(&pdev->dev, "failed to find card model name\n"); - goto fail; - } + if (codec_np) { + ret = snd_soc_of_get_dai_link_codecs(dev, codec_np, link); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get codec dais\n"); + goto fail; + } + } else { + dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); + if (!dlc) { + dev_err(dev, "failed to allocate memory for dlc\n"); + ret = -ENOMEM; + goto err_fail; + } + + link->codecs = dlc; + link->num_codecs = 1; + + link->codecs->dai_name = "snd-soc-dummy-dai"; + link->codecs->name = "snd-soc-dummy"; + } - if (of_property_read_bool(np, "audio-routing")) { - ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); + ret = asoc_simple_parse_daifmt(dev, np, codec_np, NULL, + &link->dai_fmt); if (ret) { - dev_err(&pdev->dev, "failed to parse audio-routing\n"); - goto fail; + dev_warn(dev, "failed to parse dai format\n"); + link->dai_fmt = SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_I2S; } - } - if (of_property_read_bool(np, "audio-widgets")) { - ret = snd_soc_of_parse_audio_simple_widgets(&data->card, "audio-widgets"); + ret = asoc_simple_parse_tdm(np, simple_dai); if (ret) { - dev_err(&pdev->dev, "failed to parse audio-widgets\n"); - goto fail; + dev_err(dev, "failed to parse dai tdm\n"); } - } - snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkmaster, &framemaster); - data->daifmt = snd_soc_daifmt_parse_format(np, NULL); - - if (codec_np == bitclkmaster) - data->daifmt |= (codec_np == framemaster) ? - SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; - else - data->daifmt |= (codec_np == framemaster) ? - SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - - if (!bitclkmaster) - of_node_put(bitclkmaster); - if (!framemaster) - of_node_put(framemaster); - - if (of_property_read_bool(codec_np, "clocks")) - data->dac_sclk = true; - - data->dai_link[0].cpus = &comp[0]; - data->dai_link[0].num_cpus = 1; - data->dai_link[0].codecs = &comp[1]; - data->dai_link[0].num_codecs = 1; - data->dai_link[0].platforms = &comp[2]; - data->dai_link[0].num_platforms = 1; - - data->dai_link[0].cpus->of_node = cpu_np; - data->dai_link[0].platforms->of_node = cpu_np; - data->dai_link[0].codecs->of_node = codec_np; - data->dai_link[0].codecs->dai_name = "pcm512x-hifi"; - data->dai_link[0].dai_fmt = data->daifmt; - - ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - goto fail; + link->stream_name = link->name; + link->ignore_pmdown_time = 1; + simple_dai++; + link++; + + of_node_put(cpu_np); + of_node_put(codec_np); } - if (data->dac_gpio_unmute && data->dac_auto_mute) - gpiod_set_value_cansleep(data->mute_gpio, 1); + return 0; - ret = 0; fail: if (cpu_np) of_node_put(cpu_np); if (codec_np) of_node_put(codec_np); +err_fail: + if (np) + of_node_put(np); return ret; } +static int imx_pcm512x_parse_dt(struct imx_pcm512x_data *data) +{ + struct snd_soc_card *card = &data->priv->snd_card; + struct device *dev = card->dev; + struct device_node *np = dev->of_node; + int ret; + + /* Multiple dais */ + ret = imx_asoc_card_parse_dt(card, data->priv); + if (ret) + return ret; + + data->dac_gain_limit = + of_property_read_bool(np, "dac,24db_digital_gain"); + data->dac_auto_mute = + of_property_read_bool(np, "dac,auto_mute_amp"); + data->dac_gpio_unmute = + of_property_read_bool(np, "dac,unmute_amp"); + data->dac_led_status = + of_property_read_bool(np, "dac,led_status"); + data->dac_sclk = + of_property_read_bool(np, "dac,sclk"); + + return 0; +} + +static int imx_pcm512x_probe(struct platform_device *pdev) +{ + struct asoc_simple_priv *priv; + struct imx_pcm512x_data *data; + struct snd_soc_card *card; + int ret, i; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!data->priv) + return -ENOMEM; + + card = &data->priv->snd_card; + dev_set_drvdata(&pdev->dev, &data); + snd_soc_card_set_drvdata(card, data); + + card->owner = THIS_MODULE; + card->dev = &pdev->dev; + + ret = imx_pcm512x_parse_dt(data); + if (ret) { + dev_err(&pdev->dev, "failed to parse sound card dts\n"); + return ret; + } + + for (i = 0; i < card->num_links; i++) { + card->dai_link->ops = &imx_pcm512x_ops; + card->dai_link->init = &imx_pcm512x_dai_init; + } + + if (data->dac_auto_mute || data->dac_gpio_unmute) { + data->mute_gpio = devm_gpiod_get_optional(&pdev->dev, + "mute-amp", GPIOD_OUT_LOW); + if (IS_ERR(data->mute_gpio)) { + dev_err(&pdev->dev, "failed to get mute amp gpio\n"); + return PTR_ERR(data->mute_gpio); + } + } + + if (data->dac_auto_mute && data->dac_gpio_unmute) + card->set_bias_level = imx_pcm512x_set_bias_level; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, "failed to register snd card\n"); + return ret; + } + + if (data->dac_gpio_unmute && data->dac_auto_mute) + gpiod_set_value_cansleep(data->mute_gpio, 1); + + return 0; +} + static int imx_pcm512x_remove(struct platform_device *pdev) { struct imx_pcm512x_data *data = platform_get_drvdata(pdev); @@ -509,7 +560,7 @@ static int imx_pcm512x_remove(struct platform_device *pdev) if (data->mute_gpio) gpiod_set_value_cansleep(data->mute_gpio, 0); - return snd_soc_unregister_card(&data->card); + return snd_soc_unregister_card(&data->priv->snd_card); } static const struct of_device_id imx_pcm512x_dt_ids[] = {