cs42l51.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /*
  2. * cs42l51.c
  3. *
  4. * ASoC Driver for Cirrus Logic CS42L51 codecs
  5. *
  6. * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
  7. *
  8. * Based on cs4270.c - Copyright (c) Freescale Semiconductor
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License version 2 as
  12. * published by the Free Software Foundation.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * For now:
  20. * - Only I2C is support. Not SPI
  21. * - master mode *NOT* supported
  22. */
  23. #include <linux/module.h>
  24. #include <linux/slab.h>
  25. #include <sound/core.h>
  26. #include <sound/soc.h>
  27. #include <sound/tlv.h>
  28. #include <sound/initval.h>
  29. #include <sound/pcm_params.h>
  30. #include <sound/pcm.h>
  31. #include <linux/regmap.h>
  32. #include "cs42l51.h"
  33. enum master_slave_mode {
  34. MODE_SLAVE,
  35. MODE_SLAVE_AUTO,
  36. MODE_MASTER,
  37. };
  38. struct cs42l51_private {
  39. unsigned int mclk;
  40. unsigned int audio_mode; /* The mode (I2S or left-justified) */
  41. enum master_slave_mode func;
  42. };
  43. #define CS42L51_FORMATS ( \
  44. SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
  45. SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
  46. SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
  47. SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
  48. static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
  49. struct snd_ctl_elem_value *ucontrol)
  50. {
  51. struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
  52. unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
  53. switch (value) {
  54. default:
  55. case 0:
  56. ucontrol->value.enumerated.item[0] = 0;
  57. break;
  58. /* same value : (L+R)/2 and (R+L)/2 */
  59. case 1:
  60. case 2:
  61. ucontrol->value.enumerated.item[0] = 1;
  62. break;
  63. case 3:
  64. ucontrol->value.enumerated.item[0] = 2;
  65. break;
  66. }
  67. return 0;
  68. }
  69. #define CHAN_MIX_NORMAL 0x00
  70. #define CHAN_MIX_BOTH 0x55
  71. #define CHAN_MIX_SWAP 0xFF
  72. static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
  73. struct snd_ctl_elem_value *ucontrol)
  74. {
  75. struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
  76. unsigned char val;
  77. switch (ucontrol->value.enumerated.item[0]) {
  78. default:
  79. case 0:
  80. val = CHAN_MIX_NORMAL;
  81. break;
  82. case 1:
  83. val = CHAN_MIX_BOTH;
  84. break;
  85. case 2:
  86. val = CHAN_MIX_SWAP;
  87. break;
  88. }
  89. snd_soc_write(codec, CS42L51_PCM_MIXER, val);
  90. return 1;
  91. }
  92. static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
  93. static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
  94. static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
  95. static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
  96. static const char *chan_mix[] = {
  97. "L R",
  98. "L+R",
  99. "R L",
  100. };
  101. static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
  102. static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
  103. SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
  104. CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
  105. 0, 0x19, 0x7F, adc_pcm_tlv),
  106. SOC_DOUBLE_R("PCM Playback Switch",
  107. CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
  108. SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
  109. CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
  110. 0, 0x34, 0xE4, aout_tlv),
  111. SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
  112. CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
  113. 0, 0x19, 0x7F, adc_pcm_tlv),
  114. SOC_DOUBLE_R("ADC Mixer Switch",
  115. CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
  116. SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
  117. SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
  118. SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
  119. SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
  120. SOC_DOUBLE_TLV("Mic Boost Volume",
  121. CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
  122. SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
  123. SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
  124. SOC_ENUM_EXT("PCM channel mixer",
  125. cs42l51_chan_mix,
  126. cs42l51_get_chan_mix, cs42l51_set_chan_mix),
  127. };
  128. /*
  129. * to power down, one must:
  130. * 1.) Enable the PDN bit
  131. * 2.) enable power-down for the select channels
  132. * 3.) disable the PDN bit.
  133. */
  134. static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
  135. struct snd_kcontrol *kcontrol, int event)
  136. {
  137. struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
  138. switch (event) {
  139. case SND_SOC_DAPM_PRE_PMD:
  140. snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
  141. CS42L51_POWER_CTL1_PDN,
  142. CS42L51_POWER_CTL1_PDN);
  143. break;
  144. default:
  145. case SND_SOC_DAPM_POST_PMD:
  146. snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
  147. CS42L51_POWER_CTL1_PDN, 0);
  148. break;
  149. }
  150. return 0;
  151. }
  152. static const char *cs42l51_dac_names[] = {"Direct PCM",
  153. "DSP PCM", "ADC"};
  154. static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum,
  155. CS42L51_DAC_CTL, 6, cs42l51_dac_names);
  156. static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
  157. SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
  158. static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
  159. "MIC Left", "MIC+preamp Left"};
  160. static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum,
  161. CS42L51_ADC_INPUT, 4, cs42l51_adcl_names);
  162. static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
  163. SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
  164. static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
  165. "MIC Right", "MIC+preamp Right"};
  166. static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum,
  167. CS42L51_ADC_INPUT, 6, cs42l51_adcr_names);
  168. static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
  169. SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
  170. static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
  171. SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
  172. SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
  173. cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
  174. SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
  175. cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
  176. SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
  177. CS42L51_POWER_CTL1, 1, 1,
  178. cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
  179. SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
  180. CS42L51_POWER_CTL1, 2, 1,
  181. cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
  182. SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
  183. CS42L51_POWER_CTL1, 5, 1,
  184. cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
  185. SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
  186. CS42L51_POWER_CTL1, 6, 1,
  187. cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
  188. /* analog/mic */
  189. SND_SOC_DAPM_INPUT("AIN1L"),
  190. SND_SOC_DAPM_INPUT("AIN1R"),
  191. SND_SOC_DAPM_INPUT("AIN2L"),
  192. SND_SOC_DAPM_INPUT("AIN2R"),
  193. SND_SOC_DAPM_INPUT("MICL"),
  194. SND_SOC_DAPM_INPUT("MICR"),
  195. SND_SOC_DAPM_MIXER("Mic Preamp Left",
  196. CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
  197. SND_SOC_DAPM_MIXER("Mic Preamp Right",
  198. CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
  199. /* HP */
  200. SND_SOC_DAPM_OUTPUT("HPL"),
  201. SND_SOC_DAPM_OUTPUT("HPR"),
  202. /* mux */
  203. SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
  204. &cs42l51_dac_mux_controls),
  205. SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
  206. &cs42l51_adcl_mux_controls),
  207. SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
  208. &cs42l51_adcr_mux_controls),
  209. };
  210. static const struct snd_soc_dapm_route cs42l51_routes[] = {
  211. {"HPL", NULL, "Left DAC"},
  212. {"HPR", NULL, "Right DAC"},
  213. {"Left ADC", NULL, "Left PGA"},
  214. {"Right ADC", NULL, "Right PGA"},
  215. {"Mic Preamp Left", NULL, "MICL"},
  216. {"Mic Preamp Right", NULL, "MICR"},
  217. {"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" },
  218. {"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" },
  219. {"PGA-ADC Mux Left", "MIC Left", "MICL" },
  220. {"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" },
  221. {"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" },
  222. {"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" },
  223. {"PGA-ADC Mux Right", "MIC Right", "MICR" },
  224. {"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
  225. {"Left PGA", NULL, "PGA-ADC Mux Left"},
  226. {"Right PGA", NULL, "PGA-ADC Mux Right"},
  227. };
  228. static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
  229. unsigned int format)
  230. {
  231. struct snd_soc_codec *codec = codec_dai->codec;
  232. struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
  233. switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
  234. case SND_SOC_DAIFMT_I2S:
  235. case SND_SOC_DAIFMT_LEFT_J:
  236. case SND_SOC_DAIFMT_RIGHT_J:
  237. cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
  238. break;
  239. default:
  240. dev_err(codec->dev, "invalid DAI format\n");
  241. return -EINVAL;
  242. }
  243. switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
  244. case SND_SOC_DAIFMT_CBM_CFM:
  245. cs42l51->func = MODE_MASTER;
  246. break;
  247. case SND_SOC_DAIFMT_CBS_CFS:
  248. cs42l51->func = MODE_SLAVE_AUTO;
  249. break;
  250. default:
  251. dev_err(codec->dev, "Unknown master/slave configuration\n");
  252. return -EINVAL;
  253. }
  254. return 0;
  255. }
  256. struct cs42l51_ratios {
  257. unsigned int ratio;
  258. unsigned char speed_mode;
  259. unsigned char mclk;
  260. };
  261. static struct cs42l51_ratios slave_ratios[] = {
  262. { 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 },
  263. { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
  264. { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
  265. { 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 },
  266. { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
  267. { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
  268. { 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 },
  269. { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
  270. { 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 },
  271. { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
  272. { 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 },
  273. };
  274. static struct cs42l51_ratios slave_auto_ratios[] = {
  275. { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
  276. { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
  277. { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
  278. { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
  279. { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
  280. { 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 },
  281. { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
  282. { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
  283. };
  284. static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
  285. int clk_id, unsigned int freq, int dir)
  286. {
  287. struct snd_soc_codec *codec = codec_dai->codec;
  288. struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
  289. cs42l51->mclk = freq;
  290. return 0;
  291. }
  292. static int cs42l51_hw_params(struct snd_pcm_substream *substream,
  293. struct snd_pcm_hw_params *params,
  294. struct snd_soc_dai *dai)
  295. {
  296. struct snd_soc_codec *codec = dai->codec;
  297. struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
  298. int ret;
  299. unsigned int i;
  300. unsigned int rate;
  301. unsigned int ratio;
  302. struct cs42l51_ratios *ratios = NULL;
  303. int nr_ratios = 0;
  304. int intf_ctl, power_ctl, fmt;
  305. switch (cs42l51->func) {
  306. case MODE_MASTER:
  307. return -EINVAL;
  308. case MODE_SLAVE:
  309. ratios = slave_ratios;
  310. nr_ratios = ARRAY_SIZE(slave_ratios);
  311. break;
  312. case MODE_SLAVE_AUTO:
  313. ratios = slave_auto_ratios;
  314. nr_ratios = ARRAY_SIZE(slave_auto_ratios);
  315. break;
  316. }
  317. /* Figure out which MCLK/LRCK ratio to use */
  318. rate = params_rate(params); /* Sampling rate, in Hz */
  319. ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */
  320. for (i = 0; i < nr_ratios; i++) {
  321. if (ratios[i].ratio == ratio)
  322. break;
  323. }
  324. if (i == nr_ratios) {
  325. /* We did not find a matching ratio */
  326. dev_err(codec->dev, "could not find matching ratio\n");
  327. return -EINVAL;
  328. }
  329. intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
  330. power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
  331. intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
  332. | CS42L51_INTF_CTL_DAC_FORMAT(7));
  333. power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
  334. | CS42L51_MIC_POWER_CTL_MCLK_DIV2);
  335. switch (cs42l51->func) {
  336. case MODE_MASTER:
  337. intf_ctl |= CS42L51_INTF_CTL_MASTER;
  338. power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
  339. break;
  340. case MODE_SLAVE:
  341. power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
  342. break;
  343. case MODE_SLAVE_AUTO:
  344. power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
  345. break;
  346. }
  347. switch (cs42l51->audio_mode) {
  348. case SND_SOC_DAIFMT_I2S:
  349. intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
  350. intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
  351. break;
  352. case SND_SOC_DAIFMT_LEFT_J:
  353. intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
  354. break;
  355. case SND_SOC_DAIFMT_RIGHT_J:
  356. switch (params_width(params)) {
  357. case 16:
  358. fmt = CS42L51_DAC_DIF_RJ16;
  359. break;
  360. case 18:
  361. fmt = CS42L51_DAC_DIF_RJ18;
  362. break;
  363. case 20:
  364. fmt = CS42L51_DAC_DIF_RJ20;
  365. break;
  366. case 24:
  367. fmt = CS42L51_DAC_DIF_RJ24;
  368. break;
  369. default:
  370. dev_err(codec->dev, "unknown format\n");
  371. return -EINVAL;
  372. }
  373. intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
  374. break;
  375. default:
  376. dev_err(codec->dev, "unknown format\n");
  377. return -EINVAL;
  378. }
  379. if (ratios[i].mclk)
  380. power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
  381. ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
  382. if (ret < 0)
  383. return ret;
  384. ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
  385. if (ret < 0)
  386. return ret;
  387. return 0;
  388. }
  389. static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
  390. {
  391. struct snd_soc_codec *codec = dai->codec;
  392. int reg;
  393. int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
  394. reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
  395. if (mute)
  396. reg |= mask;
  397. else
  398. reg &= ~mask;
  399. return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
  400. }
  401. static const struct snd_soc_dai_ops cs42l51_dai_ops = {
  402. .hw_params = cs42l51_hw_params,
  403. .set_sysclk = cs42l51_set_dai_sysclk,
  404. .set_fmt = cs42l51_set_dai_fmt,
  405. .digital_mute = cs42l51_dai_mute,
  406. };
  407. static struct snd_soc_dai_driver cs42l51_dai = {
  408. .name = "cs42l51-hifi",
  409. .playback = {
  410. .stream_name = "Playback",
  411. .channels_min = 1,
  412. .channels_max = 2,
  413. .rates = SNDRV_PCM_RATE_8000_96000,
  414. .formats = CS42L51_FORMATS,
  415. },
  416. .capture = {
  417. .stream_name = "Capture",
  418. .channels_min = 1,
  419. .channels_max = 2,
  420. .rates = SNDRV_PCM_RATE_8000_96000,
  421. .formats = CS42L51_FORMATS,
  422. },
  423. .ops = &cs42l51_dai_ops,
  424. };
  425. static int cs42l51_codec_probe(struct snd_soc_codec *codec)
  426. {
  427. int ret, reg;
  428. /*
  429. * DAC configuration
  430. * - Use signal processor
  431. * - auto mute
  432. * - vol changes immediate
  433. * - no de-emphasize
  434. */
  435. reg = CS42L51_DAC_CTL_DATA_SEL(1)
  436. | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
  437. ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
  438. if (ret < 0)
  439. return ret;
  440. return 0;
  441. }
  442. static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
  443. .probe = cs42l51_codec_probe,
  444. .component_driver = {
  445. .controls = cs42l51_snd_controls,
  446. .num_controls = ARRAY_SIZE(cs42l51_snd_controls),
  447. .dapm_widgets = cs42l51_dapm_widgets,
  448. .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
  449. .dapm_routes = cs42l51_routes,
  450. .num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
  451. },
  452. };
  453. const struct regmap_config cs42l51_regmap = {
  454. .max_register = CS42L51_CHARGE_FREQ,
  455. .cache_type = REGCACHE_RBTREE,
  456. };
  457. EXPORT_SYMBOL_GPL(cs42l51_regmap);
  458. int cs42l51_probe(struct device *dev, struct regmap *regmap)
  459. {
  460. struct cs42l51_private *cs42l51;
  461. unsigned int val;
  462. int ret;
  463. if (IS_ERR(regmap))
  464. return PTR_ERR(regmap);
  465. cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private),
  466. GFP_KERNEL);
  467. if (!cs42l51)
  468. return -ENOMEM;
  469. dev_set_drvdata(dev, cs42l51);
  470. /* Verify that we have a CS42L51 */
  471. ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
  472. if (ret < 0) {
  473. dev_err(dev, "failed to read I2C\n");
  474. goto error;
  475. }
  476. if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
  477. (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
  478. dev_err(dev, "Invalid chip id: %x\n", val);
  479. ret = -ENODEV;
  480. goto error;
  481. }
  482. dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
  483. val & CS42L51_CHIP_REV_MASK);
  484. ret = snd_soc_register_codec(dev,
  485. &soc_codec_device_cs42l51, &cs42l51_dai, 1);
  486. error:
  487. return ret;
  488. }
  489. EXPORT_SYMBOL_GPL(cs42l51_probe);
  490. const struct of_device_id cs42l51_of_match[] = {
  491. { .compatible = "cirrus,cs42l51", },
  492. { }
  493. };
  494. MODULE_DEVICE_TABLE(of, cs42l51_of_match);
  495. EXPORT_SYMBOL_GPL(cs42l51_of_match);
  496. MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
  497. MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
  498. MODULE_LICENSE("GPL");