Path: blob/master/sound/soc/rockchip/rk3399_gru_sound.c
26428 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Rockchip machine ASoC driver for boards using MAX98357A/RT5514/DA72193*4* Copyright (c) 2016, ROCKCHIP CORPORATION. All rights reserved.5*/67#include <linux/module.h>8#include <linux/platform_device.h>9#include <linux/slab.h>10#include <linux/delay.h>11#include <linux/spi/spi.h>12#include <linux/i2c.h>13#include <linux/input.h>14#include <sound/core.h>15#include <sound/jack.h>16#include <sound/pcm.h>17#include <sound/pcm_params.h>18#include <sound/soc.h>19#include "rockchip_i2s.h"20#include "../codecs/da7219.h"21#include "../codecs/rt5514.h"2223#define DRV_NAME "rk3399-gru-sound"2425#define SOUND_FS 2562627static unsigned int dmic_wakeup_delay;2829static struct snd_soc_jack rockchip_sound_jack;3031/* Headset jack detection DAPM pins */32static struct snd_soc_jack_pin rockchip_sound_jack_pins[] = {33{34.pin = "Headphones",35.mask = SND_JACK_HEADPHONE,36},37{38.pin = "Headset Mic",39.mask = SND_JACK_MICROPHONE,40},41{42.pin = "Line Out",43.mask = SND_JACK_LINEOUT,44},45};4647static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = {48SND_SOC_DAPM_HP("Headphones", NULL),49SND_SOC_DAPM_SPK("Speakers", NULL),50SND_SOC_DAPM_MIC("Headset Mic", NULL),51SND_SOC_DAPM_LINE("Line Out", NULL),52SND_SOC_DAPM_MIC("Int Mic", NULL),53SND_SOC_DAPM_LINE("HDMI", NULL),54};5556static const struct snd_kcontrol_new rockchip_controls[] = {57SOC_DAPM_PIN_SWITCH("Headphones"),58SOC_DAPM_PIN_SWITCH("Speakers"),59SOC_DAPM_PIN_SWITCH("Headset Mic"),60SOC_DAPM_PIN_SWITCH("Line Out"),61SOC_DAPM_PIN_SWITCH("Int Mic"),62SOC_DAPM_PIN_SWITCH("HDMI"),63};6465static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream,66struct snd_pcm_hw_params *params)67{68struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);69unsigned int mclk;70int ret;7172mclk = params_rate(params) * SOUND_FS;7374ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk, 0);75if (ret) {76dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",77__func__, mclk, ret);78return ret;79}8081return 0;82}8384static int rockchip_sound_rt5514_hw_params(struct snd_pcm_substream *substream,85struct snd_pcm_hw_params *params)86{87struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);88struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);89struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);90unsigned int mclk;91int ret;9293mclk = params_rate(params) * SOUND_FS;9495ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,96SND_SOC_CLOCK_OUT);97if (ret < 0) {98dev_err(rtd->card->dev, "Can't set cpu clock out %d\n", ret);99return ret;100}101102ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK,103mclk, SND_SOC_CLOCK_IN);104if (ret) {105dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",106__func__, params_rate(params) * 512, ret);107return ret;108}109110/* Wait for DMIC stable */111msleep(dmic_wakeup_delay);112113return 0;114}115116static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream,117struct snd_pcm_hw_params *params)118{119struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);120struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);121struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);122int mclk, ret;123124/* in bypass mode, the mclk has to be one of the frequencies below */125switch (params_rate(params)) {126case 8000:127case 16000:128case 24000:129case 32000:130case 48000:131case 64000:132case 96000:133mclk = 12288000;134break;135case 11025:136case 22050:137case 44100:138case 88200:139mclk = 11289600;140break;141default:142return -EINVAL;143}144145ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,146SND_SOC_CLOCK_OUT);147if (ret < 0) {148dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret);149return ret;150}151152ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,153SND_SOC_CLOCK_IN);154if (ret < 0) {155dev_err(codec_dai->dev, "Can't set codec clock in %d\n", ret);156return ret;157}158159ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);160if (ret < 0) {161dev_err(codec_dai->dev, "Can't set pll sysclk mclk %d\n", ret);162return ret;163}164165return 0;166}167168static struct snd_soc_jack cdn_dp_card_jack;169170static int rockchip_sound_cdndp_init(struct snd_soc_pcm_runtime *rtd)171{172struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;173struct snd_soc_card *card = rtd->card;174int ret;175176/* Enable jack detection. */177ret = snd_soc_card_jack_new(card, "DP Jack", SND_JACK_LINEOUT,178&cdn_dp_card_jack);179if (ret) {180dev_err(card->dev, "Can't create DP Jack %d\n", ret);181return ret;182}183184return snd_soc_component_set_jack(component, &cdn_dp_card_jack, NULL);185}186187static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)188{189struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;190struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);191int ret;192193/* We need default MCLK and PLL settings for the accessory detection */194ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12288000,195SND_SOC_CLOCK_IN);196if (ret < 0) {197dev_err(codec_dai->dev, "Init can't set codec clock in %d\n", ret);198return ret;199}200201ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);202if (ret < 0) {203dev_err(codec_dai->dev, "Init can't set pll sysclk mclk %d\n", ret);204return ret;205}206207/* Enable Headset and 4 Buttons Jack detection */208ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",209SND_JACK_HEADSET | SND_JACK_LINEOUT |210SND_JACK_BTN_0 | SND_JACK_BTN_1 |211SND_JACK_BTN_2 | SND_JACK_BTN_3,212&rockchip_sound_jack,213rockchip_sound_jack_pins,214ARRAY_SIZE(rockchip_sound_jack_pins));215216if (ret) {217dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret);218return ret;219}220221snd_jack_set_key(222rockchip_sound_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);223snd_jack_set_key(224rockchip_sound_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);225snd_jack_set_key(226rockchip_sound_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);227snd_jack_set_key(228rockchip_sound_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);229230snd_soc_component_set_jack(component, &rockchip_sound_jack, NULL);231232return 0;233}234235static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream,236struct snd_pcm_hw_params *params)237{238struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);239unsigned int mclk;240int ret;241242mclk = params_rate(params) * SOUND_FS;243244ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk, 0);245if (ret) {246dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",247__func__, mclk, ret);248return ret;249}250251/* Wait for DMIC stable */252msleep(dmic_wakeup_delay);253254return 0;255}256257static int rockchip_sound_startup(struct snd_pcm_substream *substream)258{259struct snd_pcm_runtime *runtime = substream->runtime;260261runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;262return snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,2638000, 96000);264}265266static const struct snd_soc_ops rockchip_sound_max98357a_ops = {267.startup = rockchip_sound_startup,268.hw_params = rockchip_sound_max98357a_hw_params,269};270271static const struct snd_soc_ops rockchip_sound_rt5514_ops = {272.startup = rockchip_sound_startup,273.hw_params = rockchip_sound_rt5514_hw_params,274};275276static const struct snd_soc_ops rockchip_sound_da7219_ops = {277.startup = rockchip_sound_startup,278.hw_params = rockchip_sound_da7219_hw_params,279};280281static const struct snd_soc_ops rockchip_sound_dmic_ops = {282.startup = rockchip_sound_startup,283.hw_params = rockchip_sound_dmic_hw_params,284};285286static struct snd_soc_card rockchip_sound_card = {287.name = "rk3399-gru-sound",288.owner = THIS_MODULE,289.dapm_widgets = rockchip_dapm_widgets,290.num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets),291.controls = rockchip_controls,292.num_controls = ARRAY_SIZE(rockchip_controls),293};294295enum {296DAILINK_CDNDP,297DAILINK_DA7219,298DAILINK_DMIC,299DAILINK_MAX98357A,300DAILINK_RT5514,301DAILINK_RT5514_DSP,302};303304SND_SOC_DAILINK_DEFS(cdndp,305DAILINK_COMP_ARRAY(COMP_EMPTY()),306DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "spdif-hifi")),307DAILINK_COMP_ARRAY(COMP_EMPTY()));308309SND_SOC_DAILINK_DEFS(da7219,310DAILINK_COMP_ARRAY(COMP_EMPTY()),311DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "da7219-hifi")),312DAILINK_COMP_ARRAY(COMP_EMPTY()));313314SND_SOC_DAILINK_DEFS(dmic,315DAILINK_COMP_ARRAY(COMP_EMPTY()),316DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "dmic-hifi")),317DAILINK_COMP_ARRAY(COMP_EMPTY()));318319SND_SOC_DAILINK_DEFS(max98357a,320DAILINK_COMP_ARRAY(COMP_EMPTY()),321DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),322DAILINK_COMP_ARRAY(COMP_EMPTY()));323324SND_SOC_DAILINK_DEFS(rt5514,325DAILINK_COMP_ARRAY(COMP_EMPTY()),326DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5514-aif1")),327DAILINK_COMP_ARRAY(COMP_EMPTY()));328329SND_SOC_DAILINK_DEFS(rt5514_dsp,330DAILINK_COMP_ARRAY(COMP_EMPTY()),331DAILINK_COMP_ARRAY(COMP_DUMMY()),332DAILINK_COMP_ARRAY(COMP_EMPTY()));333334static const struct snd_soc_dai_link rockchip_dais[] = {335[DAILINK_CDNDP] = {336.name = "DP",337.stream_name = "DP PCM",338.init = rockchip_sound_cdndp_init,339.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |340SND_SOC_DAIFMT_CBC_CFC,341SND_SOC_DAILINK_REG(cdndp),342},343[DAILINK_DA7219] = {344.name = "DA7219",345.stream_name = "DA7219 PCM",346.init = rockchip_sound_da7219_init,347.ops = &rockchip_sound_da7219_ops,348/* set da7219 as slave */349.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |350SND_SOC_DAIFMT_CBC_CFC,351SND_SOC_DAILINK_REG(da7219),352},353[DAILINK_DMIC] = {354.name = "DMIC",355.stream_name = "DMIC PCM",356.ops = &rockchip_sound_dmic_ops,357.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |358SND_SOC_DAIFMT_CBC_CFC,359SND_SOC_DAILINK_REG(dmic),360},361[DAILINK_MAX98357A] = {362.name = "MAX98357A",363.stream_name = "MAX98357A PCM",364.ops = &rockchip_sound_max98357a_ops,365/* set max98357a as slave */366.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |367SND_SOC_DAIFMT_CBC_CFC,368SND_SOC_DAILINK_REG(max98357a),369},370[DAILINK_RT5514] = {371.name = "RT5514",372.stream_name = "RT5514 PCM",373.ops = &rockchip_sound_rt5514_ops,374/* set rt5514 as slave */375.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |376SND_SOC_DAIFMT_CBC_CFC,377SND_SOC_DAILINK_REG(rt5514),378},379/* RT5514 DSP for voice wakeup via spi bus */380[DAILINK_RT5514_DSP] = {381.name = "RT5514 DSP",382.stream_name = "Wake on Voice",383SND_SOC_DAILINK_REG(rt5514_dsp),384},385};386387static const struct snd_soc_dapm_route rockchip_sound_cdndp_routes[] = {388/* Output */389{"HDMI", NULL, "TX"},390};391392static const struct snd_soc_dapm_route rockchip_sound_da7219_routes[] = {393/* Output */394{"Headphones", NULL, "HPL"},395{"Headphones", NULL, "HPR"},396397/* Input */398{"MIC", NULL, "Headset Mic"},399};400401static const struct snd_soc_dapm_route rockchip_sound_dmic_routes[] = {402/* Input */403{"DMic", NULL, "Int Mic"},404};405406static const struct snd_soc_dapm_route rockchip_sound_max98357a_routes[] = {407/* Output */408{"Speakers", NULL, "Speaker"},409};410411static const struct snd_soc_dapm_route rockchip_sound_rt5514_routes[] = {412/* Input */413{"DMIC1L", NULL, "Int Mic"},414{"DMIC1R", NULL, "Int Mic"},415};416417struct rockchip_sound_route {418const struct snd_soc_dapm_route *routes;419int num_routes;420};421422static const struct rockchip_sound_route rockchip_routes[] = {423[DAILINK_CDNDP] = {424.routes = rockchip_sound_cdndp_routes,425.num_routes = ARRAY_SIZE(rockchip_sound_cdndp_routes),426},427[DAILINK_DA7219] = {428.routes = rockchip_sound_da7219_routes,429.num_routes = ARRAY_SIZE(rockchip_sound_da7219_routes),430},431[DAILINK_DMIC] = {432.routes = rockchip_sound_dmic_routes,433.num_routes = ARRAY_SIZE(rockchip_sound_dmic_routes),434},435[DAILINK_MAX98357A] = {436.routes = rockchip_sound_max98357a_routes,437.num_routes = ARRAY_SIZE(rockchip_sound_max98357a_routes),438},439[DAILINK_RT5514] = {440.routes = rockchip_sound_rt5514_routes,441.num_routes = ARRAY_SIZE(rockchip_sound_rt5514_routes),442},443[DAILINK_RT5514_DSP] = {},444};445446struct dailink_match_data {447const char *compatible;448const struct bus_type *bus_type;449};450451static const struct dailink_match_data dailink_match[] = {452[DAILINK_CDNDP] = {453.compatible = "rockchip,rk3399-cdn-dp",454},455[DAILINK_DA7219] = {456.compatible = "dlg,da7219",457},458[DAILINK_DMIC] = {459.compatible = "dmic-codec",460},461[DAILINK_MAX98357A] = {462.compatible = "maxim,max98357a",463},464[DAILINK_RT5514] = {465.compatible = "realtek,rt5514",466.bus_type = &i2c_bus_type,467},468[DAILINK_RT5514_DSP] = {469.compatible = "realtek,rt5514",470.bus_type = &spi_bus_type,471},472};473474static int rockchip_sound_codec_node_match(struct device_node *np_codec)475{476struct device *dev;477int i;478479for (i = 0; i < ARRAY_SIZE(dailink_match); i++) {480if (!of_device_is_compatible(np_codec,481dailink_match[i].compatible))482continue;483484if (dailink_match[i].bus_type) {485dev = bus_find_device_by_of_node(dailink_match[i].bus_type,486np_codec);487if (!dev)488continue;489put_device(dev);490}491492return i;493}494return -1;495}496497static int rockchip_sound_of_parse_dais(struct device *dev,498struct snd_soc_card *card)499{500struct device_node *np_cpu, *np_cpu0, *np_cpu1;501struct device_node *np_codec;502struct snd_soc_dai_link *dai;503struct snd_soc_dapm_route *routes;504int i, index;505int num_routes;506507card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais),508GFP_KERNEL);509if (!card->dai_link)510return -ENOMEM;511512num_routes = 0;513for (i = 0; i < ARRAY_SIZE(rockchip_routes); i++)514num_routes += rockchip_routes[i].num_routes;515routes = devm_kcalloc(dev, num_routes, sizeof(*routes),516GFP_KERNEL);517if (!routes)518return -ENOMEM;519card->dapm_routes = routes;520521np_cpu0 = of_parse_phandle(dev->of_node, "rockchip,cpu", 0);522np_cpu1 = of_parse_phandle(dev->of_node, "rockchip,cpu", 1);523524card->num_dapm_routes = 0;525card->num_links = 0;526for (i = 0; i < ARRAY_SIZE(rockchip_dais); i++) {527np_codec = of_parse_phandle(dev->of_node,528"rockchip,codec", i);529if (!np_codec)530break;531532if (!of_device_is_available(np_codec))533continue;534535index = rockchip_sound_codec_node_match(np_codec);536if (index < 0)537continue;538539switch (index) {540case DAILINK_CDNDP:541np_cpu = np_cpu1;542break;543case DAILINK_RT5514_DSP:544np_cpu = np_codec;545break;546default:547np_cpu = np_cpu0;548break;549}550551if (!np_cpu) {552dev_err(dev, "Missing 'rockchip,cpu' for %s\n",553rockchip_dais[index].name);554return -EINVAL;555}556557dai = &card->dai_link[card->num_links++];558*dai = rockchip_dais[index];559560if (!dai->codecs->name)561dai->codecs->of_node = np_codec;562dai->platforms->of_node = np_cpu;563dai->cpus->of_node = np_cpu;564565if (card->num_dapm_routes + rockchip_routes[index].num_routes >566num_routes) {567dev_err(dev, "Too many routes\n");568return -EINVAL;569}570571memcpy(routes + card->num_dapm_routes,572rockchip_routes[index].routes,573rockchip_routes[index].num_routes * sizeof(*routes));574card->num_dapm_routes += rockchip_routes[index].num_routes;575}576577return 0;578}579580static int rockchip_sound_probe(struct platform_device *pdev)581{582struct snd_soc_card *card = &rockchip_sound_card;583int ret;584585ret = rockchip_sound_of_parse_dais(&pdev->dev, card);586if (ret < 0) {587dev_err(&pdev->dev, "Failed to parse dais: %d\n", ret);588return ret;589}590591/* Set DMIC wakeup delay */592ret = device_property_read_u32(&pdev->dev, "dmic-wakeup-delay-ms",593&dmic_wakeup_delay);594if (ret) {595dmic_wakeup_delay = 0;596dev_dbg(&pdev->dev,597"no optional property 'dmic-wakeup-delay-ms' found, default: no delay\n");598}599600card->dev = &pdev->dev;601return devm_snd_soc_register_card(&pdev->dev, card);602}603604static const struct of_device_id rockchip_sound_of_match[] = {605{ .compatible = "rockchip,rk3399-gru-sound", },606{},607};608609static struct platform_driver rockchip_sound_driver = {610.probe = rockchip_sound_probe,611.driver = {612.name = DRV_NAME,613.of_match_table = rockchip_sound_of_match,614#ifdef CONFIG_PM615.pm = &snd_soc_pm_ops,616#endif617},618};619620module_platform_driver(rockchip_sound_driver);621622MODULE_AUTHOR("Xing Zheng <[email protected]>");623MODULE_DESCRIPTION("Rockchip ASoC Machine Driver");624MODULE_LICENSE("GPL v2");625MODULE_ALIAS("platform:" DRV_NAME);626MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);627628629