cirrus-cal-cs35l43.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. /*
  2. * Calibration support for CS35L43 Cirrus Logic Smart Amplifier
  3. *
  4. * Copyright 2021 Cirrus Logic
  5. *
  6. * Author: David Rhodes <[email protected]>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/uaccess.h>
  13. #include <linux/delay.h>
  14. #include <linux/regmap.h>
  15. #include <linux/slab.h>
  16. #include <linux/syscalls.h>
  17. #include <linux/file.h>
  18. #include <linux/fcntl.h>
  19. #include <linux/fs.h>
  20. #include <linux/init.h>
  21. #include <linux/platform_device.h>
  22. #include <asm/io.h>
  23. #include <linux/firmware.h>
  24. #include <linux/vmalloc.h>
  25. #include <linux/workqueue.h>
  26. #include <linux/power_supply.h>
  27. #include <linux/pm_runtime.h>
  28. #include <sound/cirrus/core.h>
  29. #include <sound/cirrus/calibration.h>
  30. #include "wm_adsp.h"
  31. #include "cs35l43.h"
  32. #include <linux/firmware/cirrus/wmfw.h>
  33. #include <linux/firmware/cirrus/cs_dsp.h>
  34. #define CIRRUS_CAL_COMPLETE_DELAY_MS 1250
  35. #define CIRRUS_CAL_RETRIES 2
  36. #define CIRRUS_CAL_AMBIENT_DEFAULT 23
  37. #define CIRRUS_CAL_CS35L43_ALG_ID_PROT 0x5f210
  38. #define CIRRUS_CAL_CS35L43_ALG_ID_SURFACE 0x5f222
  39. #define CIRRUS_CAL_CS35L43_TEMP_RADIX 14
  40. #define CIRRUS_CAL_CS35L43_SURFACE_TEMP_RADIX 16
  41. static unsigned long long cirrus_cal_rdc_to_ohms(unsigned long rdc)
  42. {
  43. return ((rdc * CIRRUS_CAL_AMP_CONSTANT_NUM) /
  44. CIRRUS_CAL_AMP_CONSTANT_DENOM);
  45. }
  46. static unsigned int cirrus_cal_vpk_to_mv(unsigned int vpk)
  47. {
  48. return (vpk * CIRRUS_CAL_VFS_MV) >> 19;
  49. }
  50. static int32_t cirrus_cal_sign_extend(uint32_t in)
  51. {
  52. uint8_t shift = 8;
  53. return (int32_t)(in << shift) >> shift;
  54. }
  55. static int cirrus_cal_get_power_temp(void)
  56. {
  57. union power_supply_propval value = {0};
  58. struct power_supply *psy;
  59. int ret;
  60. psy = power_supply_get_by_name("battery");
  61. if (!psy) {
  62. dev_warn(amp_group->cal_dev,
  63. "failed to get battery, assuming %d\n",
  64. CIRRUS_CAL_AMBIENT_DEFAULT);
  65. return CIRRUS_CAL_AMBIENT_DEFAULT;
  66. }
  67. ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &value);
  68. if (ret) {
  69. dev_warn(amp_group->cal_dev,
  70. "failed to get battery temp prop (%d), assuming %d\n",
  71. ret, CIRRUS_CAL_AMBIENT_DEFAULT);
  72. return CIRRUS_CAL_AMBIENT_DEFAULT;
  73. }
  74. return DIV_ROUND_CLOSEST(value.intval, 10);
  75. }
  76. static int cirrus_cal_cs35l43_start(void);
  77. static void cirrus_cal_cs35l43_complete(void)
  78. {
  79. struct cirrus_amp *amp;
  80. struct reg_sequence *post_config;
  81. struct regmap *regmap;
  82. unsigned long long ohms;
  83. int rdc, status, checksum, temp, cal_state, vsc, isc, i;
  84. int delay = msecs_to_jiffies(CIRRUS_CAL_COMPLETE_DELAY_MS);
  85. bool vsc_in_range, isc_in_range;
  86. bool cal_retry = false;
  87. dev_info(amp_group->cal_dev, "Complete Calibration\n");
  88. mutex_lock(&amp_group->cal_lock);
  89. for (i = 0; i < amp_group->num_amps; i++) {
  90. amp = &amp_group->amps[i];
  91. if (amp->calibration_disable)
  92. continue;
  93. regmap = amp->regmap;
  94. post_config = amp->post_config;
  95. cirrus_amp_read_ctl(amp, "CAL_STATUS", WMFW_ADSP2_XM,
  96. CIRRUS_CAL_CS35L43_ALG_ID_PROT, &status);
  97. cirrus_amp_read_ctl(amp, "CAL_R", WMFW_ADSP2_XM,
  98. CIRRUS_CAL_CS35L43_ALG_ID_PROT, &rdc);
  99. cirrus_amp_read_ctl(amp, "CAL_AMBIENT", WMFW_ADSP2_XM,
  100. CIRRUS_CAL_CS35L43_ALG_ID_PROT, &temp);
  101. cirrus_amp_read_ctl(amp, "CAL_CHECKSUM", WMFW_ADSP2_XM,
  102. CIRRUS_CAL_CS35L43_ALG_ID_PROT, &checksum);
  103. ohms = cirrus_cal_rdc_to_ohms((unsigned long)rdc);
  104. if (amp->perform_vimon_cal) {
  105. cirrus_amp_read_ctl(amp, "VIMON_CAL_VSC", WMFW_ADSP2_XM,
  106. amp->vimon_alg_id, &vsc);
  107. cirrus_amp_read_ctl(amp, "VIMON_CAL_ISC", WMFW_ADSP2_XM,
  108. amp->vimon_alg_id, &isc);
  109. cirrus_amp_read_ctl(amp, "VIMON_CAL_STATE",
  110. WMFW_ADSP2_XM, amp->vimon_alg_id,
  111. &cal_state);
  112. if (cal_state == CIRRUS_CAL_VIMON_STATUS_INVALID ||
  113. cal_state == 0) {
  114. dev_err(amp_group->cal_dev,
  115. "Error during VIMON cal, invalidating results\n");
  116. rdc = status = checksum = 0;
  117. }
  118. vsc_in_range = ((vsc <= amp->cal_vsc_ub) ||
  119. (vsc >= amp->cal_vsc_lb && vsc <= 0x00FFFFFF));
  120. isc_in_range = ((isc <= amp->cal_isc_ub) ||
  121. (isc >= amp->cal_isc_lb && isc <= 0x00FFFFFF));
  122. if (!vsc_in_range)
  123. dev_err(amp_group->cal_dev, "VIMON Cal %s (%s): VSC out of range (%x)\n",
  124. amp->dsp_part_name, amp->mfd_suffix, vsc);
  125. if (!isc_in_range)
  126. dev_err(amp_group->cal_dev, "VIMON Cal %s (%s): ISC out of range (%x)\n",
  127. amp->dsp_part_name, amp->mfd_suffix, isc);
  128. if (!vsc_in_range || !isc_in_range) {
  129. dev_err(amp_group->cal_dev, "VIMON cal out of range, invalidating results\n");
  130. rdc = status = checksum = 0;
  131. cirrus_amp_write_ctl(amp, "VIMON_CAL_STATE",
  132. WMFW_ADSP2_XM, amp->vimon_alg_id,
  133. CIRRUS_CAL_VIMON_STATUS_INVALID);
  134. if (amp_group->cal_retry < CIRRUS_CAL_RETRIES) {
  135. dev_info(amp_group->cal_dev, "Retry Calibration\n");
  136. cal_retry = true;
  137. }
  138. }
  139. } else {
  140. vsc = 0;
  141. isc = 0;
  142. cirrus_amp_write_ctl(amp, "VIMON_CAL_STATE",
  143. WMFW_ADSP2_XM, amp->vimon_alg_id,
  144. CIRRUS_CAL_VIMON_STATUS_INVALID);
  145. }
  146. dev_info(amp_group->cal_dev,
  147. "Calibration finished: %s (%s)\n",
  148. amp->dsp_part_name, amp->mfd_suffix);
  149. dev_info(amp_group->cal_dev, "Duration:\t%d ms\n",
  150. CIRRUS_CAL_COMPLETE_DELAY_MS);
  151. dev_info(amp_group->cal_dev, "Status:\t%d\n", status);
  152. if (status == CSPL_STATUS_OUT_OF_RANGE)
  153. dev_err(amp_group->cal_dev,
  154. "Calibration out of range\n");
  155. if (status == CSPL_STATUS_INCOMPLETE)
  156. dev_err(amp_group->cal_dev, "Calibration incomplete\n");
  157. dev_info(amp_group->cal_dev, "R :\t\t%d (%llu.%04llu Ohms)\n",
  158. rdc, ohms >> CIRRUS_CAL_RDC_RADIX,
  159. (ohms & (((1 << CIRRUS_CAL_RDC_RADIX) - 1))) *
  160. 10000 / (1 << CIRRUS_CAL_RDC_RADIX));
  161. dev_info(amp_group->cal_dev, "Checksum:\t%d\n", checksum);
  162. dev_info(amp_group->cal_dev, "Ambient:\t%d\n", temp);
  163. usleep_range(5000, 5500);
  164. cirrus_amp_write_ctl(amp, "CAL_EN", WMFW_ADSP2_XM,
  165. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0);
  166. cirrus_amp_write_ctl(amp, "PILOT_THRESH", WMFW_ADSP2_XM,
  167. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0x611);
  168. cirrus_amp_write_ctl(amp, "CAL_R_SEL", WMFW_ADSP2_XM,
  169. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0xFFFFFF);
  170. regmap_write(amp->regmap, amp->mbox_cmd, CS35L43_MBOX_CMD_AUDIO_REINIT);
  171. regmap_multi_reg_write(regmap, post_config,
  172. amp->num_post_configs);
  173. amp->cal.efs_cache_rdc = rdc;
  174. amp->cal.efs_cache_vsc = vsc;
  175. amp->cal.efs_cache_isc = isc;
  176. amp_group->efs_cache_temp = temp;
  177. amp->cal.efs_cache_valid = 1;
  178. }
  179. if (cal_retry == true) {
  180. cirrus_cal_cs35l43_start();
  181. queue_delayed_work(system_unbound_wq,
  182. &amp_group->cal_complete_work,
  183. delay);
  184. amp_group->cal_retry++;
  185. } else {
  186. amp_group->cal_running = 0;
  187. }
  188. dev_dbg(amp_group->cal_dev, "Calibration complete\n");
  189. mutex_unlock(&amp_group->cal_lock);
  190. }
  191. static void cirrus_cal_cs35l43_v_val_complete(struct cirrus_amp *amps, int num_amps,
  192. bool separate)
  193. {
  194. struct regmap *regmap;
  195. struct reg_sequence *post_config;
  196. unsigned int mbox_cmd;
  197. int amp;
  198. dev_info(amp_group->cal_dev, "Complete v-val\n");
  199. for (amp = 0; amp < num_amps; amp++) {
  200. if (amps[amp].v_val_separate && !separate)
  201. continue;
  202. regmap = amps[amp].regmap;
  203. post_config = amps[amp].post_config;
  204. mbox_cmd = amps[amp].mbox_cmd;
  205. cirrus_amp_write_ctl(&amps[amp], "CAL_EN", WMFW_ADSP2_XM,
  206. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0);
  207. cirrus_amp_write_ctl(&amps[amp], "PILOT_THRESH", WMFW_ADSP2_XM,
  208. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0x611);
  209. cirrus_amp_write_ctl(&amps[amp], "CAL_R_SEL", WMFW_ADSP2_XM,
  210. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0xFFFFFF);
  211. regmap_write(regmap, mbox_cmd, CS35L43_MBOX_CMD_AUDIO_REINIT);
  212. regmap_multi_reg_write(regmap, post_config,
  213. amps[amp].num_post_configs);
  214. }
  215. dev_info(amp_group->cal_dev, "V validation complete\n");
  216. }
  217. static int cirrus_cal_cs35l43_vimon_cal_start(struct cirrus_amp *amp)
  218. {
  219. int ret = 0;
  220. ret = cirrus_amp_write_ctl(amp, "VIMON_LBST_DELY", WMFW_ADSP2_XM,
  221. amp->vimon_alg_id, CIRRUS_CAL_CLASSH_DELAY_50MS);
  222. if (ret < 0) {
  223. dev_err(amp_group->cal_dev, "Could not access control VIMON_LBST_DELY\n");
  224. return ret;
  225. }
  226. ret = cirrus_amp_write_ctl(amp, "VIMON_HBST_DELY", WMFW_ADSP2_XM,
  227. amp->vimon_alg_id, CIRRUS_CAL_CLASSD_DELAY_50MS);
  228. if (ret < 0) {
  229. dev_err(amp_group->cal_dev, "Could not access control VIMON_HBST_DELY\n");
  230. return ret;
  231. }
  232. regmap_write(amp->regmap, CS35L43_DSP1RX3_INPUT, CS35L43_INPUT_SRC_VBSTMON);
  233. cirrus_amp_write_ctl(amp, "VIMON_CAL_STATE", WMFW_ADSP2_XM,
  234. amp->vimon_alg_id, 0);
  235. return ret;
  236. }
  237. static int cirrus_cal_cs35l43_vimon_cal_complete(struct cirrus_amp *amp)
  238. {
  239. unsigned int vimon_cal, vsc, isc, spkmon_otp;
  240. bool vsc_in_range, isc_in_range;
  241. cirrus_amp_read_ctl(amp, "VIMON_CAL_STATE", WMFW_ADSP2_XM,
  242. amp->vimon_alg_id, &vimon_cal);
  243. cirrus_amp_read_ctl(amp, "VIMON_CAL_VSC", WMFW_ADSP2_XM, amp->vimon_alg_id, &vsc);
  244. cirrus_amp_read_ctl(amp, "VIMON_CAL_ISC", WMFW_ADSP2_XM, amp->vimon_alg_id, &isc);
  245. regmap_read(amp->regmap, CS35L43_SPKMON_OTP_3, &spkmon_otp);
  246. dev_info(amp_group->cal_dev,
  247. "VIMON Cal results %s (%s), status=%d vsc=%x isc=%x\n",
  248. amp->dsp_part_name, amp->mfd_suffix, vimon_cal, vsc, isc);
  249. dev_info(amp_group->cal_dev,
  250. "spkmon_otp_3=0x%x, vsc=0x%x, isc=0x%x\n", spkmon_otp,
  251. (spkmon_otp & CS35L43_VMON_BST_COEFF_MASK) >> CS35L43_VMON_BST_COEFF_SHIFT,
  252. (spkmon_otp & CS35L43_IMON_BST_COEFF_MASK) >> CS35L43_IMON_BST_COEFF_SHIFT);
  253. vsc_in_range = ((vsc <= amp->cal_vsc_ub) ||
  254. (vsc >= amp->cal_vsc_lb && vsc <= 0x00FFFFFF));
  255. isc_in_range = ((isc <= amp->cal_isc_ub) ||
  256. (isc >= amp->cal_isc_lb && isc <= 0x00FFFFFF));
  257. if (!vsc_in_range || !isc_in_range)
  258. vimon_cal = CIRRUS_CAL_VIMON_STATUS_INVALID;
  259. return vimon_cal;
  260. }
  261. static int cirrus_cal_wait_for_active(struct cirrus_amp *amp)
  262. {
  263. struct regmap *regmap = amp->regmap;
  264. unsigned int global_en;
  265. int timeout = 50;
  266. regmap_read(regmap, amp->global_en, &global_en);
  267. while ((global_en & amp->global_en_mask) == 0) {
  268. usleep_range(1000, 1500);
  269. regmap_read(regmap, amp->global_en, &global_en);
  270. }
  271. if (timeout == 0) {
  272. dev_err(amp_group->cal_dev, "Failed to setup calibration\n");
  273. return -EINVAL;
  274. }
  275. return 0;
  276. }
  277. static void cirrus_cal_cs35l43_redc_start(struct cirrus_amp *amp)
  278. {
  279. int ambient;
  280. dev_info(amp_group->cal_dev, "ReDC Calibration\n");
  281. cirrus_amp_write_ctl(amp, "CAL_EN", WMFW_ADSP2_XM,
  282. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 1);
  283. cirrus_amp_write_ctl(amp, "PILOT_THRESH", WMFW_ADSP2_XM,
  284. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0);
  285. cirrus_amp_write_ctl(amp, "CALIB_FIRST_RUN", WMFW_ADSP2_XM,
  286. CIRRUS_CAL_CS35L43_ALG_ID_PROT, 1);
  287. ambient = cirrus_cal_get_power_temp();
  288. cirrus_amp_write_ctl(amp, "CAL_AMBIENT", WMFW_ADSP2_XM,
  289. CIRRUS_CAL_CS35L43_ALG_ID_PROT, ambient);
  290. regmap_write(amp->regmap, amp->mbox_cmd, CS35L43_MBOX_CMD_AUDIO_REINIT);
  291. }
  292. int cirrus_cal_cs35l43_cal_apply(struct cirrus_amp *amp)
  293. {
  294. unsigned int temp, rdc, status, checksum, vsc, isc;
  295. unsigned int vimon_cal_status = CIRRUS_CAL_VIMON_STATUS_SUCCESS;
  296. int ret = 0;
  297. if (!amp)
  298. return 0;
  299. if (amp->cal.efs_cache_valid == 1) {
  300. rdc = amp->cal.efs_cache_rdc;
  301. vsc = amp->cal.efs_cache_vsc;
  302. isc = amp->cal.efs_cache_isc;
  303. vimon_cal_status = CIRRUS_CAL_VIMON_STATUS_SUCCESS;
  304. temp = amp_group->efs_cache_temp;
  305. } else if (amp->cal.efs_cache_rdc && amp_group->efs_cache_temp &&
  306. (!amp->cal.efs_cache_vsc && !amp->cal.efs_cache_isc)) {
  307. dev_info(amp_group->cal_dev,
  308. "No VIMON, writing RDC only\n");
  309. rdc = amp->cal.efs_cache_rdc;
  310. temp = amp_group->efs_cache_temp;
  311. vimon_cal_status = CIRRUS_CAL_VIMON_STATUS_INVALID;
  312. } else {
  313. dev_info(amp_group->cal_dev,
  314. "No saved EFS, writing defaults\n");
  315. rdc = amp->default_redc;
  316. temp = CIRRUS_CAL_AMBIENT_DEFAULT;
  317. vimon_cal_status = CIRRUS_CAL_VIMON_STATUS_INVALID;
  318. amp->cal.efs_cache_rdc = rdc;
  319. amp_group->efs_cache_temp = temp;
  320. }
  321. status = 1;
  322. checksum = status + rdc;
  323. dev_info(amp_group->cal_dev, "Writing calibration to %s (%s)\n",
  324. amp->dsp_part_name, amp->mfd_suffix);
  325. dev_info(amp_group->cal_dev,
  326. "RDC = %d, Temp = %d, Status = %d Checksum = %d\n",
  327. rdc, temp, status, checksum);
  328. cirrus_amp_write_ctl(amp, "CAL_R_SEL", WMFW_ADSP2_XM,
  329. CIRRUS_CAL_CS35L43_ALG_ID_PROT, rdc);
  330. if (!amp->perform_vimon_cal) {
  331. cirrus_amp_write_ctl(amp, "VIMON_CAL_STATE", WMFW_ADSP2_XM,
  332. amp->vimon_alg_id,
  333. CIRRUS_CAL_VIMON_STATUS_INVALID);
  334. goto skip_vimon_cal;
  335. }
  336. cirrus_amp_write_ctl(amp, "VIMON_CAL_STATE", WMFW_ADSP2_XM,
  337. amp->vimon_alg_id, vimon_cal_status);
  338. if (amp->perform_vimon_cal &&
  339. vimon_cal_status != CIRRUS_CAL_VIMON_STATUS_INVALID) {
  340. dev_info(amp_group->cal_dev,
  341. "VIMON Cal status=%d vsc=0x%x isc=0x%x\n",
  342. vimon_cal_status, vsc, isc);
  343. cirrus_amp_write_ctl(amp, "VIMON_CAL_VSC", WMFW_ADSP2_XM,
  344. amp->vimon_alg_id, vsc);
  345. cirrus_amp_write_ctl(amp, "VIMON_CAL_ISC", WMFW_ADSP2_XM,
  346. amp->vimon_alg_id, isc);
  347. regmap_write(amp->regmap, CS35L43_TEST_KEY_CTRL, 0x55);
  348. regmap_write(amp->regmap, CS35L43_TEST_KEY_CTRL, 0xAA);
  349. regmap_update_bits(amp->regmap, CS35L43_SPKMON_OTP_3,
  350. CS35L43_VMON_BST_COEFF_MASK,
  351. vsc << CS35L43_VMON_BST_COEFF_SHIFT);
  352. regmap_update_bits(amp->regmap, CS35L43_SPKMON_OTP_3,
  353. CS35L43_IMON_BST_COEFF_MASK,
  354. isc << CS35L43_IMON_BST_COEFF_SHIFT);
  355. regmap_write(amp->regmap, CS35L43_TEST_KEY_CTRL, 0xCC);
  356. regmap_write(amp->regmap, CS35L43_TEST_KEY_CTRL, 0x33);
  357. } else {
  358. dev_info(amp_group->cal_dev, "VIMON Cal status invalid\n");
  359. }
  360. skip_vimon_cal:
  361. return ret;
  362. }
  363. int cirrus_cal_cs35l43_read_temp(struct cirrus_amp *amp)
  364. {
  365. int reg = 0, ret;
  366. struct wm_adsp *adsp;
  367. unsigned int global_en;
  368. unsigned int halo_alg_id;
  369. if (!amp)
  370. goto err;
  371. if (pm_runtime_enabled(amp->component->dev) &&
  372. pm_runtime_status_suspended(amp->component->dev))
  373. goto err;
  374. regmap_read(amp->regmap, amp->global_en, &global_en);
  375. if ((global_en & amp->global_en_mask) == 0)
  376. goto err;
  377. if (amp_group->cal_running)
  378. goto err;
  379. adsp = snd_soc_component_get_drvdata(amp->component);
  380. halo_alg_id = adsp->cs_dsp.fw_id;
  381. ret = cirrus_amp_read_ctl(amp, "HALO_HEARTBEAT", WMFW_ADSP2_XM,
  382. halo_alg_id, &reg);
  383. if (reg == 0 || ret < 0)
  384. goto err;
  385. ret = cirrus_amp_read_ctl(amp, "AUDIO_STATE", WMFW_ADSP2_XM,
  386. 0x5f212, &reg);
  387. if (reg == 0 || ret < 0)
  388. goto err;
  389. ret = cirrus_amp_read_ctl(amp, "TEMP_COIL_C", WMFW_ADSP2_XM,
  390. CIRRUS_CAL_CS35L43_ALG_ID_PROT, &reg);
  391. reg = cirrus_cal_sign_extend(reg);
  392. if (ret == 0) {
  393. dev_info(amp_group->cal_dev,
  394. "Read temp: %d.%04d degrees C\n",
  395. reg >> CIRRUS_CAL_CS35L43_TEMP_RADIX,
  396. (reg & (((1 << CIRRUS_CAL_CS35L43_TEMP_RADIX) - 1))) *
  397. 10000 / (1 << CIRRUS_CAL_CS35L43_TEMP_RADIX));
  398. return (reg >> CIRRUS_CAL_CS35L43_TEMP_RADIX);
  399. }
  400. err:
  401. return -1;
  402. }
  403. int cirrus_cal_cs35l43_set_surface_temp(struct cirrus_amp *amp, int temperature)
  404. {
  405. unsigned int global_en;
  406. if (!amp)
  407. return -EINVAL;
  408. if (pm_runtime_enabled(amp->component->dev) &&
  409. pm_runtime_status_suspended(amp->component->dev))
  410. return -EINVAL;
  411. regmap_read(amp->regmap, amp->global_en, &global_en);
  412. if ((global_en & amp->global_en_mask) == 0)
  413. return -EINVAL;
  414. if (temperature < 0) {
  415. dev_info(amp->component->dev, "Input surface temp: %d degrees\n", temperature);
  416. temperature = 0;
  417. }
  418. dev_info(amp->component->dev, "Set surface temp: %d degrees\n", temperature);
  419. cirrus_amp_write_ctl(amp, "SURFACE_TEMP", WMFW_ADSP2_XM,
  420. CIRRUS_CAL_CS35L43_ALG_ID_SURFACE,
  421. temperature << CIRRUS_CAL_CS35L43_SURFACE_TEMP_RADIX);
  422. return 0;
  423. }
  424. static int cirrus_cal_cs35l43_start(void)
  425. {
  426. bool vimon_calibration_failed = false;
  427. int amp;
  428. struct reg_sequence *config;
  429. struct regmap *regmap;
  430. int ret, vimon_cal_retries = 5;
  431. for (amp = 0; amp < amp_group->num_amps; amp++) {
  432. if (amp_group->amps[amp].calibration_disable)
  433. continue;
  434. cirrus_amp_write_ctl(&amp_group->amps[amp], "CAL_STATUS",
  435. WMFW_ADSP2_XM, CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0);
  436. cirrus_amp_write_ctl(&amp_group->amps[amp], "CAL_R",
  437. WMFW_ADSP2_XM, CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0);
  438. cirrus_amp_write_ctl(&amp_group->amps[amp], "CAL_AMBIENT",
  439. WMFW_ADSP2_XM, CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0);
  440. cirrus_amp_write_ctl(&amp_group->amps[amp], "CAL_CHECKSUM",
  441. WMFW_ADSP2_XM, CIRRUS_CAL_CS35L43_ALG_ID_PROT, 0);
  442. if (amp_group->amps[amp].perform_vimon_cal) {
  443. cirrus_amp_write_ctl(&amp_group->amps[amp], "VIMON_CAL_VSC",
  444. WMFW_ADSP2_XM,
  445. amp_group->amps[amp].vimon_alg_id, 0);
  446. cirrus_amp_write_ctl(&amp_group->amps[amp], "VIMON_CAL_ISC",
  447. WMFW_ADSP2_XM,
  448. amp_group->amps[amp].vimon_alg_id, 0);
  449. }
  450. ret = cirrus_cal_wait_for_active(&amp_group->amps[amp]);
  451. if (ret < 0) {
  452. dev_err(amp_group->cal_dev,
  453. "Could not start amp%s (%d)\n",
  454. amp_group->amps[amp].mfd_suffix, ret);
  455. return -ETIMEDOUT;
  456. }
  457. }
  458. do {
  459. vimon_calibration_failed = false;
  460. for (amp = 0; amp < amp_group->num_amps; amp++) {
  461. if (amp_group->amps[amp].calibration_disable ||
  462. vimon_calibration_failed)
  463. continue;
  464. regmap = amp_group->amps[amp].regmap;
  465. config = amp_group->amps[amp].pre_config;
  466. regmap_multi_reg_write(regmap, config,
  467. amp_group->amps[amp].num_pre_configs);
  468. if (amp_group->amps[amp].perform_vimon_cal) {
  469. ret = cirrus_cal_cs35l43_vimon_cal_start(&amp_group->amps[amp]);
  470. if (ret < 0) {
  471. dev_info(amp_group->cal_dev,
  472. "VIMON Calibration Start Error %s (%s)\n",
  473. amp_group->amps[amp].dsp_part_name,
  474. amp_group->amps[amp].mfd_suffix);
  475. vimon_calibration_failed = true;
  476. vimon_cal_retries = -1;
  477. }
  478. }
  479. }
  480. msleep(300);
  481. for (amp = 0; amp < amp_group->num_amps; amp++) {
  482. if (amp_group->amps[amp].calibration_disable ||
  483. vimon_calibration_failed)
  484. continue;
  485. if (amp_group->amps[amp].perform_vimon_cal) {
  486. ret = cirrus_cal_cs35l43_vimon_cal_complete(
  487. &amp_group->amps[amp]);
  488. if (ret != CIRRUS_CAL_VIMON_STATUS_SUCCESS) {
  489. vimon_calibration_failed = true;
  490. dev_info(amp_group->cal_dev,
  491. "VIMON Calibration Error %s (%s)\n",
  492. amp_group->amps[amp].dsp_part_name,
  493. amp_group->amps[amp].mfd_suffix);
  494. }
  495. }
  496. }
  497. vimon_cal_retries--;
  498. } while (vimon_cal_retries >= 0 && vimon_calibration_failed);
  499. for (amp = 0; amp < amp_group->num_amps; amp++) {
  500. if (amp_group->amps[amp].calibration_disable)
  501. continue;
  502. cirrus_cal_cs35l43_redc_start(&amp_group->amps[amp]);
  503. }
  504. return 0;
  505. }
  506. int cirrus_cal_cs35l43_v_val_start(struct cirrus_amp *amps, int num_amps, bool separate)
  507. {
  508. struct reg_sequence *config;
  509. struct regmap *regmap;
  510. unsigned int vmax[CIRRUS_MAX_AMPS] = {0, 0, 0, 0, 0, 0, 0, 0};
  511. unsigned int vmin[CIRRUS_MAX_AMPS] = {0, 0, 0, 0, 0, 0, 0, 0};
  512. int ret = 0, i, j, reg, count;
  513. for (i = 0; i < amp_group->num_amps; i++) {
  514. if (amps[i].v_val_separate && !separate)
  515. continue;
  516. regmap = amps[i].regmap;
  517. config = amps[i].pre_config;
  518. vmax[i] = 0;
  519. vmin[i] = INT_MAX;
  520. ret = cirrus_cal_wait_for_active(&amps[i]);
  521. if (ret < 0) {
  522. dev_err(amp_group->cal_dev,
  523. "Could not start amp%s\n",
  524. amps[i].mfd_suffix);
  525. goto err;
  526. }
  527. regmap_multi_reg_write(regmap, config,
  528. amps[i].num_pre_configs);
  529. cirrus_cal_cs35l43_redc_start(&amps[i]);
  530. }
  531. dev_info(amp_group->cal_dev, "V validation prepare complete\n");
  532. for (i = 0; i < 1000; i++) {
  533. count = 0;
  534. for (j = 0; j < num_amps; j++) {
  535. if (amps[j].v_val_separate && !separate)
  536. continue;
  537. cirrus_amp_read_ctl(&amps[j], "CAL_VOLTAGE_PEAK", WMFW_ADSP2_XM,
  538. CIRRUS_CAL_CS35L43_ALG_ID_PROT, &reg);
  539. if (reg > vmax[j])
  540. vmax[j] = reg;
  541. if (reg < vmin[j])
  542. vmin[j] = reg;
  543. cirrus_amp_read_ctl(&amps[j], "CAL_STATUS",
  544. WMFW_ADSP2_XM,
  545. CIRRUS_CAL_CS35L43_ALG_ID_PROT, &reg);
  546. if (reg != 0 && reg != CSPL_STATUS_INCOMPLETE)
  547. count++;
  548. }
  549. if (count == num_amps) {
  550. dev_info(amp_group->cal_dev,
  551. "V Validation complete (%d)\n", i);
  552. break;
  553. }
  554. }
  555. for (i = 0; i < num_amps; i++) {
  556. if (amps[i].v_val_separate && !separate)
  557. continue;
  558. dev_info(amp_group->cal_dev,
  559. "V Validation results for amp%s\n",
  560. amps[i].mfd_suffix);
  561. dev_dbg(amp_group->cal_dev, "V Max: 0x%x\n", vmax[i]);
  562. vmax[i] = cirrus_cal_vpk_to_mv(vmax[i]);
  563. dev_info(amp_group->cal_dev, "V Max: %d mV\n", vmax[i]);
  564. dev_dbg(amp_group->cal_dev, "V Min: 0x%x\n", vmin[i]);
  565. vmin[i] = cirrus_cal_vpk_to_mv(vmin[i]);
  566. dev_info(amp_group->cal_dev, "V Min: %d mV\n", vmin[i]);
  567. if (vmax[i] < CIRRUS_CAL_V_VAL_UB_MV &&
  568. vmax[i] > CIRRUS_CAL_V_VAL_LB_MV) {
  569. amps[i].cal.v_validation = 1;
  570. dev_info(amp_group->cal_dev,
  571. "V validation success\n");
  572. } else {
  573. amps[i].cal.v_validation = 0xCC;
  574. dev_err(amp_group->cal_dev,
  575. "V validation failed\n");
  576. }
  577. }
  578. cirrus_cal_cs35l43_v_val_complete(amps, num_amps, separate);
  579. return 0;
  580. err:
  581. return -1;
  582. }
  583. struct cirrus_cal_ops cirrus_cs35l43_cal_ops = {
  584. .controls = {
  585. { "CAL_R", CIRRUS_CAL_CS35L43_ALG_ID_PROT},
  586. { "CAL_CHECKSUM", CIRRUS_CAL_CS35L43_ALG_ID_PROT},
  587. { "CAL_R_INIT", CIRRUS_CAL_CS35L43_ALG_ID_PROT},
  588. },
  589. .cal_start = cirrus_cal_cs35l43_start,
  590. .cal_complete = cirrus_cal_cs35l43_complete,
  591. .v_val = cirrus_cal_cs35l43_v_val_start,
  592. .cal_apply = cirrus_cal_cs35l43_cal_apply,
  593. .read_temp = cirrus_cal_cs35l43_read_temp,
  594. .set_temp = cirrus_cal_cs35l43_set_surface_temp,
  595. };