extd_multi_control.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. #include <linux/kthread.h>
  2. /*#include <linux/rtpm_prio.h>*/
  3. #include "extd_multi_control.h"
  4. #include "disp_drv_platform.h"
  5. #include "external_display.h"
  6. #include "extd_platform.h"
  7. #include "extd_log.h"
  8. #include "mtk_ovl.h"
  9. #ifdef CONFIG_SINGLE_PANEL_OUTPUT
  10. #include "extd_hdmi.h"
  11. #include <linux/delay.h>
  12. #include <linux/ktime.h>
  13. #include "primary_display.h"
  14. #include "leds_drv.h"
  15. struct task_struct *primary_resume_task = NULL;
  16. wait_queue_head_t primary_resume_wq;
  17. atomic_t primary_resume_event = ATOMIC_INIT(0);
  18. unsigned int g_suspend_flag = 0;
  19. #endif
  20. static const struct EXTD_DRIVER *extd_driver[DEV_MAX_NUM-1];
  21. struct SWITCH_MODE_INFO_STRUCT path_info;
  22. struct task_struct *disp_switch_mode_task = NULL;
  23. wait_queue_head_t switch_mode_wq;
  24. atomic_t switch_mode_event = ATOMIC_INIT(0);
  25. static int extd_create_path(enum EXT_DISP_PATH_MODE mode, unsigned int session)
  26. {
  27. int ret = 0;
  28. MULTI_COTRL_LOG("extd_create_path session:%08x, mode:%d", session, mode);
  29. ext_disp_path_set_mode(mode, session);
  30. ret = ext_disp_init(NULL, session);
  31. return ret;
  32. }
  33. /*
  34. static int extd_recompute_bg(int src_w, int src_h, unsigned int session)
  35. {
  36. int ret = 0;
  37. int device_id = (session & 0x0FF) - 1;
  38. if (device_id == DEV_MHL && extd_driver[DEV_MHL]->ioctl) {
  39. ret = extd_driver[DEV_MHL]->ioctl(RECOMPUTE_BG_CMD, src_w, src_h, NULL);
  40. } else if (device_id == DEV_EINK && extd_driver[DEV_EINK]->ioctl) {
  41. ret = extd_driver[DEV_EINK]->ioctl(RECOMPUTE_BG_CMD, src_w, src_h, NULL);
  42. }
  43. return ret;
  44. }
  45. */
  46. static int extd_get_device_type(unsigned int session)
  47. {
  48. int ret = 0;
  49. int device_id = (session & 0x0FF) - 1;
  50. if (device_id == DEV_MHL && extd_driver[DEV_MHL]->ioctl) {
  51. /*for mhl device*/
  52. ret = extd_driver[DEV_MHL]->ioctl(GET_DEV_TYPE_CMD, 0, 0, NULL);
  53. } else if (device_id == DEV_EINK && extd_driver[DEV_EINK]->ioctl) {
  54. /*for eink device*/
  55. ret = extd_driver[DEV_EINK]->ioctl(GET_DEV_TYPE_CMD, 0, 0, NULL);
  56. }
  57. MULTI_COTRL_LOG("device type is:%d\n", ret);
  58. return ret;
  59. }
  60. static void extd_set_layer_num(int layer_num, unsigned int session)
  61. {
  62. int device_id = (session & 0x0FF) - 1;
  63. if (device_id == DEV_MHL && extd_driver[DEV_MHL]->ioctl) {
  64. /*for mhl device*/
  65. extd_driver[DEV_MHL]->ioctl(SET_LAYER_NUM_CMD, layer_num, 0, NULL);
  66. } else if (device_id == DEV_EINK && extd_driver[DEV_EINK]->ioctl) {
  67. /*for eink device*/
  68. extd_driver[DEV_EINK]->ioctl(SET_LAYER_NUM_CMD, layer_num, 0, NULL);
  69. }
  70. }
  71. static int create_external_display_path(unsigned int session, int mode)
  72. {
  73. int ret = 0;
  74. int extd_type = DISP_IF_MHL;
  75. pr_warn("create_external_display_path session:%08x, mode:%d\n", session, mode);
  76. if (DISP_SESSION_TYPE(session) == DISP_SESSION_MEMORY && EXTD_OVERLAY_CNT > 0) {
  77. if (mode < DISP_SESSION_DIRECT_LINK_MIRROR_MODE
  78. && (path_info.old_mode[DEV_WFD] >= DISP_SESSION_DIRECT_LINK_MIRROR_MODE
  79. || path_info.old_session[DEV_WFD] != DISP_SESSION_MEMORY)) {
  80. if (ext_disp_wait_ovl_available(0) > 0) {
  81. ovl2mem_init(session);
  82. ovl2mem_setlayernum(4);
  83. } else {
  84. MULTI_COTRL_ERR("mhl path: OVL1 can not be split out!\n");
  85. ret = -1;
  86. }
  87. } else if (mode >= DISP_SESSION_DIRECT_LINK_MIRROR_MODE
  88. && path_info.old_session[DEV_WFD] == DISP_SESSION_MEMORY
  89. && path_info.old_mode[DEV_WFD] < DISP_SESSION_DIRECT_LINK_MIRROR_MODE) {
  90. ovl2mem_deinit();
  91. ovl2mem_setlayernum(0);
  92. ext_disp_path_change(EXTD_OVL_IDLE_REQ, session);
  93. }
  94. } else if (DISP_SESSION_TYPE(session) == DISP_SESSION_EXTERNAL) {
  95. int device_id = DISP_SESSION_DEV(session) - 1;
  96. extd_type = extd_get_device_type(session);
  97. if (path_info.old_session[device_id] == DISP_SESSION_EXTERNAL) {
  98. ext_disp_resume(session);
  99. if (EXTD_OVERLAY_CNT < 1) {
  100. /*external display has no OVL to use, so no actions for mode switch */
  101. return ret;
  102. }
  103. } else {
  104. extd_create_path(EXTD_RDMA_DPI_MODE, session);
  105. extd_set_layer_num(1, session);
  106. }
  107. #if 0
  108. if (extd_type != DISP_IF_EPD && (mode < DISP_SESSION_DIRECT_LINK_MIRROR_MODE
  109. || extd_type == DISP_IF_HDMI_SMARTBOOK)) {
  110. if (ext_disp_wait_ovl_available(0) > 0) {
  111. if (path_info.old_session[device_id] == DISP_SESSION_EXTERNAL
  112. && extd_type != DISP_IF_HDMI_SMARTBOOK) {
  113. /*insert OVL to external dispaly path*/
  114. ext_disp_path_change(EXTD_OVL_INSERT_REQ, session);
  115. } else {
  116. /*create external display path with OVL*/
  117. extd_create_path(EXTD_DIRECT_LINK_MODE, session);
  118. }
  119. extd_set_layer_num(4, session);
  120. } else {
  121. extd_create_path(EXTD_RDMA_DPI_MODE, session);
  122. extd_set_layer_num(1, session);
  123. }
  124. } else {
  125. if (path_info.old_session[device_id] == DISP_SESSION_EXTERNAL) {
  126. ext_disp_path_change(EXTD_OVL_REMOVE_REQ, session);
  127. extd_set_layer_num(1, session);
  128. } else {
  129. extd_create_path(EXTD_RDMA_DPI_MODE, session);
  130. extd_set_layer_num(1, session);
  131. }
  132. }
  133. #endif
  134. } else if (DISP_SESSION_TYPE(session) == DISP_SESSION_MEMORY && EXTD_OVERLAY_CNT == 0) {
  135. MULTI_COTRL_ERR("memory session and ovl time sharing!\n");
  136. ovl2mem_setlayernum(4);
  137. }
  138. return ret;
  139. }
  140. static void destroy_external_display_path(unsigned int session, int mode)
  141. {
  142. int device_id = DISP_SESSION_DEV(session) - 1;
  143. MULTI_COTRL_LOG("destroy_external_display_path session:%08x\n", session);
  144. if ((path_info.old_session[device_id] == DISP_SESSION_PRIMARY)
  145. || (path_info.old_session[device_id] == DISP_SESSION_MEMORY
  146. && path_info.old_mode[device_id] >= DISP_SESSION_DIRECT_LINK_MIRROR_MODE)) {
  147. /*discard for memory session in mirror mode*/
  148. return;
  149. }
  150. if (path_info.old_session[device_id] == DISP_SESSION_EXTERNAL) {
  151. ext_disp_deinit(session);
  152. extd_set_layer_num(0, session);
  153. ext_disp_path_change(EXTD_OVL_IDLE_REQ, session);
  154. } else if (path_info.old_session[device_id] == DISP_SESSION_MEMORY && EXTD_OVERLAY_CNT > 0) {
  155. ovl2mem_deinit();
  156. ovl2mem_setlayernum(0);
  157. ext_disp_path_change(EXTD_OVL_IDLE_REQ, session);
  158. }
  159. }
  160. static int disp_switch_mode_kthread(void *data)
  161. {
  162. int ret = 0;
  163. struct sched_param param = { .sched_priority = 94 }; /*RTPM_PRIO_SCRN_UPDATE*/
  164. sched_setscheduler(current, SCHED_RR, &param);
  165. MULTI_COTRL_LOG("disp_switch_mode_kthread in!\n");
  166. for (;;) {
  167. wait_event_interruptible(switch_mode_wq, atomic_read(&switch_mode_event));
  168. atomic_set(&switch_mode_event, 0);
  169. MULTI_COTRL_LOG("switch mode, create or change path, mode:%d, session:0x%x\n",
  170. path_info.cur_mode, path_info.ext_sid);
  171. ret = create_external_display_path(path_info.ext_sid, path_info.cur_mode);
  172. if (ret == 0) {
  173. path_info.old_session[path_info.switching] = DISP_SESSION_TYPE(path_info.ext_sid);
  174. path_info.old_mode[path_info.switching] = path_info.cur_mode;
  175. }
  176. path_info.switching = DEV_MAX_NUM;
  177. path_info.ext_sid = 0;
  178. if (kthread_should_stop()) {
  179. /*thread exit*/
  180. break;
  181. }
  182. }
  183. return 0;
  184. }
  185. #ifdef CONFIG_SINGLE_PANEL_OUTPUT
  186. static int primary_resume_kthread(void *data)
  187. {
  188. struct sched_param param = { .sched_priority = 94 };
  189. sched_setscheduler(current, SCHED_RR, &param);
  190. pr_warn("primary_resume_kthread in!\n");
  191. for ( ; ; ) {
  192. wait_event_interruptible(primary_resume_wq, atomic_read(&primary_resume_event));
  193. if (is_hdmi_plug_out_valid() && !is_early_suspended) {
  194. g_suspend_flag = 1;
  195. clr_hdmi_plug_out_valid();
  196. pr_warn("primary_resume_kthread: hdmi plug out,call primary_resume\n");
  197. msleep(30);
  198. primary_display_resume();
  199. atomic_set(&primary_resume_event, 0);
  200. msleep(700);
  201. g_suspend_flag = 0;
  202. #if defined(CONFIG_MTK_LEDS)
  203. mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, LED_HALF);
  204. #endif
  205. } else {
  206. atomic_set(&primary_resume_event, 0);
  207. pr_warn("primary_resume_kthread: hdmi not plug out--skip primary_resume\n");
  208. }
  209. if (kthread_should_stop())
  210. break;
  211. }
  212. return 0;
  213. }
  214. #endif
  215. int external_display_path_change_without_cascade(DISP_MODE mode, unsigned int session_id,
  216. unsigned int device_id, unsigned int change_flag)
  217. {
  218. int ret = -1;
  219. unsigned int session = 0;
  220. MULTI_COTRL_FUNC();
  221. if (1 == change_flag) {
  222. #ifdef CONFIG_SINGLE_PANEL_OUTPUT
  223. if (primary_display_is_alive()) {
  224. g_suspend_flag = 1;
  225. #if defined(CONFIG_MTK_LEDS)
  226. mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, LED_OFF);
  227. #endif
  228. primary_display_suspend();
  229. g_suspend_flag = 0;
  230. }
  231. #endif
  232. }
  233. /*destroy external display path*/
  234. if (session_id == 0 && path_info.old_session[device_id] != DISP_SESSION_PRIMARY) {
  235. if (device_id == DEV_WFD) {
  236. /*make memory session for WFD*/
  237. session = MAKE_DISP_SESSION(DISP_SESSION_MEMORY, device_id);
  238. } else {
  239. /*make external session*/
  240. session = MAKE_DISP_SESSION(DISP_SESSION_EXTERNAL, device_id + 1);
  241. }
  242. destroy_external_display_path(session, DISP_SESSION_DIRECT_LINK_MODE);
  243. #ifdef CONFIG_SINGLE_PANEL_OUTPUT
  244. atomic_set(&primary_resume_event, 1);
  245. wake_up_interruptible(&primary_resume_wq);
  246. #endif
  247. path_info.old_session[device_id] = DISP_SESSION_PRIMARY;
  248. path_info.old_mode[device_id] = DISP_SESSION_DIRECT_LINK_MODE;
  249. path_info.switching = DEV_MAX_NUM;
  250. return 1;
  251. }
  252. /*create path or change path*/
  253. if ((session_id > 0 && path_info.old_session[device_id] == DISP_SESSION_PRIMARY)
  254. || (mode != path_info.old_mode[device_id] && path_info.old_session[device_id] != DISP_SESSION_PRIMARY)) {
  255. ret = create_external_display_path(session_id, mode);
  256. if (ret == 0) {
  257. path_info.old_session[device_id] = DISP_SESSION_TYPE(session_id);
  258. path_info.old_mode[device_id] = mode;
  259. }
  260. path_info.switching = DEV_MAX_NUM;
  261. return 1;
  262. }
  263. return 0;
  264. }
  265. /*
  266. static int path_change_with_cascade(DISP_MODE mode, unsigned int session_id, unsigned int device_id)
  267. {
  268. int disp_type = 0;
  269. unsigned int session = 0;
  270. if (session_id == 0 && path_info.old_session[device_id] != DISP_SESSION_PRIMARY) {
  271. if (device_id == DEV_WFD) {
  272. session = MAKE_DISP_SESSION(DISP_SESSION_MEMORY, device_id);
  273. } else {
  274. session = MAKE_DISP_SESSION(DISP_SESSION_EXTERNAL, device_id + 1);
  275. }
  276. destroy_external_display_path(session, DISP_SESSION_DIRECT_LINK_MODE);
  277. path_info.old_session[device_id] = DISP_SESSION_PRIMARY;
  278. path_info.old_mode[device_id] = DISP_SESSION_DIRECT_LINK_MODE;
  279. path_info.switching = DEV_MAX_NUM;
  280. path_info.ext_sid = 0;
  281. return 1;
  282. }
  283. if ((session_id > 0 && path_info.old_session[device_id] == DISP_SESSION_PRIMARY)
  284. || (mode != path_info.old_mode[device_id] && path_info.old_session[device_id] != DISP_SESSION_PRIMARY)) {
  285. disp_type = extd_get_device_type(session_id);
  286. if (disp_type != DISP_IF_EPD
  287. && (mode < DISP_SESSION_DIRECT_LINK_MIRROR_MODE || disp_type == DISP_IF_HDMI_SMARTBOOK)) {
  288. ext_disp_path_change(EXTD_OVL_REQUSTING_REQ, session_id);
  289. }
  290. if (path_info.old_session[device_id] == DISP_SESSION_EXTERNAL
  291. && mode >= DISP_SESSION_DIRECT_LINK_MIRROR_MODE) {
  292. extd_set_layer_num(1, session_id);
  293. }
  294. MULTI_COTRL_LOG("path_change_with_cascade, wake up\n");
  295. path_info.cur_mode = mode;
  296. path_info.ext_sid = session_id;
  297. path_info.switching = device_id;
  298. atomic_set(&switch_mode_event, 1);
  299. wake_up_interruptible(&switch_mode_wq);
  300. return 1;
  301. }
  302. return 0;
  303. }
  304. */
  305. void external_display_control_init(void)
  306. {
  307. int i = 0;
  308. MULTI_COTRL_FUNC();
  309. memset(&path_info, 0, sizeof(struct SWITCH_MODE_INFO_STRUCT));
  310. path_info.switching = DEV_MAX_NUM;
  311. for (i = 0; i < DEV_MAX_NUM; i++) {
  312. path_info.old_mode[i] = DISP_SESSION_DIRECT_LINK_MODE;
  313. path_info.old_session[i] = DISP_SESSION_PRIMARY;
  314. }
  315. extd_driver[DEV_MHL] = EXTD_HDMI_Driver();
  316. extd_driver[DEV_EINK] = EXTD_EPD_Driver();
  317. #ifdef CONFIG_SINGLE_PANEL_OUTPUT
  318. init_waitqueue_head(&primary_resume_wq);
  319. primary_resume_task = kthread_create(primary_resume_kthread, NULL, "primary_resume_kthread");
  320. wake_up_process(primary_resume_task);
  321. #endif
  322. init_waitqueue_head(&switch_mode_wq);
  323. disp_switch_mode_task = kthread_create(disp_switch_mode_kthread, NULL, "disp_switch_mode_kthread");
  324. wake_up_process(disp_switch_mode_task);
  325. ext_disp_probe();
  326. }
  327. int external_display_config_input(disp_session_input_config *input, int idx, unsigned int session)
  328. {
  329. int ret = 0;
  330. if (ext_disp_path_get_mode(session) == EXTD_RDMA_DPI_MODE) {
  331. /*recompule background*/
  332. /*extd_recompute_bg(input->src_w, input->src_h, session);*/
  333. }
  334. ret = ext_disp_config_input_multiple(input, idx, session);
  335. return ret;
  336. }
  337. int external_display_trigger(EXTD_TRIGGER_MODE trigger, unsigned int session)
  338. {
  339. int ret = 0;
  340. enum EXTD_OVL_REQ_STATUS ovl_status = EXTD_OVL_NO_REQ;
  341. if (trigger == TRIGGER_RESUME) {
  342. ext_disp_resume(session);
  343. if (DISP_SESSION_TYPE(session) == DISP_SESSION_EXTERNAL && DISP_SESSION_DEV(session) == DEV_EINK+1) {
  344. if (extd_driver[DEV_EINK]->power_enable) {
  345. /*1 for power on, 0 for power off*/
  346. extd_driver[DEV_EINK]->power_enable(1);
  347. }
  348. }
  349. }
  350. ret = ext_disp_trigger(0, NULL, 0, session);
  351. if (trigger == TRIGGER_SUSPEND) {
  352. ext_disp_suspend_trigger(NULL, 0, session);
  353. if (DISP_SESSION_TYPE(session) == DISP_SESSION_EXTERNAL && DISP_SESSION_DEV(session) == DEV_EINK+1) {
  354. if (extd_driver[DEV_EINK]->power_enable) {
  355. /*1 for power on, 0 for power off*/
  356. extd_driver[DEV_EINK]->power_enable(0);
  357. }
  358. }
  359. }
  360. ovl_status = ext_disp_get_ovl_req_status(session);
  361. if (ovl_status == EXTD_OVL_REMOVING) {
  362. /*the new buffer configured, ovl can be removed*/
  363. ext_disp_path_change(EXTD_OVL_REMOVED, session);
  364. } else if (ovl_status == EXTD_OVL_INSERT_REQ) {
  365. /*the new buffer configured, ovl already is inserted in the path*/
  366. ext_disp_path_change(EXTD_OVL_INSERTED, session);
  367. }
  368. return ret;
  369. }
  370. int external_display_wait_for_vsync(void *config, unsigned int session)
  371. {
  372. int ret = 0;
  373. ret = ext_disp_wait_for_vsync(config, session);
  374. return ret;
  375. }
  376. int external_display_get_info(void *info, unsigned int session)
  377. {
  378. int ret = 0;
  379. int device_id = (session & 0x0FF) - 1;
  380. if (device_id == DEV_MHL && extd_driver[DEV_MHL]->get_dev_info) {
  381. /*get device info for MHL*/
  382. ret = extd_driver[DEV_MHL]->get_dev_info(SF_GET_INFO, info);
  383. } else if (device_id == DEV_EINK && extd_driver[DEV_EINK]->get_dev_info) {
  384. /*get device info for EINK*/
  385. ret = extd_driver[DEV_EINK]->get_dev_info(SF_GET_INFO, info);
  386. }
  387. return ret;
  388. }
  389. int external_display_switch_mode(DISP_MODE mode, unsigned int *session_created, unsigned int session)
  390. {
  391. int i = 0;
  392. int j = 0;
  393. int ret = -1;
  394. int switching = 0;
  395. static unsigned int change_flag;
  396. int session_id[DEV_MAX_NUM] = {0};
  397. MULTI_COTRL_FUNC();
  398. if (session_created == NULL) {
  399. /*error,no session can be compared*/
  400. return ret;
  401. }
  402. if (path_info.switching < DEV_MAX_NUM
  403. #ifdef CONFIG_SINGLE_PANEL_OUTPUT
  404. || (atomic_read(&primary_resume_event) == 1)
  405. #endif
  406. ) {
  407. /*mode is switching, return directly*/
  408. return ret;
  409. }
  410. path_info.switching = DEV_MAX_NUM - 1;
  411. for (i = 0; i < MAX_SESSION_COUNT; i++) {
  412. if (session_created[i] == MAKE_DISP_SESSION(DISP_SESSION_EXTERNAL, DEV_MHL+1)) {
  413. /*it has MHL session*/
  414. session_id[DEV_MHL] = session_created[i];
  415. }
  416. if (session_created[i] == MAKE_DISP_SESSION(DISP_SESSION_EXTERNAL, DEV_EINK+1)) {
  417. /*it has EINK session*/
  418. session_id[DEV_EINK] = session_created[i];
  419. }
  420. if (session_created[i] == MAKE_DISP_SESSION(DISP_SESSION_MEMORY, DEV_WFD)) {
  421. /*it has WFD session*/
  422. session_id[DEV_WFD] = session_created[i];
  423. }
  424. }
  425. for (j = 0; j < DEV_MAX_NUM; j++) {
  426. #ifndef OVL_CASCADE_SUPPORT
  427. #ifdef CONFIG_SINGLE_PANEL_OUTPUT
  428. if ((session_id[j] == 0 && path_info.old_session[j] != DISP_SESSION_PRIMARY) ||
  429. (session_id[j] > 0 && path_info.old_session[j] == DISP_SESSION_PRIMARY) ||
  430. (mode != path_info.old_mode[j] && path_info.old_session[j] != DISP_SESSION_PRIMARY)) {
  431. pr_warn("need change path, set change flag 1\n");
  432. change_flag = 1;
  433. }
  434. #endif
  435. switching = external_display_path_change_without_cascade(mode, session_id[j], j, change_flag);
  436. #else
  437. switching = path_change_with_cascade(mode, session_id[j], j);
  438. #endif
  439. change_flag = 0;
  440. if (switching == 1) {
  441. /*session switching one by one*/
  442. break;
  443. }
  444. }
  445. path_info.switching = (switching == 0) ? DEV_MAX_NUM : path_info.switching;
  446. return 0;
  447. }