1841 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2014 Emilio López <emilio@elopez.com.ar>4 * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>5 * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>6 * Copyright 2015 Adam Sampson <ats@offog.org>7 * Copyright 2016 Chen-Yu Tsai <wens@csie.org>8 *9 * Based on the Allwinner SDK driver, released under the GPL.10 */11 12#include <linux/init.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/delay.h>17#include <linux/slab.h>18#include <linux/clk.h>19#include <linux/regmap.h>20#include <linux/reset.h>21#include <linux/gpio/consumer.h>22 23#include <sound/core.h>24#include <sound/pcm.h>25#include <sound/pcm_params.h>26#include <sound/soc.h>27#include <sound/tlv.h>28#include <sound/initval.h>29#include <sound/dmaengine_pcm.h>30 31/* Codec DAC digital controls and FIFO registers */32#define SUN4I_CODEC_DAC_DPC (0x00)33#define SUN4I_CODEC_DAC_DPC_EN_DA (31)34#define SUN4I_CODEC_DAC_DPC_DVOL (12)35#define SUN4I_CODEC_DAC_FIFOC (0x04)36#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29)37#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28)38#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26)39#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24)40#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21)41#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8)42#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6)43#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5)44#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4)45#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)46#define SUN4I_CODEC_DAC_FIFOS (0x08)47#define SUN4I_CODEC_DAC_TXDATA (0x0c)48 49/* Codec DAC side analog signal controls */50#define SUN4I_CODEC_DAC_ACTL (0x10)51#define SUN4I_CODEC_DAC_ACTL_DACAENR (31)52#define SUN4I_CODEC_DAC_ACTL_DACAENL (30)53#define SUN4I_CODEC_DAC_ACTL_MIXEN (29)54#define SUN4I_CODEC_DAC_ACTL_LNG (26)55#define SUN4I_CODEC_DAC_ACTL_FMG (23)56#define SUN4I_CODEC_DAC_ACTL_MICG (20)57#define SUN4I_CODEC_DAC_ACTL_LLNS (19)58#define SUN4I_CODEC_DAC_ACTL_RLNS (18)59#define SUN4I_CODEC_DAC_ACTL_LFMS (17)60#define SUN4I_CODEC_DAC_ACTL_RFMS (16)61#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15)62#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14)63#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13)64#define SUN4I_CODEC_DAC_ACTL_MIC1LS (12)65#define SUN4I_CODEC_DAC_ACTL_MIC1RS (11)66#define SUN4I_CODEC_DAC_ACTL_MIC2LS (10)67#define SUN4I_CODEC_DAC_ACTL_MIC2RS (9)68#define SUN4I_CODEC_DAC_ACTL_DACPAS (8)69#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7)70#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6)71#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0)72#define SUN4I_CODEC_DAC_TUNE (0x14)73#define SUN4I_CODEC_DAC_DEBUG (0x18)74 75/* Codec ADC digital controls and FIFO registers */76#define SUN4I_CODEC_ADC_FIFOC (0x1c)77#define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29)78#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)79#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)80#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)81#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7)82#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6)83#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4)84#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)85#define SUN4I_CODEC_ADC_FIFOS (0x20)86#define SUN4I_CODEC_ADC_RXDATA (0x24)87 88/* Codec ADC side analog signal controls */89#define SUN4I_CODEC_ADC_ACTL (0x28)90#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)91#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)92#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29)93#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28)94#define SUN4I_CODEC_ADC_ACTL_VMICEN (27)95#define SUN4I_CODEC_ADC_ACTL_PREG1 (25)96#define SUN4I_CODEC_ADC_ACTL_PREG2 (23)97#define SUN4I_CODEC_ADC_ACTL_VADCG (20)98#define SUN4I_CODEC_ADC_ACTL_ADCIS (17)99#define SUN4I_CODEC_ADC_ACTL_LNPREG (13)100#define SUN4I_CODEC_ADC_ACTL_PA_EN (4)101#define SUN4I_CODEC_ADC_ACTL_DDE (3)102#define SUN4I_CODEC_ADC_DEBUG (0x2c)103 104/* FIFO counters */105#define SUN4I_CODEC_DAC_TXCNT (0x30)106#define SUN4I_CODEC_ADC_RXCNT (0x34)107 108/* Calibration register (sun7i only) */109#define SUN7I_CODEC_AC_DAC_CAL (0x38)110 111/* Microphone controls (sun7i only) */112#define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c)113 114#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1 (29)115#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2 (26)116 117/*118 * sun6i specific registers119 *120 * sun6i shares the same digital control and FIFO registers as sun4i,121 * but only the DAC digital controls are at the same offset. The others122 * have been moved around to accommodate extra analog controls.123 */124 125/* Codec DAC digital controls and FIFO registers */126#define SUN6I_CODEC_ADC_FIFOC (0x10)127#define SUN6I_CODEC_ADC_FIFOC_EN_AD (28)128#define SUN6I_CODEC_ADC_FIFOS (0x14)129#define SUN6I_CODEC_ADC_RXDATA (0x18)130 131/* Output mixer and gain controls */132#define SUN6I_CODEC_OM_DACA_CTRL (0x20)133#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31)134#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30)135#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29)136#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28)137#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23)138#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22)139#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21)140#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20)141#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19)142#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18)143#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17)144#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16)145#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15)146#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14)147#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13)148#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12)149#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11)150#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10)151#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9)152#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8)153#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7)154#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6)155#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0)156#define SUN6I_CODEC_OM_PA_CTRL (0x24)157#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31)158#define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL (29)159#define SUN6I_CODEC_OM_PA_CTRL_COMPTEN (28)160#define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15)161#define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12)162#define SUN6I_CODEC_OM_PA_CTRL_LINEING (9)163#define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6)164#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3)165#define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0)166 167/* Microphone, line out and phone out controls */168#define SUN6I_CODEC_MIC_CTRL (0x28)169#define SUN6I_CODEC_MIC_CTRL_HBIASEN (31)170#define SUN6I_CODEC_MIC_CTRL_MBIASEN (30)171#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28)172#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25)173#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24)174#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21)175#define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20)176#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19)177#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18)178#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17)179#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16)180#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11)181#define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8)182 183/* ADC mixer controls */184#define SUN6I_CODEC_ADC_ACTL (0x2c)185#define SUN6I_CODEC_ADC_ACTL_ADCREN (31)186#define SUN6I_CODEC_ADC_ACTL_ADCLEN (30)187#define SUN6I_CODEC_ADC_ACTL_ADCRG (27)188#define SUN6I_CODEC_ADC_ACTL_ADCLG (24)189#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13)190#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12)191#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11)192#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10)193#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9)194#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8)195#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7)196#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6)197#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5)198#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4)199#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3)200#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2)201#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1)202#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0)203 204/* Analog performance tuning controls */205#define SUN6I_CODEC_ADDA_TUNE (0x30)206 207/* Calibration controls */208#define SUN6I_CODEC_CALIBRATION (0x34)209 210/* FIFO counters */211#define SUN6I_CODEC_DAC_TXCNT (0x40)212#define SUN6I_CODEC_ADC_RXCNT (0x44)213 214/* headset jack detection and button support registers */215#define SUN6I_CODEC_HMIC_CTL (0x50)216#define SUN6I_CODEC_HMIC_DATA (0x54)217 218/* TODO sun6i DAP (Digital Audio Processing) bits */219 220/* FIFO counters moved on A23 */221#define SUN8I_A23_CODEC_DAC_TXCNT (0x1c)222#define SUN8I_A23_CODEC_ADC_RXCNT (0x20)223 224/* TX FIFO moved on H3 */225#define SUN8I_H3_CODEC_DAC_TXDATA (0x20)226#define SUN8I_H3_CODEC_DAC_DBG (0x48)227#define SUN8I_H3_CODEC_ADC_DBG (0x4c)228 229/* TODO H3 DAP (Digital Audio Processing) bits */230 231struct sun4i_codec {232 struct device *dev;233 struct regmap *regmap;234 struct clk *clk_apb;235 struct clk *clk_module;236 struct reset_control *rst;237 struct gpio_desc *gpio_pa;238 239 /* ADC_FIFOC register is at different offset on different SoCs */240 struct regmap_field *reg_adc_fifoc;241 242 struct snd_dmaengine_dai_dma_data capture_dma_data;243 struct snd_dmaengine_dai_dma_data playback_dma_data;244};245 246static void sun4i_codec_start_playback(struct sun4i_codec *scodec)247{248 /* Flush TX FIFO */249 regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,250 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));251 252 /* Enable DAC DRQ */253 regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,254 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));255}256 257static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)258{259 /* Disable DAC DRQ */260 regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,261 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));262}263 264static void sun4i_codec_start_capture(struct sun4i_codec *scodec)265{266 /* Enable ADC DRQ */267 regmap_field_set_bits(scodec->reg_adc_fifoc,268 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));269}270 271static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)272{273 /* Disable ADC DRQ */274 regmap_field_clear_bits(scodec->reg_adc_fifoc,275 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));276}277 278static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,279 struct snd_soc_dai *dai)280{281 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);282 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);283 284 switch (cmd) {285 case SNDRV_PCM_TRIGGER_START:286 case SNDRV_PCM_TRIGGER_RESUME:287 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:288 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)289 sun4i_codec_start_playback(scodec);290 else291 sun4i_codec_start_capture(scodec);292 break;293 294 case SNDRV_PCM_TRIGGER_STOP:295 case SNDRV_PCM_TRIGGER_SUSPEND:296 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:297 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)298 sun4i_codec_stop_playback(scodec);299 else300 sun4i_codec_stop_capture(scodec);301 break;302 303 default:304 return -EINVAL;305 }306 307 return 0;308}309 310static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,311 struct snd_soc_dai *dai)312{313 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);314 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);315 316 317 /* Flush RX FIFO */318 regmap_field_set_bits(scodec->reg_adc_fifoc,319 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));320 321 322 /* Set RX FIFO trigger level */323 regmap_field_update_bits(scodec->reg_adc_fifoc,324 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,325 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);326 327 /*328 * FIXME: Undocumented in the datasheet, but329 * Allwinner's code mentions that it is330 * related to microphone gain331 */332 if (of_device_is_compatible(scodec->dev->of_node,333 "allwinner,sun4i-a10-codec") ||334 of_device_is_compatible(scodec->dev->of_node,335 "allwinner,sun7i-a20-codec")) {336 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,337 0x3 << 25,338 0x1 << 25);339 }340 341 if (of_device_is_compatible(scodec->dev->of_node,342 "allwinner,sun7i-a20-codec"))343 /* FIXME: Undocumented bits */344 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,345 0x3 << 8,346 0x1 << 8);347 348 return 0;349}350 351static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,352 struct snd_soc_dai *dai)353{354 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);355 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);356 u32 val;357 358 /* Flush the TX FIFO */359 regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,360 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));361 362 /* Set TX FIFO Empty Trigger Level */363 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,364 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,365 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);366 367 if (substream->runtime->rate > 32000)368 /* Use 64 bits FIR filter */369 val = 0;370 else371 /* Use 32 bits FIR filter */372 val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);373 374 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,375 BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),376 val);377 378 /* Send zeros when we have an underrun */379 regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,380 BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));381 382 return 0;383};384 385static int sun4i_codec_prepare(struct snd_pcm_substream *substream,386 struct snd_soc_dai *dai)387{388 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)389 return sun4i_codec_prepare_playback(substream, dai);390 391 return sun4i_codec_prepare_capture(substream, dai);392}393 394static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)395{396 unsigned int rate = params_rate(params);397 398 switch (rate) {399 case 176400:400 case 88200:401 case 44100:402 case 33075:403 case 22050:404 case 14700:405 case 11025:406 case 7350:407 return 22579200;408 409 case 192000:410 case 96000:411 case 48000:412 case 32000:413 case 24000:414 case 16000:415 case 12000:416 case 8000:417 return 24576000;418 419 default:420 return 0;421 }422}423 424static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)425{426 unsigned int rate = params_rate(params);427 428 switch (rate) {429 case 192000:430 case 176400:431 return 6;432 433 case 96000:434 case 88200:435 return 7;436 437 case 48000:438 case 44100:439 return 0;440 441 case 32000:442 case 33075:443 return 1;444 445 case 24000:446 case 22050:447 return 2;448 449 case 16000:450 case 14700:451 return 3;452 453 case 12000:454 case 11025:455 return 4;456 457 case 8000:458 case 7350:459 return 5;460 461 default:462 return -EINVAL;463 }464}465 466static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,467 struct snd_pcm_hw_params *params,468 unsigned int hwrate)469{470 /* Set ADC sample rate */471 regmap_field_update_bits(scodec->reg_adc_fifoc,472 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,473 hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);474 475 /* Set the number of channels we want to use */476 if (params_channels(params) == 1)477 regmap_field_set_bits(scodec->reg_adc_fifoc,478 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));479 else480 regmap_field_clear_bits(scodec->reg_adc_fifoc,481 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));482 483 /* Set the number of sample bits to either 16 or 24 bits */484 if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {485 regmap_field_set_bits(scodec->reg_adc_fifoc,486 BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));487 488 regmap_field_clear_bits(scodec->reg_adc_fifoc,489 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));490 491 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;492 } else {493 regmap_field_clear_bits(scodec->reg_adc_fifoc,494 BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));495 496 /* Fill most significant bits with valid data MSB */497 regmap_field_set_bits(scodec->reg_adc_fifoc,498 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));499 500 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;501 }502 503 return 0;504}505 506static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,507 struct snd_pcm_hw_params *params,508 unsigned int hwrate)509{510 u32 val;511 512 /* Set DAC sample rate */513 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,514 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,515 hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);516 517 /* Set the number of channels we want to use */518 if (params_channels(params) == 1)519 val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);520 else521 val = 0;522 523 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,524 BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),525 val);526 527 /* Set the number of sample bits to either 16 or 24 bits */528 if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {529 regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,530 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));531 532 /* Set TX FIFO mode to padding the LSBs with 0 */533 regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,534 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));535 536 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;537 } else {538 regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,539 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));540 541 /* Set TX FIFO mode to repeat the MSB */542 regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,543 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));544 545 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;546 }547 548 return 0;549}550 551static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,552 struct snd_pcm_hw_params *params,553 struct snd_soc_dai *dai)554{555 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);556 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);557 unsigned long clk_freq;558 int ret, hwrate;559 560 clk_freq = sun4i_codec_get_mod_freq(params);561 if (!clk_freq)562 return -EINVAL;563 564 ret = clk_set_rate(scodec->clk_module, clk_freq);565 if (ret)566 return ret;567 568 hwrate = sun4i_codec_get_hw_rate(params);569 if (hwrate < 0)570 return hwrate;571 572 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)573 return sun4i_codec_hw_params_playback(scodec, params,574 hwrate);575 576 return sun4i_codec_hw_params_capture(scodec, params,577 hwrate);578}579 580static int sun4i_codec_startup(struct snd_pcm_substream *substream,581 struct snd_soc_dai *dai)582{583 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);584 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);585 586 /*587 * Stop issuing DRQ when we have room for less than 16 samples588 * in our TX FIFO589 */590 regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,591 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);592 593 return clk_prepare_enable(scodec->clk_module);594}595 596static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,597 struct snd_soc_dai *dai)598{599 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);600 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);601 602 clk_disable_unprepare(scodec->clk_module);603}604 605static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {606 .startup = sun4i_codec_startup,607 .shutdown = sun4i_codec_shutdown,608 .trigger = sun4i_codec_trigger,609 .hw_params = sun4i_codec_hw_params,610 .prepare = sun4i_codec_prepare,611};612 613#define SUN4I_CODEC_RATES ( \614 SNDRV_PCM_RATE_8000_48000 | \615 SNDRV_PCM_RATE_12000 | \616 SNDRV_PCM_RATE_24000 | \617 SNDRV_PCM_RATE_96000 | \618 SNDRV_PCM_RATE_192000)619 620static struct snd_soc_dai_driver sun4i_codec_dai = {621 .name = "Codec",622 .ops = &sun4i_codec_dai_ops,623 .playback = {624 .stream_name = "Codec Playback",625 .channels_min = 1,626 .channels_max = 2,627 .rate_min = 8000,628 .rate_max = 192000,629 .rates = SUN4I_CODEC_RATES,630 .formats = SNDRV_PCM_FMTBIT_S16_LE |631 SNDRV_PCM_FMTBIT_S32_LE,632 .sig_bits = 24,633 },634 .capture = {635 .stream_name = "Codec Capture",636 .channels_min = 1,637 .channels_max = 2,638 .rate_min = 8000,639 .rate_max = 48000,640 .rates = SUN4I_CODEC_RATES,641 .formats = SNDRV_PCM_FMTBIT_S16_LE |642 SNDRV_PCM_FMTBIT_S32_LE,643 .sig_bits = 24,644 },645};646 647/*** sun4i Codec ***/648static const struct snd_kcontrol_new sun4i_codec_pa_mute =649 SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,650 SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);651 652static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);653static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_loopback_gain_scale, -150, 150,654 0);655static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_preamp_gain_scale, -1200, 300,656 0);657static DECLARE_TLV_DB_SCALE(sun4i_codec_fmin_loopback_gain_scale, -450, 150,658 0);659static DECLARE_TLV_DB_SCALE(sun4i_codec_micin_loopback_gain_scale, -450, 150,660 0);661static DECLARE_TLV_DB_RANGE(sun4i_codec_micin_preamp_gain_scale,662 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),663 1, 7, TLV_DB_SCALE_ITEM(3500, 300, 0));664static DECLARE_TLV_DB_RANGE(sun7i_codec_micin_preamp_gain_scale,665 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),666 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0));667 668static const struct snd_kcontrol_new sun4i_codec_controls[] = {669 SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,670 SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,671 sun4i_codec_pa_volume_scale),672 SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL,673 SUN4I_CODEC_DAC_ACTL_LNG, 1, 0,674 sun4i_codec_linein_loopback_gain_scale),675 SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL,676 SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0,677 sun4i_codec_linein_preamp_gain_scale),678 SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL,679 SUN4I_CODEC_DAC_ACTL_FMG, 3, 0,680 sun4i_codec_fmin_loopback_gain_scale),681 SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,682 SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,683 sun4i_codec_micin_loopback_gain_scale),684 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN4I_CODEC_ADC_ACTL,685 SUN4I_CODEC_ADC_ACTL_PREG1, 3, 0,686 sun4i_codec_micin_preamp_gain_scale),687 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN4I_CODEC_ADC_ACTL,688 SUN4I_CODEC_ADC_ACTL_PREG2, 3, 0,689 sun4i_codec_micin_preamp_gain_scale),690};691 692static const struct snd_kcontrol_new sun7i_codec_controls[] = {693 SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,694 SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,695 sun4i_codec_pa_volume_scale),696 SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL,697 SUN4I_CODEC_DAC_ACTL_LNG, 1, 0,698 sun4i_codec_linein_loopback_gain_scale),699 SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL,700 SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0,701 sun4i_codec_linein_preamp_gain_scale),702 SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL,703 SUN4I_CODEC_DAC_ACTL_FMG, 3, 0,704 sun4i_codec_fmin_loopback_gain_scale),705 SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,706 SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,707 sun4i_codec_micin_loopback_gain_scale),708 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,709 SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1, 7, 0,710 sun7i_codec_micin_preamp_gain_scale),711 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,712 SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2, 7, 0,713 sun7i_codec_micin_preamp_gain_scale),714};715 716static const struct snd_kcontrol_new sun4i_codec_mixer_controls[] = {717 SOC_DAPM_SINGLE("Left Mixer Left DAC Playback Switch",718 SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_LDACLMIXS,719 1, 0),720 SOC_DAPM_SINGLE("Right Mixer Right DAC Playback Switch",721 SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_RDACRMIXS,722 1, 0),723 SOC_DAPM_SINGLE("Right Mixer Left DAC Playback Switch",724 SUN4I_CODEC_DAC_ACTL,725 SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),726 SOC_DAPM_DOUBLE("Line Playback Switch", SUN4I_CODEC_DAC_ACTL,727 SUN4I_CODEC_DAC_ACTL_LLNS,728 SUN4I_CODEC_DAC_ACTL_RLNS, 1, 0),729 SOC_DAPM_DOUBLE("FM Playback Switch", SUN4I_CODEC_DAC_ACTL,730 SUN4I_CODEC_DAC_ACTL_LFMS,731 SUN4I_CODEC_DAC_ACTL_RFMS, 1, 0),732 SOC_DAPM_DOUBLE("Mic1 Playback Switch", SUN4I_CODEC_DAC_ACTL,733 SUN4I_CODEC_DAC_ACTL_MIC1LS,734 SUN4I_CODEC_DAC_ACTL_MIC1RS, 1, 0),735 SOC_DAPM_DOUBLE("Mic2 Playback Switch", SUN4I_CODEC_DAC_ACTL,736 SUN4I_CODEC_DAC_ACTL_MIC2LS,737 SUN4I_CODEC_DAC_ACTL_MIC2RS, 1, 0),738};739 740static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {741 SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,742 SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),743 SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,744 SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),745};746 747static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {748 /* Digital parts of the ADCs */749 SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,750 SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,751 NULL, 0),752 753 /* Digital parts of the DACs */754 SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,755 SUN4I_CODEC_DAC_DPC_EN_DA, 0,756 NULL, 0),757 758 /* Analog parts of the ADCs */759 SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,760 SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),761 SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,762 SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),763 764 /* Analog parts of the DACs */765 SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,766 SUN4I_CODEC_DAC_ACTL_DACAENL, 0),767 SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,768 SUN4I_CODEC_DAC_ACTL_DACAENR, 0),769 770 /* Mixers */771 SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,772 sun4i_codec_mixer_controls,773 ARRAY_SIZE(sun4i_codec_mixer_controls)),774 SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,775 sun4i_codec_mixer_controls,776 ARRAY_SIZE(sun4i_codec_mixer_controls)),777 778 /* Global Mixer Enable */779 SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,780 SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),781 782 /* VMIC */783 SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,784 SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),785 786 /* Mic Pre-Amplifiers */787 SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,788 SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),789 SND_SOC_DAPM_PGA("MIC2 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,790 SUN4I_CODEC_ADC_ACTL_PREG2EN, 0, NULL, 0),791 792 /* Power Amplifier */793 SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,794 SUN4I_CODEC_ADC_ACTL_PA_EN, 0,795 sun4i_codec_pa_mixer_controls,796 ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),797 SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,798 &sun4i_codec_pa_mute),799 800 SND_SOC_DAPM_INPUT("Line Right"),801 SND_SOC_DAPM_INPUT("Line Left"),802 SND_SOC_DAPM_INPUT("FM Right"),803 SND_SOC_DAPM_INPUT("FM Left"),804 SND_SOC_DAPM_INPUT("Mic1"),805 SND_SOC_DAPM_INPUT("Mic2"),806 807 SND_SOC_DAPM_OUTPUT("HP Right"),808 SND_SOC_DAPM_OUTPUT("HP Left"),809};810 811static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {812 /* Left ADC / DAC Routes */813 { "Left ADC", NULL, "ADC" },814 { "Left DAC", NULL, "DAC" },815 816 /* Right ADC / DAC Routes */817 { "Right ADC", NULL, "ADC" },818 { "Right DAC", NULL, "DAC" },819 820 /* Right Mixer Routes */821 { "Right Mixer", NULL, "Mixer Enable" },822 { "Right Mixer", "Right Mixer Left DAC Playback Switch", "Left DAC" },823 { "Right Mixer", "Right Mixer Right DAC Playback Switch", "Right DAC" },824 { "Right Mixer", "Line Playback Switch", "Line Right" },825 { "Right Mixer", "FM Playback Switch", "FM Right" },826 { "Right Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },827 { "Right Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },828 829 /* Left Mixer Routes */830 { "Left Mixer", NULL, "Mixer Enable" },831 { "Left Mixer", "Left Mixer Left DAC Playback Switch", "Left DAC" },832 { "Left Mixer", "Line Playback Switch", "Line Left" },833 { "Left Mixer", "FM Playback Switch", "FM Left" },834 { "Left Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },835 { "Left Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },836 837 /* Power Amplifier Routes */838 { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },839 { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },840 { "Power Amplifier", "DAC Playback Switch", "Left DAC" },841 { "Power Amplifier", "DAC Playback Switch", "Right DAC" },842 843 /* Headphone Output Routes */844 { "Power Amplifier Mute", "Switch", "Power Amplifier" },845 { "HP Right", NULL, "Power Amplifier Mute" },846 { "HP Left", NULL, "Power Amplifier Mute" },847 848 /* Mic1 Routes */849 { "Left ADC", NULL, "MIC1 Pre-Amplifier" },850 { "Right ADC", NULL, "MIC1 Pre-Amplifier" },851 { "MIC1 Pre-Amplifier", NULL, "Mic1"},852 { "Mic1", NULL, "VMIC" },853 854 /* Mic2 Routes */855 { "Left ADC", NULL, "MIC2 Pre-Amplifier" },856 { "Right ADC", NULL, "MIC2 Pre-Amplifier" },857 { "MIC2 Pre-Amplifier", NULL, "Mic2"},858 { "Mic2", NULL, "VMIC" },859};860 861static const struct snd_soc_component_driver sun4i_codec_codec = {862 .controls = sun4i_codec_controls,863 .num_controls = ARRAY_SIZE(sun4i_codec_controls),864 .dapm_widgets = sun4i_codec_codec_dapm_widgets,865 .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),866 .dapm_routes = sun4i_codec_codec_dapm_routes,867 .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),868 .idle_bias_on = 1,869 .use_pmdown_time = 1,870 .endianness = 1,871};872 873static const struct snd_soc_component_driver sun7i_codec_codec = {874 .controls = sun7i_codec_controls,875 .num_controls = ARRAY_SIZE(sun7i_codec_controls),876 .dapm_widgets = sun4i_codec_codec_dapm_widgets,877 .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),878 .dapm_routes = sun4i_codec_codec_dapm_routes,879 .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),880 .idle_bias_on = 1,881 .use_pmdown_time = 1,882 .endianness = 1,883};884 885/*** sun6i Codec ***/886 887/* mixer controls */888static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {889 SOC_DAPM_DOUBLE("DAC Playback Switch",890 SUN6I_CODEC_OM_DACA_CTRL,891 SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,892 SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),893 SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",894 SUN6I_CODEC_OM_DACA_CTRL,895 SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,896 SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),897 SOC_DAPM_DOUBLE("Line In Playback Switch",898 SUN6I_CODEC_OM_DACA_CTRL,899 SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,900 SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),901 SOC_DAPM_DOUBLE("Mic1 Playback Switch",902 SUN6I_CODEC_OM_DACA_CTRL,903 SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1,904 SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0),905 SOC_DAPM_DOUBLE("Mic2 Playback Switch",906 SUN6I_CODEC_OM_DACA_CTRL,907 SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2,908 SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),909};910 911/* ADC mixer controls */912static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = {913 SOC_DAPM_DOUBLE("Mixer Capture Switch",914 SUN6I_CODEC_ADC_ACTL,915 SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL,916 SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0),917 SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch",918 SUN6I_CODEC_ADC_ACTL,919 SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR,920 SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0),921 SOC_DAPM_DOUBLE("Line In Capture Switch",922 SUN6I_CODEC_ADC_ACTL,923 SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL,924 SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0),925 SOC_DAPM_DOUBLE("Mic1 Capture Switch",926 SUN6I_CODEC_ADC_ACTL,927 SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1,928 SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0),929 SOC_DAPM_DOUBLE("Mic2 Capture Switch",930 SUN6I_CODEC_ADC_ACTL,931 SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2,932 SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0),933};934 935/* headphone controls */936static const char * const sun6i_codec_hp_src_enum_text[] = {937 "DAC", "Mixer",938};939 940static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,941 SUN6I_CODEC_OM_DACA_CTRL,942 SUN6I_CODEC_OM_DACA_CTRL_LHPIS,943 SUN6I_CODEC_OM_DACA_CTRL_RHPIS,944 sun6i_codec_hp_src_enum_text);945 946static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {947 SOC_DAPM_ENUM("Headphone Source Playback Route",948 sun6i_codec_hp_src_enum),949};950 951/* microphone controls */952static const char * const sun6i_codec_mic2_src_enum_text[] = {953 "Mic2", "Mic3",954};955 956static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum,957 SUN6I_CODEC_MIC_CTRL,958 SUN6I_CODEC_MIC_CTRL_MIC2SLT,959 sun6i_codec_mic2_src_enum_text);960 961static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = {962 SOC_DAPM_ENUM("Mic2 Amplifier Source Route",963 sun6i_codec_mic2_src_enum),964};965 966/* line out controls */967static const char * const sun6i_codec_lineout_src_enum_text[] = {968 "Stereo", "Mono Differential",969};970 971static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum,972 SUN6I_CODEC_MIC_CTRL,973 SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC,974 SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC,975 sun6i_codec_lineout_src_enum_text);976 977static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = {978 SOC_DAPM_ENUM("Line Out Source Playback Route",979 sun6i_codec_lineout_src_enum),980};981 982/* volume / mute controls */983static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);984static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);985static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,986 -450, 150, 0);987static const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale,988 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),989 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),990);991static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale,992 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),993 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),994);995 996static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {997 SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,998 SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,999 sun6i_codec_dvol_scale),1000 SOC_SINGLE_TLV("Headphone Playback Volume",1001 SUN6I_CODEC_OM_DACA_CTRL,1002 SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,1003 sun6i_codec_hp_vol_scale),1004 SOC_SINGLE_TLV("Line Out Playback Volume",1005 SUN6I_CODEC_MIC_CTRL,1006 SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0,1007 sun6i_codec_lineout_vol_scale),1008 SOC_DOUBLE("Headphone Playback Switch",1009 SUN6I_CODEC_OM_DACA_CTRL,1010 SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,1011 SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),1012 SOC_DOUBLE("Line Out Playback Switch",1013 SUN6I_CODEC_MIC_CTRL,1014 SUN6I_CODEC_MIC_CTRL_LINEOUTLEN,1015 SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0),1016 /* Mixer pre-gains */1017 SOC_SINGLE_TLV("Line In Playback Volume",1018 SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,1019 0x7, 0, sun6i_codec_out_mixer_pregain_scale),1020 SOC_SINGLE_TLV("Mic1 Playback Volume",1021 SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G,1022 0x7, 0, sun6i_codec_out_mixer_pregain_scale),1023 SOC_SINGLE_TLV("Mic2 Playback Volume",1024 SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G,1025 0x7, 0, sun6i_codec_out_mixer_pregain_scale),1026 1027 /* Microphone Amp boost gains */1028 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL,1029 SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0,1030 sun6i_codec_mic_gain_scale),1031 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,1032 SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,1033 sun6i_codec_mic_gain_scale),1034 SOC_DOUBLE_TLV("ADC Capture Volume",1035 SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG,1036 SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0,1037 sun6i_codec_out_mixer_pregain_scale),1038};1039 1040static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {1041 /* Microphone inputs */1042 SND_SOC_DAPM_INPUT("MIC1"),1043 SND_SOC_DAPM_INPUT("MIC2"),1044 SND_SOC_DAPM_INPUT("MIC3"),1045 1046 /* Microphone Bias */1047 SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL,1048 SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0),1049 SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL,1050 SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0),1051 1052 /* Mic input path */1053 SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route",1054 SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src),1055 SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL,1056 SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0),1057 SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL,1058 SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0),1059 1060 /* Line In */1061 SND_SOC_DAPM_INPUT("LINEIN"),1062 1063 /* Digital parts of the ADCs */1064 SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,1065 SUN6I_CODEC_ADC_FIFOC_EN_AD, 0,1066 NULL, 0),1067 1068 /* Analog parts of the ADCs */1069 SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,1070 SUN6I_CODEC_ADC_ACTL_ADCLEN, 0),1071 SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,1072 SUN6I_CODEC_ADC_ACTL_ADCREN, 0),1073 1074 /* ADC Mixers */1075 SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,1076 sun6i_codec_adc_mixer_controls),1077 SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,1078 sun6i_codec_adc_mixer_controls),1079 1080 /* Digital parts of the DACs */1081 SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,1082 SUN4I_CODEC_DAC_DPC_EN_DA, 0,1083 NULL, 0),1084 1085 /* Analog parts of the DACs */1086 SND_SOC_DAPM_DAC("Left DAC", "Codec Playback",1087 SUN6I_CODEC_OM_DACA_CTRL,1088 SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),1089 SND_SOC_DAPM_DAC("Right DAC", "Codec Playback",1090 SUN6I_CODEC_OM_DACA_CTRL,1091 SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),1092 1093 /* Mixers */1094 SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,1095 SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,1096 sun6i_codec_mixer_controls),1097 SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,1098 SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,1099 sun6i_codec_mixer_controls),1100 1101 /* Headphone output path */1102 SND_SOC_DAPM_MUX("Headphone Source Playback Route",1103 SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),1104 SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,1105 SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),1106 SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN6I_CODEC_OM_PA_CTRL,1107 SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0),1108 SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN6I_CODEC_OM_PA_CTRL,1109 SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0),1110 SND_SOC_DAPM_OUTPUT("HP"),1111 1112 /* Line Out path */1113 SND_SOC_DAPM_MUX("Line Out Source Playback Route",1114 SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src),1115 SND_SOC_DAPM_OUTPUT("LINEOUT"),1116};1117 1118static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {1119 /* DAC Routes */1120 { "Left DAC", NULL, "DAC Enable" },1121 { "Right DAC", NULL, "DAC Enable" },1122 1123 /* Microphone Routes */1124 { "Mic1 Amplifier", NULL, "MIC1"},1125 { "Mic2 Amplifier Source Route", "Mic2", "MIC2" },1126 { "Mic2 Amplifier Source Route", "Mic3", "MIC3" },1127 { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"},1128 1129 /* Left Mixer Routes */1130 { "Left Mixer", "DAC Playback Switch", "Left DAC" },1131 { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },1132 { "Left Mixer", "Line In Playback Switch", "LINEIN" },1133 { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },1134 { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },1135 1136 /* Right Mixer Routes */1137 { "Right Mixer", "DAC Playback Switch", "Right DAC" },1138 { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },1139 { "Right Mixer", "Line In Playback Switch", "LINEIN" },1140 { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },1141 { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },1142 1143 /* Left ADC Mixer Routes */1144 { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },1145 { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },1146 { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },1147 { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },1148 { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },1149 1150 /* Right ADC Mixer Routes */1151 { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },1152 { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },1153 { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },1154 { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },1155 { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },1156 1157 /* Headphone Routes */1158 { "Headphone Source Playback Route", "DAC", "Left DAC" },1159 { "Headphone Source Playback Route", "DAC", "Right DAC" },1160 { "Headphone Source Playback Route", "Mixer", "Left Mixer" },1161 { "Headphone Source Playback Route", "Mixer", "Right Mixer" },1162 { "Headphone Amp", NULL, "Headphone Source Playback Route" },1163 { "HP", NULL, "Headphone Amp" },1164 { "HPCOM", NULL, "HPCOM Protection" },1165 1166 /* Line Out Routes */1167 { "Line Out Source Playback Route", "Stereo", "Left Mixer" },1168 { "Line Out Source Playback Route", "Stereo", "Right Mixer" },1169 { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },1170 { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },1171 { "LINEOUT", NULL, "Line Out Source Playback Route" },1172 1173 /* ADC Routes */1174 { "Left ADC", NULL, "ADC Enable" },1175 { "Right ADC", NULL, "ADC Enable" },1176 { "Left ADC", NULL, "Left ADC Mixer" },1177 { "Right ADC", NULL, "Right ADC Mixer" },1178};1179 1180static const struct snd_soc_component_driver sun6i_codec_codec = {1181 .controls = sun6i_codec_codec_widgets,1182 .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets),1183 .dapm_widgets = sun6i_codec_codec_dapm_widgets,1184 .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),1185 .dapm_routes = sun6i_codec_codec_dapm_routes,1186 .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes),1187 .idle_bias_on = 1,1188 .use_pmdown_time = 1,1189 .endianness = 1,1190};1191 1192/* sun8i A23 codec */1193static const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = {1194 SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,1195 SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,1196 sun6i_codec_dvol_scale),1197};1198 1199static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = {1200 /* Digital parts of the ADCs */1201 SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,1202 SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),1203 /* Digital parts of the DACs */1204 SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,1205 SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),1206 1207};1208 1209static const struct snd_soc_component_driver sun8i_a23_codec_codec = {1210 .controls = sun8i_a23_codec_codec_controls,1211 .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls),1212 .dapm_widgets = sun8i_a23_codec_codec_widgets,1213 .num_dapm_widgets = ARRAY_SIZE(sun8i_a23_codec_codec_widgets),1214 .idle_bias_on = 1,1215 .use_pmdown_time = 1,1216 .endianness = 1,1217};1218 1219static const struct snd_soc_component_driver sun4i_codec_component = {1220 .name = "sun4i-codec",1221 .legacy_dai_naming = 1,1222#ifdef CONFIG_DEBUG_FS1223 .debugfs_prefix = "cpu",1224#endif1225};1226 1227#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \1228 SNDRV_PCM_FMTBIT_S32_LE)1229 1230static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)1231{1232 struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);1233 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);1234 1235 snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,1236 &scodec->capture_dma_data);1237 1238 return 0;1239}1240 1241static const struct snd_soc_dai_ops dummy_dai_ops = {1242 .probe = sun4i_codec_dai_probe,1243};1244 1245static struct snd_soc_dai_driver dummy_cpu_dai = {1246 .name = "sun4i-codec-cpu-dai",1247 .playback = {1248 .stream_name = "Playback",1249 .channels_min = 1,1250 .channels_max = 2,1251 .rates = SUN4I_CODEC_RATES,1252 .formats = SUN4I_CODEC_FORMATS,1253 .sig_bits = 24,1254 },1255 .capture = {1256 .stream_name = "Capture",1257 .channels_min = 1,1258 .channels_max = 2,1259 .rates = SUN4I_CODEC_RATES,1260 .formats = SUN4I_CODEC_FORMATS,1261 .sig_bits = 24,1262 },1263 .ops = &dummy_dai_ops,1264};1265 1266static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,1267 int *num_links)1268{1269 struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),1270 GFP_KERNEL);1271 struct snd_soc_dai_link_component *dlc = devm_kzalloc(dev,1272 3 * sizeof(*dlc), GFP_KERNEL);1273 if (!link || !dlc)1274 return NULL;1275 1276 link->cpus = &dlc[0];1277 link->codecs = &dlc[1];1278 link->platforms = &dlc[2];1279 1280 link->num_cpus = 1;1281 link->num_codecs = 1;1282 link->num_platforms = 1;1283 1284 link->name = "cdc";1285 link->stream_name = "CDC PCM";1286 link->codecs->dai_name = "Codec";1287 link->cpus->dai_name = dev_name(dev);1288 link->codecs->name = dev_name(dev);1289 link->platforms->name = dev_name(dev);1290 link->dai_fmt = SND_SOC_DAIFMT_I2S;1291 1292 *num_links = 1;1293 1294 return link;1295};1296 1297static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,1298 struct snd_kcontrol *k, int event)1299{1300 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);1301 1302 gpiod_set_value_cansleep(scodec->gpio_pa,1303 !!SND_SOC_DAPM_EVENT_ON(event));1304 1305 if (SND_SOC_DAPM_EVENT_ON(event)) {1306 /*1307 * Need a delay to wait for DAC to push the data. 700ms seems1308 * to be the best compromise not to feel this delay while1309 * playing a sound.1310 */1311 msleep(700);1312 }1313 1314 return 0;1315}1316 1317static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = {1318 SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),1319};1320 1321static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = {1322 { "Speaker", NULL, "HP Right" },1323 { "Speaker", NULL, "HP Left" },1324};1325 1326static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)1327{1328 struct snd_soc_card *card;1329 1330 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);1331 if (!card)1332 return ERR_PTR(-ENOMEM);1333 1334 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);1335 if (!card->dai_link)1336 return ERR_PTR(-ENOMEM);1337 1338 card->dev = dev;1339 card->owner = THIS_MODULE;1340 card->name = "sun4i-codec";1341 card->dapm_widgets = sun4i_codec_card_dapm_widgets;1342 card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets);1343 card->dapm_routes = sun4i_codec_card_dapm_routes;1344 card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes);1345 1346 return card;1347};1348 1349static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = {1350 SND_SOC_DAPM_HP("Headphone", NULL),1351 SND_SOC_DAPM_LINE("Line In", NULL),1352 SND_SOC_DAPM_LINE("Line Out", NULL),1353 SND_SOC_DAPM_MIC("Headset Mic", NULL),1354 SND_SOC_DAPM_MIC("Mic", NULL),1355 SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),1356};1357 1358static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)1359{1360 struct snd_soc_card *card;1361 int ret;1362 1363 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);1364 if (!card)1365 return ERR_PTR(-ENOMEM);1366 1367 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);1368 if (!card->dai_link)1369 return ERR_PTR(-ENOMEM);1370 1371 card->dev = dev;1372 card->owner = THIS_MODULE;1373 card->name = "A31 Audio Codec";1374 card->dapm_widgets = sun6i_codec_card_dapm_widgets;1375 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);1376 card->fully_routed = true;1377 1378 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");1379 if (ret)1380 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);1381 1382 return card;1383};1384 1385/* Connect digital side enables to analog side widgets */1386static const struct snd_soc_dapm_route sun8i_codec_card_routes[] = {1387 /* ADC Routes */1388 { "Left ADC", NULL, "ADC Enable" },1389 { "Right ADC", NULL, "ADC Enable" },1390 { "Codec Capture", NULL, "Left ADC" },1391 { "Codec Capture", NULL, "Right ADC" },1392 1393 /* DAC Routes */1394 { "Left DAC", NULL, "DAC Enable" },1395 { "Right DAC", NULL, "DAC Enable" },1396 { "Left DAC", NULL, "Codec Playback" },1397 { "Right DAC", NULL, "Codec Playback" },1398};1399 1400static struct snd_soc_aux_dev aux_dev = {1401 .dlc = COMP_EMPTY(),1402};1403 1404static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev)1405{1406 struct snd_soc_card *card;1407 int ret;1408 1409 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);1410 if (!card)1411 return ERR_PTR(-ENOMEM);1412 1413 aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,1414 "allwinner,codec-analog-controls",1415 0);1416 if (!aux_dev.dlc.of_node) {1417 dev_err(dev, "Can't find analog controls for codec.\n");1418 return ERR_PTR(-EINVAL);1419 }1420 1421 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);1422 if (!card->dai_link)1423 return ERR_PTR(-ENOMEM);1424 1425 card->dev = dev;1426 card->owner = THIS_MODULE;1427 card->name = "A23 Audio Codec";1428 card->dapm_widgets = sun6i_codec_card_dapm_widgets;1429 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);1430 card->dapm_routes = sun8i_codec_card_routes;1431 card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);1432 card->aux_dev = &aux_dev;1433 card->num_aux_devs = 1;1434 card->fully_routed = true;1435 1436 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");1437 if (ret)1438 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);1439 1440 return card;1441};1442 1443static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev)1444{1445 struct snd_soc_card *card;1446 int ret;1447 1448 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);1449 if (!card)1450 return ERR_PTR(-ENOMEM);1451 1452 aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,1453 "allwinner,codec-analog-controls",1454 0);1455 if (!aux_dev.dlc.of_node) {1456 dev_err(dev, "Can't find analog controls for codec.\n");1457 return ERR_PTR(-EINVAL);1458 }1459 1460 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);1461 if (!card->dai_link)1462 return ERR_PTR(-ENOMEM);1463 1464 card->dev = dev;1465 card->owner = THIS_MODULE;1466 card->name = "H3 Audio Codec";1467 card->dapm_widgets = sun6i_codec_card_dapm_widgets;1468 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);1469 card->dapm_routes = sun8i_codec_card_routes;1470 card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);1471 card->aux_dev = &aux_dev;1472 card->num_aux_devs = 1;1473 card->fully_routed = true;1474 1475 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");1476 if (ret)1477 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);1478 1479 return card;1480};1481 1482static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)1483{1484 struct snd_soc_card *card;1485 int ret;1486 1487 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);1488 if (!card)1489 return ERR_PTR(-ENOMEM);1490 1491 aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,1492 "allwinner,codec-analog-controls",1493 0);1494 if (!aux_dev.dlc.of_node) {1495 dev_err(dev, "Can't find analog controls for codec.\n");1496 return ERR_PTR(-EINVAL);1497 }1498 1499 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);1500 if (!card->dai_link)1501 return ERR_PTR(-ENOMEM);1502 1503 card->dev = dev;1504 card->owner = THIS_MODULE;1505 card->name = "V3s Audio Codec";1506 card->dapm_widgets = sun6i_codec_card_dapm_widgets;1507 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);1508 card->dapm_routes = sun8i_codec_card_routes;1509 card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);1510 card->aux_dev = &aux_dev;1511 card->num_aux_devs = 1;1512 card->fully_routed = true;1513 1514 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");1515 if (ret)1516 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);1517 1518 return card;1519};1520 1521static const struct regmap_config sun4i_codec_regmap_config = {1522 .reg_bits = 32,1523 .reg_stride = 4,1524 .val_bits = 32,1525 .max_register = SUN4I_CODEC_ADC_RXCNT,1526};1527 1528static const struct regmap_config sun6i_codec_regmap_config = {1529 .reg_bits = 32,1530 .reg_stride = 4,1531 .val_bits = 32,1532 .max_register = SUN6I_CODEC_HMIC_DATA,1533};1534 1535static const struct regmap_config sun7i_codec_regmap_config = {1536 .reg_bits = 32,1537 .reg_stride = 4,1538 .val_bits = 32,1539 .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,1540};1541 1542static const struct regmap_config sun8i_a23_codec_regmap_config = {1543 .reg_bits = 32,1544 .reg_stride = 4,1545 .val_bits = 32,1546 .max_register = SUN8I_A23_CODEC_ADC_RXCNT,1547};1548 1549static const struct regmap_config sun8i_h3_codec_regmap_config = {1550 .reg_bits = 32,1551 .reg_stride = 4,1552 .val_bits = 32,1553 .max_register = SUN8I_H3_CODEC_ADC_DBG,1554};1555 1556static const struct regmap_config sun8i_v3s_codec_regmap_config = {1557 .reg_bits = 32,1558 .reg_stride = 4,1559 .val_bits = 32,1560 .max_register = SUN8I_H3_CODEC_ADC_DBG,1561};1562 1563struct sun4i_codec_quirks {1564 const struct regmap_config *regmap_config;1565 const struct snd_soc_component_driver *codec;1566 struct snd_soc_card * (*create_card)(struct device *dev);1567 struct reg_field reg_adc_fifoc; /* used for regmap_field */1568 unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */1569 unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */1570 bool has_reset;1571};1572 1573static const struct sun4i_codec_quirks sun4i_codec_quirks = {1574 .regmap_config = &sun4i_codec_regmap_config,1575 .codec = &sun4i_codec_codec,1576 .create_card = sun4i_codec_create_card,1577 .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),1578 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,1579 .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,1580};1581 1582static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {1583 .regmap_config = &sun6i_codec_regmap_config,1584 .codec = &sun6i_codec_codec,1585 .create_card = sun6i_codec_create_card,1586 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),1587 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,1588 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,1589 .has_reset = true,1590};1591 1592static const struct sun4i_codec_quirks sun7i_codec_quirks = {1593 .regmap_config = &sun7i_codec_regmap_config,1594 .codec = &sun7i_codec_codec,1595 .create_card = sun4i_codec_create_card,1596 .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),1597 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,1598 .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,1599};1600 1601static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = {1602 .regmap_config = &sun8i_a23_codec_regmap_config,1603 .codec = &sun8i_a23_codec_codec,1604 .create_card = sun8i_a23_codec_create_card,1605 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),1606 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,1607 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,1608 .has_reset = true,1609};1610 1611static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {1612 .regmap_config = &sun8i_h3_codec_regmap_config,1613 /*1614 * TODO Share the codec structure with A23 for now.1615 * This should be split out when adding digital audio1616 * processing support for the H3.1617 */1618 .codec = &sun8i_a23_codec_codec,1619 .create_card = sun8i_h3_codec_create_card,1620 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),1621 .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,1622 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,1623 .has_reset = true,1624};1625 1626static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {1627 .regmap_config = &sun8i_v3s_codec_regmap_config,1628 /*1629 * TODO The codec structure should be split out, like1630 * H3, when adding digital audio processing support.1631 */1632 .codec = &sun8i_a23_codec_codec,1633 .create_card = sun8i_v3s_codec_create_card,1634 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),1635 .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,1636 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,1637 .has_reset = true,1638};1639 1640static const struct of_device_id sun4i_codec_of_match[] = {1641 {1642 .compatible = "allwinner,sun4i-a10-codec",1643 .data = &sun4i_codec_quirks,1644 },1645 {1646 .compatible = "allwinner,sun6i-a31-codec",1647 .data = &sun6i_a31_codec_quirks,1648 },1649 {1650 .compatible = "allwinner,sun7i-a20-codec",1651 .data = &sun7i_codec_quirks,1652 },1653 {1654 .compatible = "allwinner,sun8i-a23-codec",1655 .data = &sun8i_a23_codec_quirks,1656 },1657 {1658 .compatible = "allwinner,sun8i-h3-codec",1659 .data = &sun8i_h3_codec_quirks,1660 },1661 {1662 .compatible = "allwinner,sun8i-v3s-codec",1663 .data = &sun8i_v3s_codec_quirks,1664 },1665 {}1666};1667MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);1668 1669static int sun4i_codec_probe(struct platform_device *pdev)1670{1671 struct snd_soc_card *card;1672 struct sun4i_codec *scodec;1673 const struct sun4i_codec_quirks *quirks;1674 struct resource *res;1675 void __iomem *base;1676 int ret;1677 1678 scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);1679 if (!scodec)1680 return -ENOMEM;1681 1682 scodec->dev = &pdev->dev;1683 1684 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);1685 if (IS_ERR(base))1686 return PTR_ERR(base);1687 1688 quirks = of_device_get_match_data(&pdev->dev);1689 if (quirks == NULL) {1690 dev_err(&pdev->dev, "Failed to determine the quirks to use\n");1691 return -ENODEV;1692 }1693 1694 scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,1695 quirks->regmap_config);1696 if (IS_ERR(scodec->regmap)) {1697 dev_err(&pdev->dev, "Failed to create our regmap\n");1698 return PTR_ERR(scodec->regmap);1699 }1700 1701 /* Get the clocks from the DT */1702 scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");1703 if (IS_ERR(scodec->clk_apb)) {1704 dev_err(&pdev->dev, "Failed to get the APB clock\n");1705 return PTR_ERR(scodec->clk_apb);1706 }1707 1708 scodec->clk_module = devm_clk_get(&pdev->dev, "codec");1709 if (IS_ERR(scodec->clk_module)) {1710 dev_err(&pdev->dev, "Failed to get the module clock\n");1711 return PTR_ERR(scodec->clk_module);1712 }1713 1714 if (quirks->has_reset) {1715 scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,1716 NULL);1717 if (IS_ERR(scodec->rst)) {1718 dev_err(&pdev->dev, "Failed to get reset control\n");1719 return PTR_ERR(scodec->rst);1720 }1721 }1722 1723 scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",1724 GPIOD_OUT_LOW);1725 if (IS_ERR(scodec->gpio_pa)) {1726 ret = PTR_ERR(scodec->gpio_pa);1727 dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n");1728 return ret;1729 }1730 1731 /* reg_field setup */1732 scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev,1733 scodec->regmap,1734 quirks->reg_adc_fifoc);1735 if (IS_ERR(scodec->reg_adc_fifoc)) {1736 ret = PTR_ERR(scodec->reg_adc_fifoc);1737 dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",1738 ret);1739 return ret;1740 }1741 1742 /* Enable the bus clock */1743 if (clk_prepare_enable(scodec->clk_apb)) {1744 dev_err(&pdev->dev, "Failed to enable the APB clock\n");1745 return -EINVAL;1746 }1747 1748 /* Deassert the reset control */1749 if (scodec->rst) {1750 ret = reset_control_deassert(scodec->rst);1751 if (ret) {1752 dev_err(&pdev->dev,1753 "Failed to deassert the reset control\n");1754 goto err_clk_disable;1755 }1756 }1757 1758 /* DMA configuration for TX FIFO */1759 scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;1760 scodec->playback_dma_data.maxburst = 8;1761 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;1762 1763 /* DMA configuration for RX FIFO */1764 scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;1765 scodec->capture_dma_data.maxburst = 8;1766 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;1767 1768 ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec,1769 &sun4i_codec_dai, 1);1770 if (ret) {1771 dev_err(&pdev->dev, "Failed to register our codec\n");1772 goto err_assert_reset;1773 }1774 1775 ret = devm_snd_soc_register_component(&pdev->dev,1776 &sun4i_codec_component,1777 &dummy_cpu_dai, 1);1778 if (ret) {1779 dev_err(&pdev->dev, "Failed to register our DAI\n");1780 goto err_assert_reset;1781 }1782 1783 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);1784 if (ret) {1785 dev_err(&pdev->dev, "Failed to register against DMAEngine\n");1786 goto err_assert_reset;1787 }1788 1789 card = quirks->create_card(&pdev->dev);1790 if (IS_ERR(card)) {1791 ret = PTR_ERR(card);1792 dev_err(&pdev->dev, "Failed to create our card\n");1793 goto err_assert_reset;1794 }1795 1796 snd_soc_card_set_drvdata(card, scodec);1797 1798 ret = snd_soc_register_card(card);1799 if (ret) {1800 dev_err_probe(&pdev->dev, ret, "Failed to register our card\n");1801 goto err_assert_reset;1802 }1803 1804 return 0;1805 1806err_assert_reset:1807 if (scodec->rst)1808 reset_control_assert(scodec->rst);1809err_clk_disable:1810 clk_disable_unprepare(scodec->clk_apb);1811 return ret;1812}1813 1814static void sun4i_codec_remove(struct platform_device *pdev)1815{1816 struct snd_soc_card *card = platform_get_drvdata(pdev);1817 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);1818 1819 snd_soc_unregister_card(card);1820 if (scodec->rst)1821 reset_control_assert(scodec->rst);1822 clk_disable_unprepare(scodec->clk_apb);1823}1824 1825static struct platform_driver sun4i_codec_driver = {1826 .driver = {1827 .name = "sun4i-codec",1828 .of_match_table = sun4i_codec_of_match,1829 },1830 .probe = sun4i_codec_probe,1831 .remove = sun4i_codec_remove,1832};1833module_platform_driver(sun4i_codec_driver);1834 1835MODULE_DESCRIPTION("Allwinner A10 codec driver");1836MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");1837MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");1838MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");1839MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");1840MODULE_LICENSE("GPL");1841