lcdc_st15.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /* Copyright (c) 2010, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #include <linux/i2c.h>
  13. #include <linux/delay.h>
  14. #include "msm_fb.h"
  15. #define DEVICE_NAME "sii9022"
  16. #define SII9022_DEVICE_ID 0xB0
  17. #define SII9022_ISR 0x3D
  18. #define SII9022_ISR_RXS_STATUS 0x08
  19. static int lcdc_sii9022_panel_on(struct platform_device *pdev);
  20. static int lcdc_sii9022_panel_off(struct platform_device *pdev);
  21. static struct i2c_client *sii9022_i2c_client;
  22. struct sii9022_data {
  23. struct msm_hdmi_platform_data *pd;
  24. struct platform_device *pdev;
  25. struct work_struct work;
  26. int x_res;
  27. int y_res;
  28. int sysfs_entry_created;
  29. int hdmi_attached;
  30. };
  31. static struct sii9022_data *dd;
  32. struct sii9022_i2c_addr_data{
  33. u8 addr;
  34. u8 data;
  35. };
  36. /* video mode data */
  37. static u8 video_mode_data[] = {
  38. 0x00,
  39. 0xF9, 0x1C, 0x70, 0x17, 0x72, 0x06, 0xEE, 0x02,
  40. };
  41. static u8 avi_io_format[] = {
  42. 0x09,
  43. 0x00, 0x00,
  44. };
  45. /* power state */
  46. static struct sii9022_i2c_addr_data regset0[] = {
  47. { 0x60, 0x04 },
  48. { 0x63, 0x00 },
  49. { 0x1E, 0x00 },
  50. };
  51. static u8 video_infoframe[] = {
  52. 0x0C,
  53. 0xF0, 0x00, 0x68, 0x00, 0x04, 0x00, 0x19, 0x00,
  54. 0xE9, 0x02, 0x04, 0x01, 0x04, 0x06,
  55. };
  56. /* configure audio */
  57. static struct sii9022_i2c_addr_data regset1[] = {
  58. { 0x26, 0x90 },
  59. { 0x20, 0x90 },
  60. { 0x1F, 0x80 },
  61. { 0x26, 0x80 },
  62. { 0x24, 0x02 },
  63. { 0x25, 0x0B },
  64. { 0xBC, 0x02 },
  65. { 0xBD, 0x24 },
  66. { 0xBE, 0x02 },
  67. };
  68. /* enable audio */
  69. static u8 misc_infoframe[] = {
  70. 0xBF,
  71. 0xC2, 0x84, 0x01, 0x0A, 0x6F, 0x02, 0x00, 0x00,
  72. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  73. };
  74. /* set HDMI, active */
  75. static struct sii9022_i2c_addr_data regset2[] = {
  76. { 0x1A, 0x01 },
  77. { 0x3D, 0x00 },
  78. { 0x3C, 0x02 },
  79. };
  80. static struct msm_fb_panel_data sii9022_panel_data = {
  81. .on = lcdc_sii9022_panel_on,
  82. .off = lcdc_sii9022_panel_off,
  83. };
  84. static struct platform_device sii9022_device = {
  85. .name = DEVICE_NAME,
  86. .id = 1,
  87. .dev = {
  88. .platform_data = &sii9022_panel_data,
  89. }
  90. };
  91. static int send_i2c_data(struct i2c_client *client,
  92. struct sii9022_i2c_addr_data *regset,
  93. int size)
  94. {
  95. int i;
  96. int rc = 0;
  97. for (i = 0; i < size; i++) {
  98. rc = i2c_smbus_write_byte_data(
  99. client,
  100. regset[i].addr, regset[i].data);
  101. if (rc)
  102. break;
  103. }
  104. return rc;
  105. }
  106. static void sii9022_work_f(struct work_struct *work)
  107. {
  108. int isr;
  109. isr = i2c_smbus_read_byte_data(sii9022_i2c_client, SII9022_ISR);
  110. if (isr < 0) {
  111. dev_err(&sii9022_i2c_client->dev,
  112. "i2c read of isr failed rc = 0x%x\n", isr);
  113. return;
  114. }
  115. if (isr == 0)
  116. return;
  117. /* reset any set bits */
  118. i2c_smbus_write_byte_data(sii9022_i2c_client, SII9022_ISR, isr);
  119. dd->hdmi_attached = isr & SII9022_ISR_RXS_STATUS;
  120. if (dd->pd->cable_detect)
  121. dd->pd->cable_detect(dd->hdmi_attached);
  122. if (dd->hdmi_attached) {
  123. dd->x_res = 1280;
  124. dd->y_res = 720;
  125. } else {
  126. dd->x_res = sii9022_panel_data.panel_info.xres;
  127. dd->y_res = sii9022_panel_data.panel_info.yres;
  128. }
  129. }
  130. static irqreturn_t sii9022_interrupt(int irq, void *dev_id)
  131. {
  132. struct sii9022_data *dd = dev_id;
  133. schedule_work(&dd->work);
  134. return IRQ_HANDLED;
  135. }
  136. static int hdmi_sii_enable(struct i2c_client *client)
  137. {
  138. int rc;
  139. int retries = 10;
  140. int count;
  141. rc = i2c_smbus_write_byte_data(client, 0xC7, 0x00);
  142. if (rc)
  143. goto enable_exit;
  144. do {
  145. msleep(1);
  146. rc = i2c_smbus_read_byte_data(client, 0x1B);
  147. } while ((rc != SII9022_DEVICE_ID) && retries--);
  148. if (rc != SII9022_DEVICE_ID)
  149. return -ENODEV;
  150. rc = i2c_smbus_write_byte_data(client, 0x1A, 0x11);
  151. if (rc)
  152. goto enable_exit;
  153. count = ARRAY_SIZE(video_mode_data);
  154. rc = i2c_master_send(client, video_mode_data, count);
  155. if (rc != count) {
  156. rc = -EIO;
  157. goto enable_exit;
  158. }
  159. rc = i2c_smbus_write_byte_data(client, 0x08, 0x20);
  160. if (rc)
  161. goto enable_exit;
  162. count = ARRAY_SIZE(avi_io_format);
  163. rc = i2c_master_send(client, avi_io_format, count);
  164. if (rc != count) {
  165. rc = -EIO;
  166. goto enable_exit;
  167. }
  168. rc = send_i2c_data(client, regset0, ARRAY_SIZE(regset0));
  169. if (rc)
  170. goto enable_exit;
  171. count = ARRAY_SIZE(video_infoframe);
  172. rc = i2c_master_send(client, video_infoframe, count);
  173. if (rc != count) {
  174. rc = -EIO;
  175. goto enable_exit;
  176. }
  177. rc = send_i2c_data(client, regset1, ARRAY_SIZE(regset1));
  178. if (rc)
  179. goto enable_exit;
  180. count = ARRAY_SIZE(misc_infoframe);
  181. rc = i2c_master_send(client, misc_infoframe, count);
  182. if (rc != count) {
  183. rc = -EIO;
  184. goto enable_exit;
  185. }
  186. rc = send_i2c_data(client, regset2, ARRAY_SIZE(regset2));
  187. if (rc)
  188. goto enable_exit;
  189. return 0;
  190. enable_exit:
  191. printk(KERN_ERR "%s: exited rc=%d\n", __func__, rc);
  192. return rc;
  193. }
  194. static ssize_t show_res(struct device *device,
  195. struct device_attribute *attr, char *buf)
  196. {
  197. return snprintf(buf, PAGE_SIZE, "%dx%d\n", dd->x_res, dd->y_res);
  198. }
  199. static struct device_attribute device_attrs[] = {
  200. __ATTR(screen_resolution, S_IRUGO|S_IWUSR, show_res, NULL),
  201. };
  202. static int lcdc_sii9022_panel_on(struct platform_device *pdev)
  203. {
  204. int rc;
  205. if (!dd->sysfs_entry_created) {
  206. dd->pdev = pdev;
  207. rc = device_create_file(&pdev->dev, &device_attrs[0]);
  208. if (!rc)
  209. dd->sysfs_entry_created = 1;
  210. }
  211. rc = hdmi_sii_enable(sii9022_i2c_client);
  212. if (rc) {
  213. dd->hdmi_attached = 0;
  214. dd->x_res = sii9022_panel_data.panel_info.xres;
  215. dd->y_res = sii9022_panel_data.panel_info.yres;
  216. }
  217. if (dd->pd->irq)
  218. enable_irq(dd->pd->irq);
  219. /* Don't return the value from hdmi_sii_enable().
  220. * It may fail on some ST1.5s, but we must return 0 from this
  221. * function in order for the on-board display to turn on.
  222. */
  223. return 0;
  224. }
  225. static int lcdc_sii9022_panel_off(struct platform_device *pdev)
  226. {
  227. if (dd->pd->irq)
  228. disable_irq(dd->pd->irq);
  229. return 0;
  230. }
  231. static const struct i2c_device_id hmdi_sii_id[] = {
  232. { DEVICE_NAME, 0 },
  233. { }
  234. };
  235. static int hdmi_sii_probe(struct i2c_client *client,
  236. const struct i2c_device_id *id)
  237. {
  238. int rc;
  239. if (!i2c_check_functionality(client->adapter,
  240. I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
  241. return -ENODEV;
  242. dd = kzalloc(sizeof *dd, GFP_KERNEL);
  243. if (!dd) {
  244. rc = -ENOMEM;
  245. goto probe_exit;
  246. }
  247. sii9022_i2c_client = client;
  248. i2c_set_clientdata(client, dd);
  249. dd->pd = client->dev.platform_data;
  250. if (!dd->pd) {
  251. rc = -ENODEV;
  252. goto probe_free;
  253. }
  254. if (dd->pd->irq) {
  255. INIT_WORK(&dd->work, sii9022_work_f);
  256. rc = request_irq(dd->pd->irq,
  257. &sii9022_interrupt,
  258. IRQF_TRIGGER_FALLING,
  259. "sii9022_cable", dd);
  260. if (rc)
  261. goto probe_free;
  262. disable_irq(dd->pd->irq);
  263. }
  264. msm_fb_add_device(&sii9022_device);
  265. dd->x_res = sii9022_panel_data.panel_info.xres;
  266. dd->y_res = sii9022_panel_data.panel_info.yres;
  267. return 0;
  268. probe_free:
  269. i2c_set_clientdata(client, NULL);
  270. kfree(dd);
  271. probe_exit:
  272. return rc;
  273. }
  274. static int __devexit hdmi_sii_remove(struct i2c_client *client)
  275. {
  276. int err = 0 ;
  277. struct msm_hdmi_platform_data *pd;
  278. if (dd->sysfs_entry_created)
  279. device_remove_file(&dd->pdev->dev, &device_attrs[0]);
  280. pd = client->dev.platform_data;
  281. if (pd && pd->irq)
  282. free_irq(pd->irq, dd);
  283. i2c_set_clientdata(client, NULL);
  284. kfree(dd);
  285. return err ;
  286. }
  287. #ifdef CONFIG_PM
  288. static int sii9022_suspend(struct device *dev)
  289. {
  290. if (dd && dd->pd && dd->pd->irq)
  291. disable_irq(dd->pd->irq);
  292. return 0;
  293. }
  294. static int sii9022_resume(struct device *dev)
  295. {
  296. if (dd && dd->pd && dd->pd->irq)
  297. enable_irq(dd->pd->irq);
  298. return 0;
  299. }
  300. static struct dev_pm_ops sii9022_pm_ops = {
  301. .suspend = sii9022_suspend,
  302. .resume = sii9022_resume,
  303. };
  304. #endif
  305. static struct i2c_driver hdmi_sii_i2c_driver = {
  306. .driver = {
  307. .name = DEVICE_NAME,
  308. .owner = THIS_MODULE,
  309. #ifdef CONFIG_PM
  310. .pm = &sii9022_pm_ops,
  311. #endif
  312. },
  313. .probe = hdmi_sii_probe,
  314. .remove = __exit_p(hdmi_sii_remove),
  315. .id_table = hmdi_sii_id,
  316. };
  317. static int __init lcdc_st15_init(void)
  318. {
  319. int ret;
  320. struct msm_panel_info *pinfo;
  321. if (msm_fb_detect_client("lcdc_st15"))
  322. return 0;
  323. pinfo = &sii9022_panel_data.panel_info;
  324. pinfo->xres = 1366;
  325. pinfo->yres = 768;
  326. MSM_FB_SINGLE_MODE_PANEL(pinfo);
  327. pinfo->type = LCDC_PANEL;
  328. pinfo->pdest = DISPLAY_1;
  329. pinfo->wait_cycle = 0;
  330. pinfo->bpp = 24;
  331. pinfo->fb_num = 2;
  332. pinfo->clk_rate = 74250000;
  333. pinfo->lcdc.h_back_porch = 120;
  334. pinfo->lcdc.h_front_porch = 20;
  335. pinfo->lcdc.h_pulse_width = 40;
  336. pinfo->lcdc.v_back_porch = 25;
  337. pinfo->lcdc.v_front_porch = 1;
  338. pinfo->lcdc.v_pulse_width = 7;
  339. pinfo->lcdc.border_clr = 0; /* blk */
  340. pinfo->lcdc.underflow_clr = 0xff; /* blue */
  341. pinfo->lcdc.hsync_skew = 0;
  342. ret = i2c_add_driver(&hdmi_sii_i2c_driver);
  343. if (ret)
  344. printk(KERN_ERR "%s: failed to add i2c driver\n", __func__);
  345. return ret;
  346. }
  347. static void __exit hdmi_sii_exit(void)
  348. {
  349. i2c_del_driver(&hdmi_sii_i2c_driver);
  350. }
  351. module_init(lcdc_st15_init);
  352. module_exit(hdmi_sii_exit);