470 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Rockchip machine ASoC driver for boards using a MAX90809 CODEC.4 *5 * Copyright (c) 2014, ROCKCHIP CORPORATION. All rights reserved.6 */7 8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11#include <linux/slab.h>12#include <sound/core.h>13#include <sound/jack.h>14#include <sound/pcm.h>15#include <sound/pcm_params.h>16#include <sound/soc.h>17 18#include "rockchip_i2s.h"19#include "../codecs/ts3a227e.h"20 21#define DRV_NAME "rockchip-snd-max98090"22 23static struct snd_soc_jack headset_jack;24 25/* Headset jack detection DAPM pins */26static struct snd_soc_jack_pin headset_jack_pins[] = {27 {28 .pin = "Headphone",29 .mask = SND_JACK_HEADPHONE,30 },31 {32 .pin = "Headset Mic",33 .mask = SND_JACK_MICROPHONE,34 },35 36};37 38#define RK_MAX98090_WIDGETS \39 SND_SOC_DAPM_HP("Headphone", NULL), \40 SND_SOC_DAPM_MIC("Headset Mic", NULL), \41 SND_SOC_DAPM_MIC("Int Mic", NULL), \42 SND_SOC_DAPM_SPK("Speaker", NULL)43 44#define RK_HDMI_WIDGETS \45 SND_SOC_DAPM_LINE("HDMI", NULL)46 47static const struct snd_soc_dapm_widget rk_max98090_dapm_widgets[] = {48 RK_MAX98090_WIDGETS,49};50 51static const struct snd_soc_dapm_widget rk_hdmi_dapm_widgets[] = {52 RK_HDMI_WIDGETS,53};54 55static const struct snd_soc_dapm_widget rk_max98090_hdmi_dapm_widgets[] = {56 RK_MAX98090_WIDGETS,57 RK_HDMI_WIDGETS,58};59 60#define RK_MAX98090_AUDIO_MAP \61 {"IN34", NULL, "Headset Mic"}, \62 {"Headset Mic", NULL, "MICBIAS"}, \63 {"DMICL", NULL, "Int Mic"}, \64 {"Headphone", NULL, "HPL"}, \65 {"Headphone", NULL, "HPR"}, \66 {"Speaker", NULL, "SPKL"}, \67 {"Speaker", NULL, "SPKR"}68 69#define RK_HDMI_AUDIO_MAP \70 {"HDMI", NULL, "TX"}71 72static const struct snd_soc_dapm_route rk_max98090_audio_map[] = {73 RK_MAX98090_AUDIO_MAP,74};75 76static const struct snd_soc_dapm_route rk_hdmi_audio_map[] = {77 RK_HDMI_AUDIO_MAP,78};79 80static const struct snd_soc_dapm_route rk_max98090_hdmi_audio_map[] = {81 RK_MAX98090_AUDIO_MAP,82 RK_HDMI_AUDIO_MAP,83};84 85#define RK_MAX98090_CONTROLS \86 SOC_DAPM_PIN_SWITCH("Headphone"), \87 SOC_DAPM_PIN_SWITCH("Headset Mic"), \88 SOC_DAPM_PIN_SWITCH("Int Mic"), \89 SOC_DAPM_PIN_SWITCH("Speaker")90 91#define RK_HDMI_CONTROLS \92 SOC_DAPM_PIN_SWITCH("HDMI")93 94static const struct snd_kcontrol_new rk_max98090_controls[] = {95 RK_MAX98090_CONTROLS,96};97 98static const struct snd_kcontrol_new rk_hdmi_controls[] = {99 RK_HDMI_CONTROLS,100};101 102static const struct snd_kcontrol_new rk_max98090_hdmi_controls[] = {103 RK_MAX98090_CONTROLS,104 RK_HDMI_CONTROLS,105};106 107static int rk_jack_event(struct notifier_block *nb, unsigned long event,108 void *data)109{110 struct snd_soc_jack *jack = (struct snd_soc_jack *)data;111 struct snd_soc_dapm_context *dapm = &jack->card->dapm;112 113 if (event & SND_JACK_MICROPHONE) {114 snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");115 snd_soc_dapm_force_enable_pin(dapm, "SHDN");116 } else {117 snd_soc_dapm_disable_pin(dapm, "MICBIAS");118 snd_soc_dapm_disable_pin(dapm, "SHDN");119 }120 121 snd_soc_dapm_sync(dapm);122 123 return 0;124}125 126static struct notifier_block rk_jack_nb = {127 .notifier_call = rk_jack_event,128};129 130static int rk_init(struct snd_soc_pcm_runtime *runtime)131{132 /*133 * The jack has already been created in the rk_98090_headset_init()134 * function.135 */136 snd_soc_jack_notifier_register(&headset_jack, &rk_jack_nb);137 138 return 0;139}140 141static int rk_aif1_hw_params(struct snd_pcm_substream *substream,142 struct snd_pcm_hw_params *params)143{144 int ret = 0;145 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);146 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);147 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);148 int mclk;149 150 switch (params_rate(params)) {151 case 8000:152 case 16000:153 case 24000:154 case 32000:155 case 48000:156 case 64000:157 case 96000:158 mclk = 12288000;159 break;160 case 11025:161 case 22050:162 case 44100:163 case 88200:164 mclk = 11289600;165 break;166 default:167 return -EINVAL;168 }169 170 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,171 SND_SOC_CLOCK_OUT);172 if (ret) {173 dev_err(cpu_dai->dev, "Can't set cpu dai clock %d\n", ret);174 return ret;175 }176 177 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,178 SND_SOC_CLOCK_IN);179 180 /* HDMI codec dai does not need to set sysclk. */181 if (!strcmp(rtd->dai_link->name, "HDMI"))182 return 0;183 184 if (ret) {185 dev_err(codec_dai->dev, "Can't set codec dai clock %d\n", ret);186 return ret;187 }188 189 return ret;190}191 192static int rk_aif1_startup(struct snd_pcm_substream *substream)193{194 /*195 * Set period size to 240 because pl330 has issue196 * dealing with larger period in stress testing.197 */198 return snd_pcm_hw_constraint_minmax(substream->runtime,199 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 240, 240);200}201 202static const struct snd_soc_ops rk_aif1_ops = {203 .hw_params = rk_aif1_hw_params,204 .startup = rk_aif1_startup,205};206 207SND_SOC_DAILINK_DEFS(analog,208 DAILINK_COMP_ARRAY(COMP_EMPTY()),209 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),210 DAILINK_COMP_ARRAY(COMP_EMPTY()));211 212SND_SOC_DAILINK_DEFS(hdmi,213 DAILINK_COMP_ARRAY(COMP_EMPTY()),214 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),215 DAILINK_COMP_ARRAY(COMP_EMPTY()));216 217enum {218 DAILINK_MAX98090,219 DAILINK_HDMI,220};221 222static struct snd_soc_jack rk_hdmi_jack;223 224static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime)225{226 struct snd_soc_card *card = runtime->card;227 struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;228 int ret;229 230 /* enable jack detection */231 ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,232 &rk_hdmi_jack);233 if (ret) {234 dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);235 return ret;236 }237 238 return snd_soc_component_set_jack(component, &rk_hdmi_jack, NULL);239}240 241/* max98090 dai_link */242static struct snd_soc_dai_link rk_max98090_dailinks[] = {243 {244 .name = "max98090",245 .stream_name = "Analog",246 .init = rk_init,247 .ops = &rk_aif1_ops,248 /* set max98090 as slave */249 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |250 SND_SOC_DAIFMT_CBS_CFS,251 SND_SOC_DAILINK_REG(analog),252 },253};254 255/* HDMI codec dai_link */256static struct snd_soc_dai_link rk_hdmi_dailinks[] = {257 {258 .name = "HDMI",259 .stream_name = "HDMI",260 .init = rk_hdmi_init,261 .ops = &rk_aif1_ops,262 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |263 SND_SOC_DAIFMT_CBS_CFS,264 SND_SOC_DAILINK_REG(hdmi),265 }266};267 268/* max98090 and HDMI codec dai_link */269static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = {270 [DAILINK_MAX98090] = {271 .name = "max98090",272 .stream_name = "Analog",273 .init = rk_init,274 .ops = &rk_aif1_ops,275 /* set max98090 as slave */276 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |277 SND_SOC_DAIFMT_CBS_CFS,278 SND_SOC_DAILINK_REG(analog),279 },280 [DAILINK_HDMI] = {281 .name = "HDMI",282 .stream_name = "HDMI",283 .init = rk_hdmi_init,284 .ops = &rk_aif1_ops,285 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |286 SND_SOC_DAIFMT_CBS_CFS,287 SND_SOC_DAILINK_REG(hdmi),288 }289};290 291static int rk_98090_headset_init(struct snd_soc_component *component);292 293static struct snd_soc_aux_dev rk_98090_headset_dev = {294 .dlc = COMP_EMPTY(),295 .init = rk_98090_headset_init,296};297 298static struct snd_soc_card rockchip_max98090_card = {299 .name = "ROCKCHIP-I2S",300 .owner = THIS_MODULE,301 .dai_link = rk_max98090_dailinks,302 .num_links = ARRAY_SIZE(rk_max98090_dailinks),303 .aux_dev = &rk_98090_headset_dev,304 .num_aux_devs = 1,305 .dapm_widgets = rk_max98090_dapm_widgets,306 .num_dapm_widgets = ARRAY_SIZE(rk_max98090_dapm_widgets),307 .dapm_routes = rk_max98090_audio_map,308 .num_dapm_routes = ARRAY_SIZE(rk_max98090_audio_map),309 .controls = rk_max98090_controls,310 .num_controls = ARRAY_SIZE(rk_max98090_controls),311};312 313static struct snd_soc_card rockchip_hdmi_card = {314 .name = "ROCKCHIP-HDMI",315 .owner = THIS_MODULE,316 .dai_link = rk_hdmi_dailinks,317 .num_links = ARRAY_SIZE(rk_hdmi_dailinks),318 .dapm_widgets = rk_hdmi_dapm_widgets,319 .num_dapm_widgets = ARRAY_SIZE(rk_hdmi_dapm_widgets),320 .dapm_routes = rk_hdmi_audio_map,321 .num_dapm_routes = ARRAY_SIZE(rk_hdmi_audio_map),322 .controls = rk_hdmi_controls,323 .num_controls = ARRAY_SIZE(rk_hdmi_controls),324};325 326static struct snd_soc_card rockchip_max98090_hdmi_card = {327 .name = "ROCKCHIP-MAX98090-HDMI",328 .owner = THIS_MODULE,329 .dai_link = rk_max98090_hdmi_dailinks,330 .num_links = ARRAY_SIZE(rk_max98090_hdmi_dailinks),331 .aux_dev = &rk_98090_headset_dev,332 .num_aux_devs = 1,333 .dapm_widgets = rk_max98090_hdmi_dapm_widgets,334 .num_dapm_widgets = ARRAY_SIZE(rk_max98090_hdmi_dapm_widgets),335 .dapm_routes = rk_max98090_hdmi_audio_map,336 .num_dapm_routes = ARRAY_SIZE(rk_max98090_hdmi_audio_map),337 .controls = rk_max98090_hdmi_controls,338 .num_controls = ARRAY_SIZE(rk_max98090_hdmi_controls),339};340 341static int rk_98090_headset_init(struct snd_soc_component *component)342{343 int ret;344 345 /* Enable Headset and 4 Buttons Jack detection */346 ret = snd_soc_card_jack_new_pins(component->card, "Headset Jack",347 SND_JACK_HEADSET |348 SND_JACK_BTN_0 | SND_JACK_BTN_1 |349 SND_JACK_BTN_2 | SND_JACK_BTN_3,350 &headset_jack,351 headset_jack_pins,352 ARRAY_SIZE(headset_jack_pins));353 if (ret)354 return ret;355 356 ret = ts3a227e_enable_jack_detect(component, &headset_jack);357 358 return ret;359}360 361static int rk_parse_headset_from_of(struct device *dev, struct device_node *np)362{363 rk_98090_headset_dev.dlc.of_node = of_parse_phandle(364 np, "rockchip,headset-codec", 0);365 if (!rk_98090_headset_dev.dlc.of_node) {366 dev_err(dev,367 "Property 'rockchip,headset-codec' missing/invalid\n");368 return -EINVAL;369 }370 return 0;371}372 373static int snd_rk_mc_probe(struct platform_device *pdev)374{375 int ret = 0;376 struct snd_soc_card *card;377 struct device *dev = &pdev->dev;378 struct device_node *np = pdev->dev.of_node;379 struct device_node *np_cpu;380 struct device_node *np_audio, *np_hdmi;381 382 /* Parse DTS for I2S controller. */383 np_cpu = of_parse_phandle(np, "rockchip,i2s-controller", 0);384 385 if (!np_cpu) {386 dev_err(&pdev->dev,387 "Property 'rockchip,i2s-controller missing or invalid\n");388 return -EINVAL;389 }390 391 /*392 * Find the card to use based on the presences of audio codec393 * and hdmi codec in device property. Set their of_node accordingly.394 */395 np_audio = of_parse_phandle(np, "rockchip,audio-codec", 0);396 np_hdmi = of_parse_phandle(np, "rockchip,hdmi-codec", 0);397 if (np_audio && np_hdmi) {398 card = &rockchip_max98090_hdmi_card;399 card->dai_link[DAILINK_MAX98090].codecs->of_node = np_audio;400 card->dai_link[DAILINK_HDMI].codecs->of_node = np_hdmi;401 card->dai_link[DAILINK_MAX98090].cpus->of_node = np_cpu;402 card->dai_link[DAILINK_MAX98090].platforms->of_node = np_cpu;403 card->dai_link[DAILINK_HDMI].cpus->of_node = np_cpu;404 card->dai_link[DAILINK_HDMI].platforms->of_node = np_cpu;405 } else if (np_audio) {406 card = &rockchip_max98090_card;407 card->dai_link[0].codecs->of_node = np_audio;408 card->dai_link[0].cpus->of_node = np_cpu;409 card->dai_link[0].platforms->of_node = np_cpu;410 } else if (np_hdmi) {411 card = &rockchip_hdmi_card;412 card->dai_link[0].codecs->of_node = np_hdmi;413 card->dai_link[0].cpus->of_node = np_cpu;414 card->dai_link[0].platforms->of_node = np_cpu;415 } else {416 dev_err(dev, "At least one of codecs should be specified\n");417 return -EINVAL;418 }419 420 card->dev = dev;421 422 /* Parse headset detection codec. */423 if (np_audio) {424 ret = rk_parse_headset_from_of(dev, np);425 if (ret)426 return ret;427 }428 429 /* Parse card name. */430 ret = snd_soc_of_parse_card_name(card, "rockchip,model");431 if (ret) {432 dev_err(&pdev->dev,433 "Soc parse card name failed %d\n", ret);434 return ret;435 }436 437 /* register the soc card */438 ret = devm_snd_soc_register_card(&pdev->dev, card);439 if (ret) {440 dev_err(&pdev->dev,441 "Soc register card failed %d\n", ret);442 return ret;443 }444 445 return ret;446}447 448static const struct of_device_id rockchip_max98090_of_match[] = {449 { .compatible = "rockchip,rockchip-audio-max98090", },450 {},451};452 453MODULE_DEVICE_TABLE(of, rockchip_max98090_of_match);454 455static struct platform_driver snd_rk_mc_driver = {456 .probe = snd_rk_mc_probe,457 .driver = {458 .name = DRV_NAME,459 .pm = &snd_soc_pm_ops,460 .of_match_table = rockchip_max98090_of_match,461 },462};463 464module_platform_driver(snd_rk_mc_driver);465 466MODULE_AUTHOR("jianqun <jay.xu@rock-chips.com>");467MODULE_DESCRIPTION("Rockchip max98090 machine ASoC driver");468MODULE_LICENSE("GPL v2");469MODULE_ALIAS("platform:" DRV_NAME);470