487 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver of Inno codec for rk3036 by Rockchip Inc.4 *5 * Author: Rockchip Inc.6 * Author: Zheng ShunQian<zhengsq@rock-chips.com>7 */8 9#include <sound/soc.h>10#include <sound/tlv.h>11#include <sound/soc-dapm.h>12#include <sound/soc-dai.h>13#include <sound/pcm.h>14#include <sound/pcm_params.h>15 16#include <linux/platform_device.h>17#include <linux/of.h>18#include <linux/clk.h>19#include <linux/regmap.h>20#include <linux/device.h>21#include <linux/mfd/syscon.h>22#include <linux/module.h>23#include <linux/io.h>24 25#include "inno_rk3036.h"26 27struct rk3036_codec_priv {28 void __iomem *base;29 struct clk *pclk;30 struct regmap *regmap;31 struct device *dev;32};33 34static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);35 36static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,37 struct snd_ctl_elem_info *uinfo)38{39 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;40 uinfo->count = 2;41 uinfo->value.integer.min = 0;42 uinfo->value.integer.max = 1;43 44 return 0;45}46 47static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,48 struct snd_ctl_elem_value *ucontrol)49{50 struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);51 int val, regval;52 53 regval = snd_soc_component_read(component, INNO_R09);54 val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &55 INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;56 ucontrol->value.integer.value[0] = val;57 58 val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &59 INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;60 ucontrol->value.integer.value[1] = val;61 62 return 0;63}64 65static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,66 struct snd_ctl_elem_value *ucontrol)67{68 struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);69 int val, ret, regmsk;70 71 val = (ucontrol->value.integer.value[0] ?72 INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<73 INNO_R09_HPL_ANITPOP_SHIFT;74 val |= (ucontrol->value.integer.value[1] ?75 INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<76 INNO_R09_HPR_ANITPOP_SHIFT;77 78 regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |79 INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;80 81 ret = snd_soc_component_update_bits(component, INNO_R09,82 regmsk, val);83 if (ret < 0)84 return ret;85 86 return 0;87}88 89#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \90{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \91 .info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \92 .put = rk3036_codec_antipop_put, }93 94static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {95 SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,96 INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,97 INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),98 SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,99 INNO_R06_VOUTR_CZ_SHIFT, 1, 0),100 SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,101 INNO_R09_HPR_MUTE_SHIFT, 1, 0),102 SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),103};104 105static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {106 SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,107 INNO_R09_DACL_SWITCH_SHIFT, 1, 0),108};109 110static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {111 SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,112 INNO_R09_DACR_SWITCH_SHIFT, 1, 0),113};114 115static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {116 SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,117 INNO_R05_HPL_WORK_SHIFT, 1, 0),118};119 120static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {121 SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,122 INNO_R05_HPR_WORK_SHIFT, 1, 0),123};124 125static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {126 SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,127 INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),128 SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,129 INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),130 SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,131 INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),132 SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,133 INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),134 SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,135 INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),136 SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,137 INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),138 SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,139 INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),140 141 SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,142 INNO_R04_DACL_SW_SHIFT, 0),143 SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,144 INNO_R04_DACR_SW_SHIFT, 0),145 146 SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,147 rk3036_codec_hpl_mixer_controls,148 ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),149 SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,150 rk3036_codec_hpr_mixer_controls,151 ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),152 153 SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,154 INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),155 SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,156 INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),157 158 SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0,159 rk3036_codec_hpl_switch_controls,160 ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),161 SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0,162 rk3036_codec_hpr_switch_controls,163 ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),164 165 SND_SOC_DAPM_OUTPUT("HPL"),166 SND_SOC_DAPM_OUTPUT("HPR"),167};168 169static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {170 {"DACL VREF", NULL, "DAC PWR"},171 {"DACR VREF", NULL, "DAC PWR"},172 {"DACL HiLo VREF", NULL, "DAC PWR"},173 {"DACR HiLo VREF", NULL, "DAC PWR"},174 {"DACL CLK", NULL, "DAC PWR"},175 {"DACR CLK", NULL, "DAC PWR"},176 177 {"DACL", NULL, "DACL VREF"},178 {"DACL", NULL, "DACL HiLo VREF"},179 {"DACL", NULL, "DACL CLK"},180 {"DACR", NULL, "DACR VREF"},181 {"DACR", NULL, "DACR HiLo VREF"},182 {"DACR", NULL, "DACR CLK"},183 184 {"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},185 {"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},186 {"HP Left Out", NULL, "Left Headphone Mixer"},187 {"HP Right Out", NULL, "Right Headphone Mixer"},188 189 {"HP Left Switch", "HP Left Out Switch", "HP Left Out"},190 {"HP Right Switch", "HP Right Out Switch", "HP Right Out"},191 192 {"HPL", NULL, "HP Left Switch"},193 {"HPR", NULL, "HP Right Switch"},194};195 196static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)197{198 struct snd_soc_component *component = dai->component;199 unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;200 201 dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt);202 203 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {204 case SND_SOC_DAIFMT_CBC_CFC:205 reg01_val |= INNO_R01_PINDIR_IN_SLAVE |206 INNO_R01_I2SMODE_SLAVE;207 break;208 case SND_SOC_DAIFMT_CBP_CFP:209 reg01_val |= INNO_R01_PINDIR_OUT_MASTER |210 INNO_R01_I2SMODE_MASTER;211 break;212 default:213 dev_err(component->dev, "invalid fmt\n");214 return -EINVAL;215 }216 217 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {218 case SND_SOC_DAIFMT_DSP_A:219 reg02_val |= INNO_R02_DACM_PCM;220 break;221 case SND_SOC_DAIFMT_I2S:222 reg02_val |= INNO_R02_DACM_I2S;223 break;224 case SND_SOC_DAIFMT_RIGHT_J:225 reg02_val |= INNO_R02_DACM_RJM;226 break;227 case SND_SOC_DAIFMT_LEFT_J:228 reg02_val |= INNO_R02_DACM_LJM;229 break;230 default:231 dev_err(component->dev, "set dai format failed\n");232 return -EINVAL;233 }234 235 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {236 case SND_SOC_DAIFMT_NB_NF:237 reg02_val |= INNO_R02_LRCP_NORMAL;238 reg03_val |= INNO_R03_BCP_NORMAL;239 break;240 case SND_SOC_DAIFMT_IB_IF:241 reg02_val |= INNO_R02_LRCP_REVERSAL;242 reg03_val |= INNO_R03_BCP_REVERSAL;243 break;244 case SND_SOC_DAIFMT_IB_NF:245 reg02_val |= INNO_R02_LRCP_REVERSAL;246 reg03_val |= INNO_R03_BCP_NORMAL;247 break;248 case SND_SOC_DAIFMT_NB_IF:249 reg02_val |= INNO_R02_LRCP_NORMAL;250 reg03_val |= INNO_R03_BCP_REVERSAL;251 break;252 default:253 dev_err(component->dev, "set dai format failed\n");254 return -EINVAL;255 }256 257 snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK |258 INNO_R01_PINDIR_MSK, reg01_val);259 snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |260 INNO_R02_DACM_MSK, reg02_val);261 snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val);262 263 return 0;264}265 266static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,267 struct snd_pcm_hw_params *hw_params,268 struct snd_soc_dai *dai)269{270 struct snd_soc_component *component = dai->component;271 unsigned int reg02_val = 0, reg03_val = 0;272 273 switch (params_format(hw_params)) {274 case SNDRV_PCM_FORMAT_S16_LE:275 reg02_val |= INNO_R02_VWL_16BIT;276 break;277 case SNDRV_PCM_FORMAT_S20_3LE:278 reg02_val |= INNO_R02_VWL_20BIT;279 break;280 case SNDRV_PCM_FORMAT_S24_LE:281 reg02_val |= INNO_R02_VWL_24BIT;282 break;283 case SNDRV_PCM_FORMAT_S32_LE:284 reg02_val |= INNO_R02_VWL_32BIT;285 break;286 default:287 return -EINVAL;288 }289 290 reg02_val |= INNO_R02_LRCP_NORMAL;291 reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;292 293 snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |294 INNO_R02_VWL_MSK, reg02_val);295 snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK |296 INNO_R03_FWL_MSK, reg03_val);297 return 0;298}299 300#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \301 SNDRV_PCM_RATE_16000 | \302 SNDRV_PCM_RATE_32000 | \303 SNDRV_PCM_RATE_44100 | \304 SNDRV_PCM_RATE_48000 | \305 SNDRV_PCM_RATE_96000)306 307#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \308 SNDRV_PCM_FMTBIT_S20_3LE | \309 SNDRV_PCM_FMTBIT_S24_LE | \310 SNDRV_PCM_FMTBIT_S32_LE)311 312static const struct snd_soc_dai_ops rk3036_codec_dai_ops = {313 .set_fmt = rk3036_codec_dai_set_fmt,314 .hw_params = rk3036_codec_dai_hw_params,315};316 317static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {318 {319 .name = "rk3036-codec-dai",320 .playback = {321 .stream_name = "Playback",322 .channels_min = 1,323 .channels_max = 2,324 .rates = RK3036_CODEC_RATES,325 .formats = RK3036_CODEC_FMTS,326 },327 .ops = &rk3036_codec_dai_ops,328 .symmetric_rate = 1,329 },330};331 332static void rk3036_codec_reset(struct snd_soc_component *component)333{334 snd_soc_component_write(component, INNO_R00,335 INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);336 snd_soc_component_write(component, INNO_R00,337 INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);338}339 340static int rk3036_codec_probe(struct snd_soc_component *component)341{342 rk3036_codec_reset(component);343 return 0;344}345 346static void rk3036_codec_remove(struct snd_soc_component *component)347{348 rk3036_codec_reset(component);349}350 351static int rk3036_codec_set_bias_level(struct snd_soc_component *component,352 enum snd_soc_bias_level level)353{354 switch (level) {355 case SND_SOC_BIAS_STANDBY:356 /* set a big current for capacitor charging. */357 snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);358 /* start precharge */359 snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE);360 361 break;362 363 case SND_SOC_BIAS_OFF:364 /* set a big current for capacitor discharging. */365 snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);366 /* start discharge. */367 snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE);368 369 break;370 default:371 break;372 }373 374 return 0;375}376 377static const struct snd_soc_component_driver rk3036_codec_driver = {378 .probe = rk3036_codec_probe,379 .remove = rk3036_codec_remove,380 .set_bias_level = rk3036_codec_set_bias_level,381 .controls = rk3036_codec_dapm_controls,382 .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),383 .dapm_routes = rk3036_codec_dapm_routes,384 .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),385 .dapm_widgets = rk3036_codec_dapm_widgets,386 .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),387 .idle_bias_on = 1,388 .use_pmdown_time = 1,389 .endianness = 1,390};391 392static const struct regmap_config rk3036_codec_regmap_config = {393 .reg_bits = 32,394 .reg_stride = 4,395 .val_bits = 32,396};397 398#define GRF_SOC_CON0 0x00140399#define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10))400 401static int rk3036_codec_platform_probe(struct platform_device *pdev)402{403 struct rk3036_codec_priv *priv;404 struct device_node *of_node = pdev->dev.of_node;405 void __iomem *base;406 struct regmap *grf;407 int ret;408 409 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);410 if (!priv)411 return -ENOMEM;412 413 base = devm_platform_ioremap_resource(pdev, 0);414 if (IS_ERR(base))415 return PTR_ERR(base);416 417 priv->base = base;418 priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,419 &rk3036_codec_regmap_config);420 if (IS_ERR(priv->regmap)) {421 dev_err(&pdev->dev, "init regmap failed\n");422 return PTR_ERR(priv->regmap);423 }424 425 grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");426 if (IS_ERR(grf)) {427 dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");428 return PTR_ERR(grf);429 }430 ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);431 if (ret) {432 dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);433 return ret;434 }435 436 priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");437 if (IS_ERR(priv->pclk))438 return PTR_ERR(priv->pclk);439 440 ret = clk_prepare_enable(priv->pclk);441 if (ret < 0) {442 dev_err(&pdev->dev, "failed to enable clk\n");443 return ret;444 }445 446 priv->dev = &pdev->dev;447 dev_set_drvdata(&pdev->dev, priv);448 449 ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver,450 rk3036_codec_dai_driver,451 ARRAY_SIZE(rk3036_codec_dai_driver));452 if (ret) {453 clk_disable_unprepare(priv->pclk);454 dev_set_drvdata(&pdev->dev, NULL);455 }456 457 return ret;458}459 460static void rk3036_codec_platform_remove(struct platform_device *pdev)461{462 struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);463 464 clk_disable_unprepare(priv->pclk);465}466 467static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = {468 { .compatible = "rockchip,rk3036-codec", },469 {}470};471MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);472 473static struct platform_driver rk3036_codec_platform_driver = {474 .driver = {475 .name = "rk3036-codec-platform",476 .of_match_table = of_match_ptr(rk3036_codec_of_match),477 },478 .probe = rk3036_codec_platform_probe,479 .remove = rk3036_codec_platform_remove,480};481 482module_platform_driver(rk3036_codec_platform_driver);483 484MODULE_AUTHOR("Rockchip Inc.");485MODULE_DESCRIPTION("Rockchip rk3036 codec driver");486MODULE_LICENSE("GPL");487