ddp_pwm.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. #include <linux/kernel.h>
  2. #include <linux/time.h>
  3. #include <linux/delay.h>
  4. #include <linux/sched.h>
  5. #include <linux/of.h>
  6. #include <linux/of_address.h>
  7. #include <asm/atomic.h>
  8. /* #include <mach/mt_reg_base.h> */
  9. #ifdef CONFIG_MTK_CLKMGR
  10. #include <mach/mt_clkmgr.h>
  11. #else
  12. #if defined(CONFIG_ARCH_MT6755) || defined(CONFIG_ARCH_MT6797)
  13. #include <ddp_clkmgr.h>
  14. #endif
  15. #endif
  16. #include <ddp_pwm_mux.h>
  17. /* #include <mach/mt_gpio.h> */
  18. #include <disp_dts_gpio.h> /* DTS GPIO */
  19. #include <leds_drv.h>
  20. #include <leds_sw.h>
  21. #include <ddp_reg.h>
  22. #include <ddp_path.h>
  23. #include <primary_display.h>
  24. #include <ddp_drv.h>
  25. #include <ddp_pwm.h>
  26. #define PWM_DEFAULT_DIV_VALUE 0x0
  27. #define PWM_ERR(fmt, arg...) pr_err("[PWM] " fmt "\n", ##arg)
  28. #define PWM_NOTICE(fmt, arg...) pr_warn("[PWM] " fmt "\n", ##arg)
  29. #define PWM_MSG(fmt, arg...) pr_debug("[PWM] " fmt "\n", ##arg)
  30. #define pwm_get_reg_base(id) (DISPSYS_PWM0_BASE)
  31. #define index_of_pwm(id) (0)
  32. #define PWM_LOG_BUFFER_SIZE 5
  33. static disp_pwm_id_t g_pwm_main_id = DISP_PWM0;
  34. static atomic_t g_pwm_backlight[1] = { ATOMIC_INIT(-1) };
  35. static volatile int g_pwm_max_backlight[1] = { 1023 };
  36. static ddp_module_notify g_ddp_notify;
  37. static DEFINE_SPINLOCK(g_pwm_log_lock);
  38. typedef struct {
  39. unsigned int value;
  40. unsigned long tsec;
  41. unsigned long tusec;
  42. } PWM_LOG;
  43. enum PWM_LOG_TYPE {
  44. NOTICE_LOG = 0,
  45. MSG_LOG,
  46. };
  47. static PWM_LOG g_pwm_log_buffer[PWM_LOG_BUFFER_SIZE + 1];
  48. static int g_pwm_log_index;
  49. static volatile bool g_pwm_force_backlight_update;
  50. int disp_pwm_get_cust_led(unsigned int *clocksource, unsigned int *clockdiv)
  51. {
  52. struct device_node *led_node = NULL;
  53. int ret = 0;
  54. int pwm_config[5] = { 0 };
  55. PWM_MSG("get_cust_led_dtsi: get the leds info from device tree\n");
  56. led_node = of_find_compatible_node(NULL, NULL, "mediatek,lcd-backlight");
  57. if (!led_node) {
  58. ret = -1;
  59. PWM_MSG("Cannot find LED node from dts\n");
  60. } else {
  61. ret = of_property_read_u32_array(led_node, "pwm_config", pwm_config,
  62. ARRAY_SIZE(pwm_config));
  63. if (!ret) {
  64. PWM_MSG("The backlight's pwm config data is %d %d %d %d %d\n",
  65. pwm_config[0], pwm_config[1], pwm_config[2], pwm_config[3], pwm_config[4]);
  66. *clocksource = pwm_config[0];
  67. *clockdiv = pwm_config[1];
  68. } else {
  69. PWM_MSG("led dts can not get pwm config data.\n");
  70. }
  71. }
  72. if (!ret)
  73. PWM_MSG("The backlight's pwm config mode src:%d div:%d\n", *clocksource, *clockdiv);
  74. else
  75. PWM_ERR("get pwm cust info fail");
  76. return ret;
  77. }
  78. void disp_pwm_set_force_update_flag(void)
  79. {
  80. g_pwm_force_backlight_update = true;
  81. PWM_NOTICE("disp_pwm_set_force_update_flag (%d)", g_pwm_force_backlight_update);
  82. }
  83. static int disp_pwm_config_init(DISP_MODULE_ENUM module, disp_ddp_path_config *pConfig, void *cmdq)
  84. {
  85. unsigned int pwm_div, pwm_src;
  86. /* disp_pwm_id_t id = DISP_PWM0; */
  87. unsigned long reg_base = pwm_get_reg_base(DISP_PWM0);
  88. int index = index_of_pwm(DISP_PWM0);
  89. int ret;
  90. pwm_div = PWM_DEFAULT_DIV_VALUE;
  91. #if 1
  92. ret = disp_pwm_get_cust_led(&pwm_src, &pwm_div);
  93. if (!ret) {
  94. disp_pwm_set_pwmmux(pwm_src);
  95. /* Some backlight chip/PMIC(e.g. MT6332) only accept slower clock */
  96. pwm_div = (pwm_div == 0) ? PWM_DEFAULT_DIV_VALUE : pwm_div;
  97. pwm_div &= 0x3FF;
  98. PWM_MSG("disp_pwm_init : PWM config data (%d,%d)", pwm_src, pwm_div);
  99. }
  100. #endif
  101. atomic_set(&g_pwm_backlight[index], -1);
  102. /* We don't enable PWM until we really need */
  103. DISP_REG_MASK(cmdq, reg_base + DISP_PWM_CON_0_OFF, pwm_div << 16, (0x3ff << 16));
  104. DISP_REG_MASK(cmdq, reg_base + DISP_PWM_CON_1_OFF, 1023, 0x3ff); /* 1024 levels */
  105. /* We don't init the backlight here until AAL/Android give */
  106. return 0;
  107. }
  108. static int disp_pwm_config(DISP_MODULE_ENUM module, disp_ddp_path_config *pConfig, void *cmdq)
  109. {
  110. int ret = 0;
  111. if (pConfig->dst_dirty)
  112. ret |= disp_pwm_config_init(module, pConfig, cmdq);
  113. return ret;
  114. }
  115. static void disp_pwm_trigger_refresh(disp_pwm_id_t id)
  116. {
  117. if (g_ddp_notify != NULL)
  118. g_ddp_notify(DISP_MODULE_PWM0, DISP_PATH_EVENT_TRIGGER);
  119. }
  120. /* Set the PWM which acts by default (e.g. ddp_bls_set_backlight) */
  121. void disp_pwm_set_main(disp_pwm_id_t main)
  122. {
  123. g_pwm_main_id = main;
  124. }
  125. disp_pwm_id_t disp_pwm_get_main(void)
  126. {
  127. return g_pwm_main_id;
  128. }
  129. int disp_pwm_is_enabled(disp_pwm_id_t id)
  130. {
  131. unsigned long reg_base = pwm_get_reg_base(id);
  132. return DISP_REG_GET(reg_base + DISP_PWM_EN_OFF) & 0x1;
  133. }
  134. static void disp_pwm_set_drverIC_en(disp_pwm_id_t id, int enabled)
  135. {
  136. #ifdef GPIO_LCM_LED_EN
  137. #ifndef CONFIG_MTK_FPGA
  138. if (id == DISP_PWM0) {
  139. mt_set_gpio_mode(GPIO_LCM_LED_EN, GPIO_MODE_00);
  140. mt_set_gpio_dir(GPIO_LCM_LED_EN, GPIO_DIR_OUT);
  141. if (enabled)
  142. mt_set_gpio_out(GPIO_LCM_LED_EN, GPIO_OUT_ONE);
  143. else
  144. mt_set_gpio_out(GPIO_LCM_LED_EN, GPIO_OUT_ZERO);
  145. }
  146. #endif
  147. #endif
  148. }
  149. static void disp_pwm_set_enabled(cmdqRecHandle cmdq, disp_pwm_id_t id, int enabled)
  150. {
  151. unsigned long reg_base = pwm_get_reg_base(id);
  152. if (enabled) {
  153. if (!disp_pwm_is_enabled(id)) {
  154. DISP_REG_MASK(cmdq, reg_base + DISP_PWM_EN_OFF, 0x1, 0x1);
  155. PWM_MSG("disp_pwm_set_enabled: PWN_EN = 0x1");
  156. disp_pwm_set_drverIC_en(id, enabled);
  157. }
  158. } else {
  159. DISP_REG_MASK(cmdq, reg_base + DISP_PWM_EN_OFF, 0x0, 0x1);
  160. disp_pwm_set_drverIC_en(id, enabled);
  161. }
  162. }
  163. int disp_bls_set_max_backlight(unsigned int level_1024)
  164. {
  165. return disp_pwm_set_max_backlight(disp_pwm_get_main(), level_1024);
  166. }
  167. int disp_pwm_set_max_backlight(disp_pwm_id_t id, unsigned int level_1024)
  168. {
  169. int index;
  170. if ((DISP_PWM_ALL & id) == 0) {
  171. PWM_ERR("[ERROR] disp_pwm_set_backlight: invalid PWM ID = 0x%x", id);
  172. return -EFAULT;
  173. }
  174. index = index_of_pwm(id);
  175. g_pwm_max_backlight[index] = level_1024;
  176. PWM_MSG("disp_pwm_set_max_backlight(id = 0x%x, level = %u)", id, level_1024);
  177. if (level_1024 < atomic_read(&g_pwm_backlight[index]))
  178. disp_pwm_set_backlight(id, level_1024);
  179. return 0;
  180. }
  181. int disp_pwm_get_max_backlight(disp_pwm_id_t id)
  182. {
  183. int index = index_of_pwm(id);
  184. return g_pwm_max_backlight[index];
  185. }
  186. /* For backward compatible */
  187. int disp_bls_set_backlight(int level_1024)
  188. {
  189. return disp_pwm_set_backlight(disp_pwm_get_main(), level_1024);
  190. }
  191. /*
  192. * If you want to re-map the backlight level from user space to
  193. * the real level of hardware output, please modify here.
  194. *
  195. * Inputs:
  196. * id - DISP_PWM0 / DISP_PWM1
  197. * level_1024 - Backlight value in [0, 1023]
  198. * Returns:
  199. * PWM duty in [0, 1023]
  200. */
  201. static int disp_pwm_level_remap(disp_pwm_id_t id, int level_1024)
  202. {
  203. return level_1024;
  204. }
  205. int disp_pwm_set_backlight(disp_pwm_id_t id, int level_1024)
  206. {
  207. int ret;
  208. #ifdef MTK_DISP_IDLE_LP
  209. disp_exit_idle_ex("disp_pwm_set_backlight");
  210. #endif
  211. /* Always write registers by CPU */
  212. ret = disp_pwm_set_backlight_cmdq(id, level_1024, NULL);
  213. if (ret >= 0)
  214. disp_pwm_trigger_refresh(id);
  215. return 0;
  216. }
  217. static volatile int g_pwm_duplicate_count;
  218. static void disp_pwm_log(int level_1024, int log_type)
  219. {
  220. int i;
  221. struct timeval pwm_time;
  222. char buffer[256] = "";
  223. int print_log;
  224. do_gettimeofday(&pwm_time);
  225. spin_lock(&g_pwm_log_lock);
  226. g_pwm_log_buffer[g_pwm_log_index].value = level_1024;
  227. g_pwm_log_buffer[g_pwm_log_index].tsec = (unsigned long)pwm_time.tv_sec % 1000;
  228. g_pwm_log_buffer[g_pwm_log_index].tusec = (unsigned long)pwm_time.tv_usec / 1000;
  229. g_pwm_log_index += 1;
  230. print_log = 0;
  231. if (g_pwm_log_index >= PWM_LOG_BUFFER_SIZE || level_1024 == 0) {
  232. sprintf(buffer + strlen(buffer), "(latest=%2u): ", g_pwm_log_index);
  233. for (i = 0; i < g_pwm_log_index; i += 1) {
  234. sprintf(buffer + strlen(buffer), "%5u(%4lu,%4lu)",
  235. g_pwm_log_buffer[i].value,
  236. g_pwm_log_buffer[i].tsec,
  237. g_pwm_log_buffer[i].tusec);
  238. }
  239. g_pwm_log_index = 0;
  240. print_log = 1;
  241. for (i = 0; i < PWM_LOG_BUFFER_SIZE; i += 1) {
  242. g_pwm_log_buffer[i].tsec = -1;
  243. g_pwm_log_buffer[i].tusec = -1;
  244. g_pwm_log_buffer[i].value = -1;
  245. }
  246. }
  247. spin_unlock(&g_pwm_log_lock);
  248. if (print_log == 1) {
  249. if (log_type == MSG_LOG)
  250. PWM_MSG("%s", buffer);
  251. else
  252. PWM_NOTICE("%s", buffer);
  253. }
  254. }
  255. int disp_pwm_set_backlight_cmdq(disp_pwm_id_t id, int level_1024, void *cmdq)
  256. {
  257. unsigned long reg_base;
  258. int old_pwm;
  259. int index;
  260. int abs_diff;
  261. bool force_update = false;
  262. if ((DISP_PWM_ALL & id) == 0) {
  263. PWM_ERR("[ERROR] disp_pwm_set_backlight_cmdq: invalid PWM ID = 0x%x", id);
  264. return -EFAULT;
  265. }
  266. /* we have to set backlight = 0 through CMDQ again to avoid timimg issue */
  267. if (g_pwm_force_backlight_update == true && cmdq != NULL)
  268. force_update = true;
  269. index = index_of_pwm(id);
  270. old_pwm = atomic_xchg(&g_pwm_backlight[index], level_1024);
  271. if (old_pwm != level_1024 || force_update == true) {
  272. if (force_update == true) {
  273. PWM_NOTICE("PWM force set backlight to 0 again\n");
  274. g_pwm_force_backlight_update = false;
  275. }
  276. abs_diff = level_1024 - old_pwm;
  277. if (abs_diff < 0)
  278. abs_diff = -abs_diff;
  279. if (old_pwm == 0 || level_1024 == 0 || abs_diff > 64) {
  280. /* To be printed in UART log */
  281. disp_pwm_log(level_1024, MSG_LOG);
  282. PWM_NOTICE("disp_pwm_set_backlight_cmdq(id = 0x%x, level_1024 = %d), old = %d", id, level_1024,
  283. old_pwm);
  284. } else {
  285. disp_pwm_log(level_1024, MSG_LOG);
  286. }
  287. if (level_1024 > g_pwm_max_backlight[index])
  288. level_1024 = g_pwm_max_backlight[index];
  289. else if (level_1024 < 0)
  290. level_1024 = 0;
  291. level_1024 = disp_pwm_level_remap(id, level_1024);
  292. reg_base = pwm_get_reg_base(id);
  293. DISP_REG_MASK(cmdq, reg_base + DISP_PWM_CON_1_OFF, level_1024 << 16, 0x1fff << 16);
  294. if (level_1024 > 0)
  295. disp_pwm_set_enabled(cmdq, id, 1);
  296. else
  297. disp_pwm_set_enabled(cmdq, id, 0); /* To save power */
  298. DISP_REG_MASK(cmdq, reg_base + DISP_PWM_COMMIT_OFF, 1, ~0);
  299. DISP_REG_MASK(cmdq, reg_base + DISP_PWM_COMMIT_OFF, 0, ~0);
  300. g_pwm_duplicate_count = 0;
  301. } else {
  302. g_pwm_duplicate_count = (g_pwm_duplicate_count + 1) & 63;
  303. }
  304. return 0;
  305. }
  306. static int ddp_pwm_power_on(DISP_MODULE_ENUM module, void *handle)
  307. {
  308. PWM_MSG("ddp_pwm_power_on: %d\n", module);
  309. #ifdef ENABLE_CLK_MGR
  310. if (module == DISP_MODULE_PWM0) {
  311. #ifdef CONFIG_MTK_CLKMGR /* MTK Clock Manager */
  312. #if defined(CONFIG_ARCH_MT6752)
  313. enable_clock(MT_CG_DISP1_DISP_PWM_26M, "PWM");
  314. enable_clock(MT_CG_DISP1_DISP_PWM_MM, "PWM");
  315. #elif defined(CONFIG_ARCH_MT6580)
  316. enable_clock(MT_CG_PWM_MM_SW_CG, "PWM");
  317. #else
  318. enable_clock(MT_CG_PERI_DISP_PWM, "DISP_PWM");
  319. #endif
  320. #else /* Common Clock Framework */
  321. ddp_clk_enable(DISP_PWM);
  322. #endif
  323. }
  324. #endif
  325. return 0;
  326. }
  327. static int ddp_pwm_power_off(DISP_MODULE_ENUM module, void *handle)
  328. {
  329. PWM_MSG("ddp_pwm_power_off: %d\n", module);
  330. #ifdef ENABLE_CLK_MGR
  331. if (module == DISP_MODULE_PWM0) {
  332. atomic_set(&g_pwm_backlight[0], 0);
  333. #ifdef CONFIG_MTK_CLKMGR /* MTK Clock Manager */
  334. #if defined(CONFIG_ARCH_MT6752)
  335. disable_clock(MT_CG_DISP1_DISP_PWM_26M, "PWM");
  336. disable_clock(MT_CG_DISP1_DISP_PWM_MM, "PWM");
  337. #elif defined(CONFIG_ARCH_MT6580)
  338. disable_clock(MT_CG_PWM_MM_SW_CG, "PWM");
  339. #else
  340. disable_clock(MT_CG_PERI_DISP_PWM, "DISP_PWM");
  341. #endif
  342. #else /* Common Clock Framework */
  343. ddp_clk_disable(DISP_PWM);
  344. #endif
  345. }
  346. #endif
  347. return 0;
  348. }
  349. static int ddp_pwm_init(DISP_MODULE_ENUM module, void *cmq_handle)
  350. {
  351. ddp_pwm_power_on(module, cmq_handle);
  352. return 0;
  353. }
  354. static int ddp_pwm_set_listener(DISP_MODULE_ENUM module, ddp_module_notify notify)
  355. {
  356. g_ddp_notify = notify;
  357. return 0;
  358. }
  359. DDP_MODULE_DRIVER ddp_driver_pwm = {
  360. .init = ddp_pwm_init,
  361. .config = disp_pwm_config,
  362. .power_on = ddp_pwm_power_on,
  363. .power_off = ddp_pwm_power_off,
  364. .set_listener = ddp_pwm_set_listener,
  365. };
  366. /* ---------------------------------------------------------------------- */
  367. /* Test code */
  368. /* Following is only for PWM functional test, not normal code */
  369. /* Will not be linked into user build. */
  370. /* ---------------------------------------------------------------------- */
  371. static void disp_pwm_test_source(const char *cmd)
  372. {
  373. unsigned long reg_base = pwm_get_reg_base(DISP_PWM0);
  374. int sel = (cmd[0] - '0') & 0x3;
  375. DISP_REG_MASK(NULL, reg_base + DISP_PWM_CON_0_OFF, (sel << 4), (0x3 << 4));
  376. }
  377. static void disp_pwm_test_grad(const char *cmd)
  378. {
  379. const unsigned long reg_grad = pwm_get_reg_base(DISP_PWM0) + 0x18;
  380. switch (cmd[0]) {
  381. case 'H':
  382. DISP_REG_SET(NULL, reg_grad, (1 << 16) | (1 << 8) | 1);
  383. disp_pwm_set_backlight(DISP_PWM0, 1023);
  384. break;
  385. case 'L':
  386. DISP_REG_SET(NULL, reg_grad, (1 << 16) | (1 << 8) | 1);
  387. disp_pwm_set_backlight(DISP_PWM0, 40);
  388. break;
  389. default:
  390. DISP_REG_SET(NULL, reg_grad, 0);
  391. disp_pwm_set_backlight(DISP_PWM0, 512);
  392. break;
  393. }
  394. }
  395. static void disp_pwm_test_div(const char *cmd)
  396. {
  397. const unsigned long reg_base = pwm_get_reg_base(DISP_PWM0);
  398. int div = cmd[0] - '0';
  399. if (div > 5)
  400. div = 5;
  401. DISP_REG_MASK(NULL, reg_base + DISP_PWM_CON_0_OFF, div << 16, (0x3ff << 16));
  402. disp_pwm_set_backlight(DISP_PWM0, 256 + div); /* to be applied */
  403. }
  404. static void disp_pwm_enable_debug(const char *cmd)
  405. {
  406. const unsigned long reg_base = pwm_get_reg_base(DISP_PWM0);
  407. if (cmd[0] == '1')
  408. DISP_REG_SET(NULL, reg_base + 0x20, 3);
  409. else
  410. DISP_REG_SET(NULL, reg_base + 0x20, 0);
  411. }
  412. static void disp_pwm_test_pin_mux(void)
  413. {
  414. const unsigned long reg_base = pwm_get_reg_base(DISP_PWM1);
  415. #if 0
  416. /* set gpio function for dvt test */
  417. mt_set_gpio_mode(GPIO157, GPIO_MODE_01); /* For DVT PIN MUX verification only, not normal path */
  418. mt_set_gpio_dir(GPIO157, GPIO_DIR_OUT); /* For DVT PIN MUX verification only, not normal path */
  419. mt_set_gpio_mode(GPIO4, GPIO_MODE_01);
  420. mt_set_gpio_dir(GPIO4, GPIO_DIR_OUT);
  421. mt_set_gpio_mode(GPIO14, GPIO_MODE_03);
  422. mt_set_gpio_dir(GPIO14, GPIO_DIR_OUT);
  423. mt_set_gpio_mode(GPIO127, GPIO_MODE_02);
  424. mt_set_gpio_dir(GPIO127, GPIO_DIR_OUT);
  425. mt_set_gpio_mode(GPIO134, GPIO_MODE_02);
  426. mt_set_gpio_dir(GPIO134, GPIO_DIR_OUT);
  427. mt_set_gpio_mode(GPIO153, GPIO_MODE_05);
  428. mt_set_gpio_dir(GPIO153, GPIO_DIR_OUT);
  429. mt_set_gpio_mode(GPIO186, GPIO_MODE_02);
  430. mt_set_gpio_dir(GPIO186, GPIO_DIR_OUT);
  431. #endif
  432. DISP_REG_MASK(NULL, reg_base + DISP_PWM_CON_1_OFF, 512 << 16, 0x1fff << 16);
  433. DISP_REG_MASK(NULL, reg_base + DISP_PWM_EN_OFF, 0x1, 0x1);
  434. DISP_REG_SET(NULL, reg_base + 0x20, 3);
  435. DISP_REG_MASK(NULL, reg_base + DISP_PWM_COMMIT_OFF, 1, ~0);
  436. DISP_REG_MASK(NULL, reg_base + DISP_PWM_COMMIT_OFF, 0, ~0);
  437. }
  438. static int pwm_simple_strtoul(char *ptr, unsigned long *res)
  439. {
  440. int i;
  441. char buffer[20];
  442. int end = 0;
  443. int ret = 0;
  444. for (i = 0; i < 20; i += 1) {
  445. end = i;
  446. PWM_MSG("%c\n", ptr[i]);
  447. if (ptr[i] < '0' || ptr[i] > '9')
  448. break;
  449. }
  450. if (end > 0) {
  451. strncpy(buffer, ptr, end);
  452. buffer[end] = '\0';
  453. ret = kstrtoul(buffer, 0, res);
  454. }
  455. return end;
  456. }
  457. static int pwm_parse_triple(const char *cmd, unsigned long *offset, unsigned long *value, unsigned long *mask)
  458. {
  459. int count = 0;
  460. char *next = (char *)cmd;
  461. int end;
  462. *value = 0;
  463. *mask = 0;
  464. end = pwm_simple_strtoul(next, offset);
  465. next += end;
  466. if (*offset > 0x1000UL || (*offset & 0x3UL) != 0) {
  467. *offset = 0UL;
  468. return 0;
  469. }
  470. count++;
  471. if (*next == ',')
  472. next++;
  473. end = pwm_simple_strtoul(next, value);
  474. next += end;
  475. count++;
  476. if (*next == ',')
  477. next++;
  478. end = pwm_simple_strtoul(next, mask);
  479. next += end;
  480. count++;
  481. return count;
  482. }
  483. static void disp_pwm_dump(void)
  484. {
  485. const unsigned long reg_base = pwm_get_reg_base(DISP_PWM0);
  486. int offset;
  487. PWM_NOTICE("[DUMP] Base = 0x%lx", reg_base);
  488. for (offset = 0; offset <= 0x28; offset += 4) {
  489. unsigned int val = DISP_REG_GET(reg_base + offset);
  490. PWM_NOTICE("[DUMP] [+0x%02x] = 0x%08x", offset, val);
  491. }
  492. }
  493. void disp_pwm_test(const char *cmd, char *debug_output)
  494. {
  495. unsigned long offset, value, mask;
  496. const unsigned long reg_base = pwm_get_reg_base(DISP_PWM0);
  497. debug_output[0] = '\0';
  498. PWM_NOTICE("disp_pwm_test(%s)", cmd);
  499. if (strncmp(cmd, "src:", 4) == 0) {
  500. disp_pwm_test_source(cmd + 4);
  501. } else if (strncmp(cmd, "div:", 4) == 0) {
  502. disp_pwm_test_div(cmd + 4);
  503. } else if (strncmp(cmd, "grad:", 5) == 0) {
  504. disp_pwm_test_grad(cmd + 5);
  505. } else if (strncmp(cmd, "dbg:", 4) == 0) {
  506. disp_pwm_enable_debug(cmd + 4);
  507. } else if (strncmp(cmd, "set:", 4) == 0) {
  508. int count = pwm_parse_triple(cmd + 4, &offset, &value, &mask);
  509. if (count == 3)
  510. DISP_REG_MASK(NULL, reg_base + offset, value, mask);
  511. else if (count == 2) {
  512. DISP_REG_SET(NULL, reg_base + offset, value);
  513. mask = 0xffffffff;
  514. }
  515. if (count >= 2)
  516. PWM_MSG("[+0x%03lx] = 0x%08lx(%lu) & 0x%08lx", offset, value, value, mask);
  517. } else if (strncmp(cmd, "dump", 4) == 0) {
  518. disp_pwm_dump();
  519. } else if (strncmp(cmd, "pinmux", 6) == 0) {
  520. disp_pwm_test_pin_mux();
  521. } else if (strncmp(cmd, "pwmmux:", 7) == 0) {
  522. unsigned int clksrc = 0;
  523. clksrc = (unsigned int)(cmd[7] - '0');
  524. disp_pwm_set_pwmmux(clksrc);
  525. }
  526. }