sun8i-codec-analog.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * This driver supports the analog controls for the internal codec
  4. * found in Allwinner's A31s, A23, A33 and H3 SoCs.
  5. *
  6. * Copyright 2016 Chen-Yu Tsai <[email protected]>
  7. */
  8. #include <linux/io.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/of_device.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/regmap.h>
  15. #include <sound/soc.h>
  16. #include <sound/soc-dapm.h>
  17. #include <sound/tlv.h>
  18. #include "sun8i-adda-pr-regmap.h"
  19. /* Codec analog control register offsets and bit fields */
  20. #define SUN8I_ADDA_HP_VOLC 0x00
  21. #define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7
  22. #define SUN8I_ADDA_HP_VOLC_HP_VOL 0
  23. #define SUN8I_ADDA_LOMIXSC 0x01
  24. #define SUN8I_ADDA_LOMIXSC_MIC1 6
  25. #define SUN8I_ADDA_LOMIXSC_MIC2 5
  26. #define SUN8I_ADDA_LOMIXSC_PHONE 4
  27. #define SUN8I_ADDA_LOMIXSC_PHONEN 3
  28. #define SUN8I_ADDA_LOMIXSC_LINEINL 2
  29. #define SUN8I_ADDA_LOMIXSC_DACL 1
  30. #define SUN8I_ADDA_LOMIXSC_DACR 0
  31. #define SUN8I_ADDA_ROMIXSC 0x02
  32. #define SUN8I_ADDA_ROMIXSC_MIC1 6
  33. #define SUN8I_ADDA_ROMIXSC_MIC2 5
  34. #define SUN8I_ADDA_ROMIXSC_PHONE 4
  35. #define SUN8I_ADDA_ROMIXSC_PHONEP 3
  36. #define SUN8I_ADDA_ROMIXSC_LINEINR 2
  37. #define SUN8I_ADDA_ROMIXSC_DACR 1
  38. #define SUN8I_ADDA_ROMIXSC_DACL 0
  39. #define SUN8I_ADDA_DAC_PA_SRC 0x03
  40. #define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7
  41. #define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6
  42. #define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5
  43. #define SUN8I_ADDA_DAC_PA_SRC_LMIXEN 4
  44. #define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE 3
  45. #define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE 2
  46. #define SUN8I_ADDA_DAC_PA_SRC_RHPIS 1
  47. #define SUN8I_ADDA_DAC_PA_SRC_LHPIS 0
  48. #define SUN8I_ADDA_PHONEIN_GCTRL 0x04
  49. #define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG 4
  50. #define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG 0
  51. #define SUN8I_ADDA_LINEIN_GCTRL 0x05
  52. #define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4
  53. #define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0
  54. #define SUN8I_ADDA_MICIN_GCTRL 0x06
  55. #define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4
  56. #define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0
  57. #define SUN8I_ADDA_PAEN_HP_CTRL 0x07
  58. #define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7
  59. #define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */
  60. #define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC 5
  61. #define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN 4
  62. #define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL 2
  63. #define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE 1
  64. #define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE 0
  65. #define SUN8I_ADDA_PHONEOUT_CTRL 0x08
  66. #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG 5
  67. #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4
  68. #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1 3
  69. #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2 2
  70. #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX 1
  71. #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX 0
  72. #define SUN8I_ADDA_PHONE_GAIN_CTRL 0x09
  73. #define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL 3
  74. #define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG 0
  75. #define SUN8I_ADDA_MIC2G_CTRL 0x0a
  76. #define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN 7
  77. #define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST 4
  78. #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN 3
  79. #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN 2
  80. #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC 1
  81. #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC 0
  82. #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL 0x0b
  83. #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN 7
  84. #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN 6
  85. #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5
  86. #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN 3
  87. #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST 0
  88. #define SUN8I_ADDA_LADCMIXSC 0x0c
  89. #define SUN8I_ADDA_LADCMIXSC_MIC1 6
  90. #define SUN8I_ADDA_LADCMIXSC_MIC2 5
  91. #define SUN8I_ADDA_LADCMIXSC_PHONE 4
  92. #define SUN8I_ADDA_LADCMIXSC_PHONEN 3
  93. #define SUN8I_ADDA_LADCMIXSC_LINEINL 2
  94. #define SUN8I_ADDA_LADCMIXSC_OMIXRL 1
  95. #define SUN8I_ADDA_LADCMIXSC_OMIXRR 0
  96. #define SUN8I_ADDA_RADCMIXSC 0x0d
  97. #define SUN8I_ADDA_RADCMIXSC_MIC1 6
  98. #define SUN8I_ADDA_RADCMIXSC_MIC2 5
  99. #define SUN8I_ADDA_RADCMIXSC_PHONE 4
  100. #define SUN8I_ADDA_RADCMIXSC_PHONEP 3
  101. #define SUN8I_ADDA_RADCMIXSC_LINEINR 2
  102. #define SUN8I_ADDA_RADCMIXSC_OMIXR 1
  103. #define SUN8I_ADDA_RADCMIXSC_OMIXL 0
  104. #define SUN8I_ADDA_RES 0x0e
  105. #define SUN8I_ADDA_RES_MMICBIAS_SEL 4
  106. #define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL 0
  107. #define SUN8I_ADDA_ADC_AP_EN 0x0f
  108. #define SUN8I_ADDA_ADC_AP_EN_ADCREN 7
  109. #define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6
  110. #define SUN8I_ADDA_ADC_AP_EN_ADCG 0
  111. /* mixer controls */
  112. static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
  113. SOC_DAPM_DOUBLE_R("DAC Playback Switch",
  114. SUN8I_ADDA_LOMIXSC,
  115. SUN8I_ADDA_ROMIXSC,
  116. SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
  117. SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
  118. SUN8I_ADDA_LOMIXSC,
  119. SUN8I_ADDA_ROMIXSC,
  120. SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
  121. SOC_DAPM_DOUBLE_R("Line In Playback Switch",
  122. SUN8I_ADDA_LOMIXSC,
  123. SUN8I_ADDA_ROMIXSC,
  124. SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0),
  125. SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
  126. SUN8I_ADDA_LOMIXSC,
  127. SUN8I_ADDA_ROMIXSC,
  128. SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
  129. SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
  130. SUN8I_ADDA_LOMIXSC,
  131. SUN8I_ADDA_ROMIXSC,
  132. SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),
  133. };
  134. /* mixer controls */
  135. static const struct snd_kcontrol_new sun8i_v3s_codec_mixer_controls[] = {
  136. SOC_DAPM_DOUBLE_R("DAC Playback Switch",
  137. SUN8I_ADDA_LOMIXSC,
  138. SUN8I_ADDA_ROMIXSC,
  139. SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
  140. SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
  141. SUN8I_ADDA_LOMIXSC,
  142. SUN8I_ADDA_ROMIXSC,
  143. SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
  144. SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
  145. SUN8I_ADDA_LOMIXSC,
  146. SUN8I_ADDA_ROMIXSC,
  147. SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
  148. };
  149. /* ADC mixer controls */
  150. static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
  151. SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
  152. SUN8I_ADDA_LADCMIXSC,
  153. SUN8I_ADDA_RADCMIXSC,
  154. SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
  155. SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
  156. SUN8I_ADDA_LADCMIXSC,
  157. SUN8I_ADDA_RADCMIXSC,
  158. SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
  159. SOC_DAPM_DOUBLE_R("Line In Capture Switch",
  160. SUN8I_ADDA_LADCMIXSC,
  161. SUN8I_ADDA_RADCMIXSC,
  162. SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0),
  163. SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
  164. SUN8I_ADDA_LADCMIXSC,
  165. SUN8I_ADDA_RADCMIXSC,
  166. SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
  167. SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
  168. SUN8I_ADDA_LADCMIXSC,
  169. SUN8I_ADDA_RADCMIXSC,
  170. SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),
  171. };
  172. /* ADC mixer controls */
  173. static const struct snd_kcontrol_new sun8i_v3s_codec_adc_mixer_controls[] = {
  174. SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
  175. SUN8I_ADDA_LADCMIXSC,
  176. SUN8I_ADDA_RADCMIXSC,
  177. SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
  178. SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
  179. SUN8I_ADDA_LADCMIXSC,
  180. SUN8I_ADDA_RADCMIXSC,
  181. SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
  182. SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
  183. SUN8I_ADDA_LADCMIXSC,
  184. SUN8I_ADDA_RADCMIXSC,
  185. SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
  186. };
  187. /* volume / mute controls */
  188. static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,
  189. -450, 150, 0);
  190. static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
  191. 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
  192. 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
  193. );
  194. static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
  195. /* Mixer pre-gain */
  196. SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
  197. SUN8I_ADDA_MICIN_GCTRL_MIC1G,
  198. 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
  199. /* Microphone Amp boost gain */
  200. SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
  201. SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
  202. sun8i_codec_mic_gain_scale),
  203. /* ADC */
  204. SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
  205. SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,
  206. sun8i_codec_out_mixer_pregain_scale),
  207. };
  208. static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
  209. /* ADC */
  210. SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
  211. SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),
  212. SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
  213. SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),
  214. /* DAC */
  215. SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
  216. SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),
  217. SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
  218. SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),
  219. /*
  220. * Due to this component and the codec belonging to separate DAPM
  221. * contexts, we need to manually link the above widgets to their
  222. * stream widgets at the card level.
  223. */
  224. /* Microphone input */
  225. SND_SOC_DAPM_INPUT("MIC1"),
  226. /* Mic input path */
  227. SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
  228. SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
  229. };
  230. static const struct snd_soc_dapm_widget sun8i_codec_mixer_widgets[] = {
  231. SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
  232. SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
  233. sun8i_codec_mixer_controls,
  234. ARRAY_SIZE(sun8i_codec_mixer_controls)),
  235. SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
  236. SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
  237. sun8i_codec_mixer_controls,
  238. ARRAY_SIZE(sun8i_codec_mixer_controls)),
  239. SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
  240. SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
  241. sun8i_codec_adc_mixer_controls,
  242. ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
  243. SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
  244. SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
  245. sun8i_codec_adc_mixer_controls,
  246. ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
  247. };
  248. static const struct snd_soc_dapm_widget sun8i_v3s_codec_mixer_widgets[] = {
  249. SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
  250. SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
  251. sun8i_v3s_codec_mixer_controls,
  252. ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),
  253. SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
  254. SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
  255. sun8i_v3s_codec_mixer_controls,
  256. ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),
  257. SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
  258. SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
  259. sun8i_v3s_codec_adc_mixer_controls,
  260. ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),
  261. SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
  262. SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
  263. sun8i_v3s_codec_adc_mixer_controls,
  264. ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),
  265. };
  266. static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
  267. /* Microphone Routes */
  268. { "Mic1 Amplifier", NULL, "MIC1"},
  269. };
  270. static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = {
  271. /* Left Mixer Routes */
  272. { "Left Mixer", "DAC Playback Switch", "Left DAC" },
  273. { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
  274. { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
  275. /* Right Mixer Routes */
  276. { "Right Mixer", "DAC Playback Switch", "Right DAC" },
  277. { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
  278. { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
  279. /* Left ADC Mixer Routes */
  280. { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
  281. { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
  282. { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
  283. /* Right ADC Mixer Routes */
  284. { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
  285. { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
  286. { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
  287. /* ADC Routes */
  288. { "Left ADC", NULL, "Left ADC Mixer" },
  289. { "Right ADC", NULL, "Right ADC Mixer" },
  290. };
  291. /* headphone specific controls, widgets, and routes */
  292. static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
  293. static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
  294. SOC_SINGLE_TLV("Headphone Playback Volume",
  295. SUN8I_ADDA_HP_VOLC,
  296. SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
  297. sun8i_codec_hp_vol_scale),
  298. SOC_DOUBLE("Headphone Playback Switch",
  299. SUN8I_ADDA_DAC_PA_SRC,
  300. SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,
  301. SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),
  302. };
  303. static const char * const sun8i_codec_hp_src_enum_text[] = {
  304. "DAC", "Mixer",
  305. };
  306. static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,
  307. SUN8I_ADDA_DAC_PA_SRC,
  308. SUN8I_ADDA_DAC_PA_SRC_LHPIS,
  309. SUN8I_ADDA_DAC_PA_SRC_RHPIS,
  310. sun8i_codec_hp_src_enum_text);
  311. static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
  312. SOC_DAPM_ENUM("Headphone Source Playback Route",
  313. sun8i_codec_hp_src_enum),
  314. };
  315. static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w,
  316. struct snd_kcontrol *k, int event)
  317. {
  318. struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
  319. if (SND_SOC_DAPM_EVENT_ON(event)) {
  320. snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
  321. BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
  322. BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN));
  323. /*
  324. * Need a delay to have the amplifier up. 700ms seems the best
  325. * compromise between the time to let the amplifier up and the
  326. * time not to feel this delay while playing a sound.
  327. */
  328. msleep(700);
  329. } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
  330. snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
  331. BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
  332. 0x0);
  333. }
  334. return 0;
  335. }
  336. static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
  337. SND_SOC_DAPM_MUX("Headphone Source Playback Route",
  338. SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
  339. SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
  340. SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0,
  341. sun8i_headphone_amp_event,
  342. SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
  343. SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
  344. SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
  345. SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
  346. SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),
  347. SND_SOC_DAPM_OUTPUT("HP"),
  348. };
  349. static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {
  350. { "Headphone Source Playback Route", "DAC", "Left DAC" },
  351. { "Headphone Source Playback Route", "DAC", "Right DAC" },
  352. { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
  353. { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
  354. { "Headphone Amp", NULL, "Headphone Source Playback Route" },
  355. { "HPCOM", NULL, "HPCOM Protection" },
  356. { "HP", NULL, "Headphone Amp" },
  357. };
  358. static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
  359. {
  360. struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
  361. struct device *dev = cmpnt->dev;
  362. int ret;
  363. ret = snd_soc_add_component_controls(cmpnt,
  364. sun8i_codec_headphone_controls,
  365. ARRAY_SIZE(sun8i_codec_headphone_controls));
  366. if (ret) {
  367. dev_err(dev, "Failed to add Headphone controls: %d\n", ret);
  368. return ret;
  369. }
  370. ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,
  371. ARRAY_SIZE(sun8i_codec_headphone_widgets));
  372. if (ret) {
  373. dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);
  374. return ret;
  375. }
  376. ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,
  377. ARRAY_SIZE(sun8i_codec_headphone_routes));
  378. if (ret) {
  379. dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);
  380. return ret;
  381. }
  382. return 0;
  383. }
  384. /* mbias specific widget */
  385. static const struct snd_soc_dapm_widget sun8i_codec_mbias_widgets[] = {
  386. SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
  387. SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
  388. 0, NULL, 0),
  389. };
  390. static int sun8i_codec_add_mbias(struct snd_soc_component *cmpnt)
  391. {
  392. struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
  393. struct device *dev = cmpnt->dev;
  394. int ret;
  395. ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mbias_widgets,
  396. ARRAY_SIZE(sun8i_codec_mbias_widgets));
  397. if (ret)
  398. dev_err(dev, "Failed to add MBIAS DAPM widgets: %d\n", ret);
  399. return ret;
  400. }
  401. /* hmic specific widget */
  402. static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
  403. SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
  404. SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,
  405. 0, NULL, 0),
  406. };
  407. static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
  408. {
  409. struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
  410. struct device *dev = cmpnt->dev;
  411. int ret;
  412. ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,
  413. ARRAY_SIZE(sun8i_codec_hmic_widgets));
  414. if (ret)
  415. dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);
  416. return ret;
  417. }
  418. /* line in specific controls, widgets and rines */
  419. static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = {
  420. /* Mixer pre-gain */
  421. SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
  422. SUN8I_ADDA_LINEIN_GCTRL_LINEING,
  423. 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
  424. };
  425. static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = {
  426. /* Line input */
  427. SND_SOC_DAPM_INPUT("LINEIN"),
  428. };
  429. static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = {
  430. { "Left Mixer", "Line In Playback Switch", "LINEIN" },
  431. { "Right Mixer", "Line In Playback Switch", "LINEIN" },
  432. { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
  433. { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
  434. };
  435. static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt)
  436. {
  437. struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
  438. struct device *dev = cmpnt->dev;
  439. int ret;
  440. ret = snd_soc_add_component_controls(cmpnt,
  441. sun8i_codec_linein_controls,
  442. ARRAY_SIZE(sun8i_codec_linein_controls));
  443. if (ret) {
  444. dev_err(dev, "Failed to add Line In controls: %d\n", ret);
  445. return ret;
  446. }
  447. ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets,
  448. ARRAY_SIZE(sun8i_codec_linein_widgets));
  449. if (ret) {
  450. dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret);
  451. return ret;
  452. }
  453. ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes,
  454. ARRAY_SIZE(sun8i_codec_linein_routes));
  455. if (ret) {
  456. dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret);
  457. return ret;
  458. }
  459. return 0;
  460. }
  461. /* line out specific controls, widgets and routes */
  462. static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
  463. 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
  464. 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
  465. );
  466. static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {
  467. SOC_SINGLE_TLV("Line Out Playback Volume",
  468. SUN8I_ADDA_PHONE_GAIN_CTRL,
  469. SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,
  470. sun8i_codec_lineout_vol_scale),
  471. SOC_DOUBLE("Line Out Playback Switch",
  472. SUN8I_ADDA_MIC2G_CTRL,
  473. SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,
  474. SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),
  475. };
  476. static const char * const sun8i_codec_lineout_src_enum_text[] = {
  477. "Stereo", "Mono Differential",
  478. };
  479. static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,
  480. SUN8I_ADDA_MIC2G_CTRL,
  481. SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,
  482. SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,
  483. sun8i_codec_lineout_src_enum_text);
  484. static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {
  485. SOC_DAPM_ENUM("Line Out Source Playback Route",
  486. sun8i_codec_lineout_src_enum),
  487. };
  488. static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {
  489. SND_SOC_DAPM_MUX("Line Out Source Playback Route",
  490. SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),
  491. /* It is unclear if this is a buffer or gate, model it as a supply */
  492. SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,
  493. SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),
  494. SND_SOC_DAPM_OUTPUT("LINEOUT"),
  495. };
  496. static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {
  497. { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
  498. { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
  499. { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
  500. { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
  501. { "LINEOUT", NULL, "Line Out Source Playback Route" },
  502. { "LINEOUT", NULL, "Line Out Enable", },
  503. };
  504. static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
  505. {
  506. struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
  507. struct device *dev = cmpnt->dev;
  508. int ret;
  509. ret = snd_soc_add_component_controls(cmpnt,
  510. sun8i_codec_lineout_controls,
  511. ARRAY_SIZE(sun8i_codec_lineout_controls));
  512. if (ret) {
  513. dev_err(dev, "Failed to add Line Out controls: %d\n", ret);
  514. return ret;
  515. }
  516. ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,
  517. ARRAY_SIZE(sun8i_codec_lineout_widgets));
  518. if (ret) {
  519. dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);
  520. return ret;
  521. }
  522. ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,
  523. ARRAY_SIZE(sun8i_codec_lineout_routes));
  524. if (ret) {
  525. dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);
  526. return ret;
  527. }
  528. return 0;
  529. }
  530. /* mic2 specific controls, widgets and routes */
  531. static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = {
  532. /* Mixer pre-gain */
  533. SOC_SINGLE_TLV("Mic2 Playback Volume",
  534. SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
  535. 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
  536. /* Microphone Amp boost gain */
  537. SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
  538. SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
  539. sun8i_codec_mic_gain_scale),
  540. };
  541. static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = {
  542. /* Microphone input */
  543. SND_SOC_DAPM_INPUT("MIC2"),
  544. /* Mic input path */
  545. SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
  546. SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
  547. };
  548. static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = {
  549. { "Mic2 Amplifier", NULL, "MIC2"},
  550. { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
  551. { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
  552. { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
  553. { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
  554. };
  555. static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt)
  556. {
  557. struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
  558. struct device *dev = cmpnt->dev;
  559. int ret;
  560. ret = snd_soc_add_component_controls(cmpnt,
  561. sun8i_codec_mic2_controls,
  562. ARRAY_SIZE(sun8i_codec_mic2_controls));
  563. if (ret) {
  564. dev_err(dev, "Failed to add MIC2 controls: %d\n", ret);
  565. return ret;
  566. }
  567. ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets,
  568. ARRAY_SIZE(sun8i_codec_mic2_widgets));
  569. if (ret) {
  570. dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret);
  571. return ret;
  572. }
  573. ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes,
  574. ARRAY_SIZE(sun8i_codec_mic2_routes));
  575. if (ret) {
  576. dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret);
  577. return ret;
  578. }
  579. return 0;
  580. }
  581. struct sun8i_codec_analog_quirks {
  582. bool has_headphone;
  583. bool has_hmic;
  584. bool has_linein;
  585. bool has_lineout;
  586. bool has_mbias;
  587. bool has_mic2;
  588. };
  589. static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
  590. .has_headphone = true,
  591. .has_hmic = true,
  592. .has_linein = true,
  593. .has_mbias = true,
  594. .has_mic2 = true,
  595. };
  596. static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
  597. .has_linein = true,
  598. .has_lineout = true,
  599. .has_mbias = true,
  600. .has_mic2 = true,
  601. };
  602. static int sun8i_codec_analog_add_mixer(struct snd_soc_component *cmpnt,
  603. const struct sun8i_codec_analog_quirks *quirks)
  604. {
  605. struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
  606. struct device *dev = cmpnt->dev;
  607. int ret;
  608. if (!quirks->has_mic2 && !quirks->has_linein) {
  609. /*
  610. * Apply the special widget set which has uses a control
  611. * without MIC2 and Line In, for SoCs without these.
  612. * TODO: not all special cases are supported now, this case
  613. * is present because it's the case of V3s.
  614. */
  615. ret = snd_soc_dapm_new_controls(dapm,
  616. sun8i_v3s_codec_mixer_widgets,
  617. ARRAY_SIZE(sun8i_v3s_codec_mixer_widgets));
  618. if (ret) {
  619. dev_err(dev, "Failed to add V3s Mixer DAPM widgets: %d\n", ret);
  620. return ret;
  621. }
  622. } else {
  623. /* Apply the generic mixer widget set. */
  624. ret = snd_soc_dapm_new_controls(dapm,
  625. sun8i_codec_mixer_widgets,
  626. ARRAY_SIZE(sun8i_codec_mixer_widgets));
  627. if (ret) {
  628. dev_err(dev, "Failed to add Mixer DAPM widgets: %d\n", ret);
  629. return ret;
  630. }
  631. }
  632. ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mixer_routes,
  633. ARRAY_SIZE(sun8i_codec_mixer_routes));
  634. if (ret) {
  635. dev_err(dev, "Failed to add Mixer DAPM routes: %d\n", ret);
  636. return ret;
  637. }
  638. return 0;
  639. }
  640. static const struct sun8i_codec_analog_quirks sun8i_v3s_quirks = {
  641. .has_headphone = true,
  642. .has_hmic = true,
  643. };
  644. static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
  645. {
  646. struct device *dev = cmpnt->dev;
  647. const struct sun8i_codec_analog_quirks *quirks;
  648. int ret;
  649. /*
  650. * This would never return NULL unless someone directly registers a
  651. * platform device matching this driver's name, without specifying a
  652. * device tree node.
  653. */
  654. quirks = of_device_get_match_data(dev);
  655. /* Add controls, widgets, and routes for individual features */
  656. ret = sun8i_codec_analog_add_mixer(cmpnt, quirks);
  657. if (ret)
  658. return ret;
  659. if (quirks->has_headphone) {
  660. ret = sun8i_codec_add_headphone(cmpnt);
  661. if (ret)
  662. return ret;
  663. }
  664. if (quirks->has_hmic) {
  665. ret = sun8i_codec_add_hmic(cmpnt);
  666. if (ret)
  667. return ret;
  668. }
  669. if (quirks->has_linein) {
  670. ret = sun8i_codec_add_linein(cmpnt);
  671. if (ret)
  672. return ret;
  673. }
  674. if (quirks->has_lineout) {
  675. ret = sun8i_codec_add_lineout(cmpnt);
  676. if (ret)
  677. return ret;
  678. }
  679. if (quirks->has_mbias) {
  680. ret = sun8i_codec_add_mbias(cmpnt);
  681. if (ret)
  682. return ret;
  683. }
  684. if (quirks->has_mic2) {
  685. ret = sun8i_codec_add_mic2(cmpnt);
  686. if (ret)
  687. return ret;
  688. }
  689. return 0;
  690. }
  691. static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {
  692. .controls = sun8i_codec_common_controls,
  693. .num_controls = ARRAY_SIZE(sun8i_codec_common_controls),
  694. .dapm_widgets = sun8i_codec_common_widgets,
  695. .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets),
  696. .dapm_routes = sun8i_codec_common_routes,
  697. .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes),
  698. .probe = sun8i_codec_analog_cmpnt_probe,
  699. };
  700. static const struct of_device_id sun8i_codec_analog_of_match[] = {
  701. {
  702. .compatible = "allwinner,sun8i-a23-codec-analog",
  703. .data = &sun8i_a23_quirks,
  704. },
  705. {
  706. .compatible = "allwinner,sun8i-h3-codec-analog",
  707. .data = &sun8i_h3_quirks,
  708. },
  709. {
  710. .compatible = "allwinner,sun8i-v3s-codec-analog",
  711. .data = &sun8i_v3s_quirks,
  712. },
  713. {}
  714. };
  715. MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
  716. static int sun8i_codec_analog_probe(struct platform_device *pdev)
  717. {
  718. struct regmap *regmap;
  719. void __iomem *base;
  720. base = devm_platform_ioremap_resource(pdev, 0);
  721. if (IS_ERR(base)) {
  722. dev_err(&pdev->dev, "Failed to map the registers\n");
  723. return PTR_ERR(base);
  724. }
  725. regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
  726. if (IS_ERR(regmap)) {
  727. dev_err(&pdev->dev, "Failed to create regmap\n");
  728. return PTR_ERR(regmap);
  729. }
  730. return devm_snd_soc_register_component(&pdev->dev,
  731. &sun8i_codec_analog_cmpnt_drv,
  732. NULL, 0);
  733. }
  734. static struct platform_driver sun8i_codec_analog_driver = {
  735. .driver = {
  736. .name = "sun8i-codec-analog",
  737. .of_match_table = sun8i_codec_analog_of_match,
  738. },
  739. .probe = sun8i_codec_analog_probe,
  740. };
  741. module_platform_driver(sun8i_codec_analog_driver);
  742. MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");
  743. MODULE_AUTHOR("Chen-Yu Tsai <[email protected]>");
  744. MODULE_LICENSE("GPL");
  745. MODULE_ALIAS("platform:sun8i-codec-analog");