root/sound/soc/codecs/inno_rk3036.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rk3036_codec_antipop_info
  2. rk3036_codec_antipop_get
  3. rk3036_codec_antipop_put
  4. rk3036_codec_dai_set_fmt
  5. rk3036_codec_dai_hw_params
  6. rk3036_codec_reset
  7. rk3036_codec_probe
  8. rk3036_codec_remove
  9. rk3036_codec_set_bias_level
  10. rk3036_codec_platform_probe
  11. rk3036_codec_platform_remove

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

/* [<][>][^][v][top][bottom][index][help] */