s6e63m0.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. /*
  2. * S6E63M0 AMOLED LCD panel driver.
  3. *
  4. * Author: InKi Dae <inki.dae@samsung.com>
  5. *
  6. * Derived from drivers/video/omap/lcd-apollon.c
  7. *
  8. * This program is free software; you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation; either version 2 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. #include <linux/wait.h>
  23. #include <linux/fb.h>
  24. #include <linux/delay.h>
  25. #include <linux/gpio.h>
  26. #include <linux/spi/spi.h>
  27. #include <linux/irq.h>
  28. #include <linux/interrupt.h>
  29. #include <linux/kernel.h>
  30. #include <linux/lcd.h>
  31. #include <linux/backlight.h>
  32. #include "s6e63m0_gamma.h"
  33. #define SLEEPMSEC 0x1000
  34. #define ENDDEF 0x2000
  35. #define DEFMASK 0xFF00
  36. #define COMMAND_ONLY 0xFE
  37. #define DATA_ONLY 0xFF
  38. #define MIN_BRIGHTNESS 0
  39. #define MAX_BRIGHTNESS 10
  40. #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
  41. struct s6e63m0 {
  42. struct device *dev;
  43. struct spi_device *spi;
  44. unsigned int power;
  45. unsigned int current_brightness;
  46. unsigned int gamma_mode;
  47. unsigned int gamma_table_count;
  48. struct lcd_device *ld;
  49. struct backlight_device *bd;
  50. struct lcd_platform_data *lcd_pd;
  51. };
  52. static const unsigned short SEQ_PANEL_CONDITION_SET[] = {
  53. 0xF8, 0x01,
  54. DATA_ONLY, 0x27,
  55. DATA_ONLY, 0x27,
  56. DATA_ONLY, 0x07,
  57. DATA_ONLY, 0x07,
  58. DATA_ONLY, 0x54,
  59. DATA_ONLY, 0x9f,
  60. DATA_ONLY, 0x63,
  61. DATA_ONLY, 0x86,
  62. DATA_ONLY, 0x1a,
  63. DATA_ONLY, 0x33,
  64. DATA_ONLY, 0x0d,
  65. DATA_ONLY, 0x00,
  66. DATA_ONLY, 0x00,
  67. ENDDEF, 0x0000
  68. };
  69. static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = {
  70. 0xf2, 0x02,
  71. DATA_ONLY, 0x03,
  72. DATA_ONLY, 0x1c,
  73. DATA_ONLY, 0x10,
  74. DATA_ONLY, 0x10,
  75. 0xf7, 0x03,
  76. DATA_ONLY, 0x00,
  77. DATA_ONLY, 0x00,
  78. ENDDEF, 0x0000
  79. };
  80. static const unsigned short SEQ_GAMMA_SETTING[] = {
  81. 0xfa, 0x00,
  82. DATA_ONLY, 0x18,
  83. DATA_ONLY, 0x08,
  84. DATA_ONLY, 0x24,
  85. DATA_ONLY, 0x64,
  86. DATA_ONLY, 0x56,
  87. DATA_ONLY, 0x33,
  88. DATA_ONLY, 0xb6,
  89. DATA_ONLY, 0xba,
  90. DATA_ONLY, 0xa8,
  91. DATA_ONLY, 0xac,
  92. DATA_ONLY, 0xb1,
  93. DATA_ONLY, 0x9d,
  94. DATA_ONLY, 0xc1,
  95. DATA_ONLY, 0xc1,
  96. DATA_ONLY, 0xb7,
  97. DATA_ONLY, 0x00,
  98. DATA_ONLY, 0x9c,
  99. DATA_ONLY, 0x00,
  100. DATA_ONLY, 0x9f,
  101. DATA_ONLY, 0x00,
  102. DATA_ONLY, 0xd6,
  103. 0xfa, 0x01,
  104. ENDDEF, 0x0000
  105. };
  106. static const unsigned short SEQ_ETC_CONDITION_SET[] = {
  107. 0xf6, 0x00,
  108. DATA_ONLY, 0x8c,
  109. DATA_ONLY, 0x07,
  110. 0xb3, 0xc,
  111. 0xb5, 0x2c,
  112. DATA_ONLY, 0x12,
  113. DATA_ONLY, 0x0c,
  114. DATA_ONLY, 0x0a,
  115. DATA_ONLY, 0x10,
  116. DATA_ONLY, 0x0e,
  117. DATA_ONLY, 0x17,
  118. DATA_ONLY, 0x13,
  119. DATA_ONLY, 0x1f,
  120. DATA_ONLY, 0x1a,
  121. DATA_ONLY, 0x2a,
  122. DATA_ONLY, 0x24,
  123. DATA_ONLY, 0x1f,
  124. DATA_ONLY, 0x1b,
  125. DATA_ONLY, 0x1a,
  126. DATA_ONLY, 0x17,
  127. DATA_ONLY, 0x2b,
  128. DATA_ONLY, 0x26,
  129. DATA_ONLY, 0x22,
  130. DATA_ONLY, 0x20,
  131. DATA_ONLY, 0x3a,
  132. DATA_ONLY, 0x34,
  133. DATA_ONLY, 0x30,
  134. DATA_ONLY, 0x2c,
  135. DATA_ONLY, 0x29,
  136. DATA_ONLY, 0x26,
  137. DATA_ONLY, 0x25,
  138. DATA_ONLY, 0x23,
  139. DATA_ONLY, 0x21,
  140. DATA_ONLY, 0x20,
  141. DATA_ONLY, 0x1e,
  142. DATA_ONLY, 0x1e,
  143. 0xb6, 0x00,
  144. DATA_ONLY, 0x00,
  145. DATA_ONLY, 0x11,
  146. DATA_ONLY, 0x22,
  147. DATA_ONLY, 0x33,
  148. DATA_ONLY, 0x44,
  149. DATA_ONLY, 0x44,
  150. DATA_ONLY, 0x44,
  151. DATA_ONLY, 0x55,
  152. DATA_ONLY, 0x55,
  153. DATA_ONLY, 0x66,
  154. DATA_ONLY, 0x66,
  155. DATA_ONLY, 0x66,
  156. DATA_ONLY, 0x66,
  157. DATA_ONLY, 0x66,
  158. DATA_ONLY, 0x66,
  159. 0xb7, 0x2c,
  160. DATA_ONLY, 0x12,
  161. DATA_ONLY, 0x0c,
  162. DATA_ONLY, 0x0a,
  163. DATA_ONLY, 0x10,
  164. DATA_ONLY, 0x0e,
  165. DATA_ONLY, 0x17,
  166. DATA_ONLY, 0x13,
  167. DATA_ONLY, 0x1f,
  168. DATA_ONLY, 0x1a,
  169. DATA_ONLY, 0x2a,
  170. DATA_ONLY, 0x24,
  171. DATA_ONLY, 0x1f,
  172. DATA_ONLY, 0x1b,
  173. DATA_ONLY, 0x1a,
  174. DATA_ONLY, 0x17,
  175. DATA_ONLY, 0x2b,
  176. DATA_ONLY, 0x26,
  177. DATA_ONLY, 0x22,
  178. DATA_ONLY, 0x20,
  179. DATA_ONLY, 0x3a,
  180. DATA_ONLY, 0x34,
  181. DATA_ONLY, 0x30,
  182. DATA_ONLY, 0x2c,
  183. DATA_ONLY, 0x29,
  184. DATA_ONLY, 0x26,
  185. DATA_ONLY, 0x25,
  186. DATA_ONLY, 0x23,
  187. DATA_ONLY, 0x21,
  188. DATA_ONLY, 0x20,
  189. DATA_ONLY, 0x1e,
  190. DATA_ONLY, 0x1e,
  191. 0xb8, 0x00,
  192. DATA_ONLY, 0x00,
  193. DATA_ONLY, 0x11,
  194. DATA_ONLY, 0x22,
  195. DATA_ONLY, 0x33,
  196. DATA_ONLY, 0x44,
  197. DATA_ONLY, 0x44,
  198. DATA_ONLY, 0x44,
  199. DATA_ONLY, 0x55,
  200. DATA_ONLY, 0x55,
  201. DATA_ONLY, 0x66,
  202. DATA_ONLY, 0x66,
  203. DATA_ONLY, 0x66,
  204. DATA_ONLY, 0x66,
  205. DATA_ONLY, 0x66,
  206. DATA_ONLY, 0x66,
  207. 0xb9, 0x2c,
  208. DATA_ONLY, 0x12,
  209. DATA_ONLY, 0x0c,
  210. DATA_ONLY, 0x0a,
  211. DATA_ONLY, 0x10,
  212. DATA_ONLY, 0x0e,
  213. DATA_ONLY, 0x17,
  214. DATA_ONLY, 0x13,
  215. DATA_ONLY, 0x1f,
  216. DATA_ONLY, 0x1a,
  217. DATA_ONLY, 0x2a,
  218. DATA_ONLY, 0x24,
  219. DATA_ONLY, 0x1f,
  220. DATA_ONLY, 0x1b,
  221. DATA_ONLY, 0x1a,
  222. DATA_ONLY, 0x17,
  223. DATA_ONLY, 0x2b,
  224. DATA_ONLY, 0x26,
  225. DATA_ONLY, 0x22,
  226. DATA_ONLY, 0x20,
  227. DATA_ONLY, 0x3a,
  228. DATA_ONLY, 0x34,
  229. DATA_ONLY, 0x30,
  230. DATA_ONLY, 0x2c,
  231. DATA_ONLY, 0x29,
  232. DATA_ONLY, 0x26,
  233. DATA_ONLY, 0x25,
  234. DATA_ONLY, 0x23,
  235. DATA_ONLY, 0x21,
  236. DATA_ONLY, 0x20,
  237. DATA_ONLY, 0x1e,
  238. DATA_ONLY, 0x1e,
  239. 0xba, 0x00,
  240. DATA_ONLY, 0x00,
  241. DATA_ONLY, 0x11,
  242. DATA_ONLY, 0x22,
  243. DATA_ONLY, 0x33,
  244. DATA_ONLY, 0x44,
  245. DATA_ONLY, 0x44,
  246. DATA_ONLY, 0x44,
  247. DATA_ONLY, 0x55,
  248. DATA_ONLY, 0x55,
  249. DATA_ONLY, 0x66,
  250. DATA_ONLY, 0x66,
  251. DATA_ONLY, 0x66,
  252. DATA_ONLY, 0x66,
  253. DATA_ONLY, 0x66,
  254. DATA_ONLY, 0x66,
  255. 0xc1, 0x4d,
  256. DATA_ONLY, 0x96,
  257. DATA_ONLY, 0x1d,
  258. DATA_ONLY, 0x00,
  259. DATA_ONLY, 0x00,
  260. DATA_ONLY, 0x01,
  261. DATA_ONLY, 0xdf,
  262. DATA_ONLY, 0x00,
  263. DATA_ONLY, 0x00,
  264. DATA_ONLY, 0x03,
  265. DATA_ONLY, 0x1f,
  266. DATA_ONLY, 0x00,
  267. DATA_ONLY, 0x00,
  268. DATA_ONLY, 0x00,
  269. DATA_ONLY, 0x00,
  270. DATA_ONLY, 0x00,
  271. DATA_ONLY, 0x00,
  272. DATA_ONLY, 0x00,
  273. DATA_ONLY, 0x00,
  274. DATA_ONLY, 0x03,
  275. DATA_ONLY, 0x06,
  276. DATA_ONLY, 0x09,
  277. DATA_ONLY, 0x0d,
  278. DATA_ONLY, 0x0f,
  279. DATA_ONLY, 0x12,
  280. DATA_ONLY, 0x15,
  281. DATA_ONLY, 0x18,
  282. 0xb2, 0x10,
  283. DATA_ONLY, 0x10,
  284. DATA_ONLY, 0x0b,
  285. DATA_ONLY, 0x05,
  286. ENDDEF, 0x0000
  287. };
  288. static const unsigned short SEQ_ACL_ON[] = {
  289. /* ACL on */
  290. 0xc0, 0x01,
  291. ENDDEF, 0x0000
  292. };
  293. static const unsigned short SEQ_ACL_OFF[] = {
  294. /* ACL off */
  295. 0xc0, 0x00,
  296. ENDDEF, 0x0000
  297. };
  298. static const unsigned short SEQ_ELVSS_ON[] = {
  299. /* ELVSS on */
  300. 0xb1, 0x0b,
  301. ENDDEF, 0x0000
  302. };
  303. static const unsigned short SEQ_ELVSS_OFF[] = {
  304. /* ELVSS off */
  305. 0xb1, 0x0a,
  306. ENDDEF, 0x0000
  307. };
  308. static const unsigned short SEQ_STAND_BY_OFF[] = {
  309. 0x11, COMMAND_ONLY,
  310. ENDDEF, 0x0000
  311. };
  312. static const unsigned short SEQ_STAND_BY_ON[] = {
  313. 0x10, COMMAND_ONLY,
  314. ENDDEF, 0x0000
  315. };
  316. static const unsigned short SEQ_DISPLAY_ON[] = {
  317. 0x29, COMMAND_ONLY,
  318. ENDDEF, 0x0000
  319. };
  320. static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
  321. {
  322. u16 buf[1];
  323. struct spi_message msg;
  324. struct spi_transfer xfer = {
  325. .len = 2,
  326. .tx_buf = buf,
  327. };
  328. buf[0] = (addr << 8) | data;
  329. spi_message_init(&msg);
  330. spi_message_add_tail(&xfer, &msg);
  331. return spi_sync(lcd->spi, &msg);
  332. }
  333. static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
  334. unsigned char command)
  335. {
  336. int ret = 0;
  337. if (address != DATA_ONLY)
  338. ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
  339. if (command != COMMAND_ONLY)
  340. ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
  341. return ret;
  342. }
  343. static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
  344. const unsigned short *wbuf)
  345. {
  346. int ret = 0, i = 0;
  347. while ((wbuf[i] & DEFMASK) != ENDDEF) {
  348. if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
  349. ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
  350. if (ret)
  351. break;
  352. } else
  353. udelay(wbuf[i+1]*1000);
  354. i += 2;
  355. }
  356. return ret;
  357. }
  358. static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
  359. {
  360. unsigned int i = 0;
  361. int ret = 0;
  362. /* disable gamma table updating. */
  363. ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
  364. if (ret) {
  365. dev_err(lcd->dev, "failed to disable gamma table updating.\n");
  366. goto gamma_err;
  367. }
  368. for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
  369. ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
  370. if (ret) {
  371. dev_err(lcd->dev, "failed to set gamma table.\n");
  372. goto gamma_err;
  373. }
  374. }
  375. /* update gamma table. */
  376. ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
  377. if (ret)
  378. dev_err(lcd->dev, "failed to update gamma table.\n");
  379. gamma_err:
  380. return ret;
  381. }
  382. static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
  383. {
  384. int ret = 0;
  385. ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
  386. return ret;
  387. }
  388. static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
  389. {
  390. int ret, i;
  391. const unsigned short *init_seq[] = {
  392. SEQ_PANEL_CONDITION_SET,
  393. SEQ_DISPLAY_CONDITION_SET,
  394. SEQ_GAMMA_SETTING,
  395. SEQ_ETC_CONDITION_SET,
  396. SEQ_ACL_ON,
  397. SEQ_ELVSS_ON,
  398. };
  399. for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
  400. ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
  401. if (ret)
  402. break;
  403. }
  404. return ret;
  405. }
  406. static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
  407. {
  408. int ret = 0, i;
  409. const unsigned short *enable_seq[] = {
  410. SEQ_STAND_BY_OFF,
  411. SEQ_DISPLAY_ON,
  412. };
  413. for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
  414. ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
  415. if (ret)
  416. break;
  417. }
  418. return ret;
  419. }
  420. static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
  421. {
  422. int ret;
  423. ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON);
  424. return ret;
  425. }
  426. static int s6e63m0_power_on(struct s6e63m0 *lcd)
  427. {
  428. int ret = 0;
  429. struct lcd_platform_data *pd = NULL;
  430. struct backlight_device *bd = NULL;
  431. pd = lcd->lcd_pd;
  432. if (!pd) {
  433. dev_err(lcd->dev, "platform data is NULL.\n");
  434. return -EFAULT;
  435. }
  436. bd = lcd->bd;
  437. if (!bd) {
  438. dev_err(lcd->dev, "backlight device is NULL.\n");
  439. return -EFAULT;
  440. }
  441. if (!pd->power_on) {
  442. dev_err(lcd->dev, "power_on is NULL.\n");
  443. return -EFAULT;
  444. } else {
  445. pd->power_on(lcd->ld, 1);
  446. mdelay(pd->power_on_delay);
  447. }
  448. if (!pd->reset) {
  449. dev_err(lcd->dev, "reset is NULL.\n");
  450. return -EFAULT;
  451. } else {
  452. pd->reset(lcd->ld);
  453. mdelay(pd->reset_delay);
  454. }
  455. ret = s6e63m0_ldi_init(lcd);
  456. if (ret) {
  457. dev_err(lcd->dev, "failed to initialize ldi.\n");
  458. return ret;
  459. }
  460. ret = s6e63m0_ldi_enable(lcd);
  461. if (ret) {
  462. dev_err(lcd->dev, "failed to enable ldi.\n");
  463. return ret;
  464. }
  465. /* set brightness to current value after power on or resume. */
  466. ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
  467. if (ret) {
  468. dev_err(lcd->dev, "lcd gamma setting failed.\n");
  469. return ret;
  470. }
  471. return 0;
  472. }
  473. static int s6e63m0_power_off(struct s6e63m0 *lcd)
  474. {
  475. int ret = 0;
  476. struct lcd_platform_data *pd = NULL;
  477. pd = lcd->lcd_pd;
  478. if (!pd) {
  479. dev_err(lcd->dev, "platform data is NULL.\n");
  480. return -EFAULT;
  481. }
  482. ret = s6e63m0_ldi_disable(lcd);
  483. if (ret) {
  484. dev_err(lcd->dev, "lcd setting failed.\n");
  485. return -EIO;
  486. }
  487. mdelay(pd->power_off_delay);
  488. if (!pd->power_on) {
  489. dev_err(lcd->dev, "power_on is NULL.\n");
  490. return -EFAULT;
  491. } else
  492. pd->power_on(lcd->ld, 0);
  493. return 0;
  494. }
  495. static int s6e63m0_power(struct s6e63m0 *lcd, int power)
  496. {
  497. int ret = 0;
  498. if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
  499. ret = s6e63m0_power_on(lcd);
  500. else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
  501. ret = s6e63m0_power_off(lcd);
  502. if (!ret)
  503. lcd->power = power;
  504. return ret;
  505. }
  506. static int s6e63m0_set_power(struct lcd_device *ld, int power)
  507. {
  508. struct s6e63m0 *lcd = lcd_get_data(ld);
  509. if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
  510. power != FB_BLANK_NORMAL) {
  511. dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
  512. return -EINVAL;
  513. }
  514. return s6e63m0_power(lcd, power);
  515. }
  516. static int s6e63m0_get_power(struct lcd_device *ld)
  517. {
  518. struct s6e63m0 *lcd = lcd_get_data(ld);
  519. return lcd->power;
  520. }
  521. static int s6e63m0_get_brightness(struct backlight_device *bd)
  522. {
  523. return bd->props.brightness;
  524. }
  525. static int s6e63m0_set_brightness(struct backlight_device *bd)
  526. {
  527. int ret = 0, brightness = bd->props.brightness;
  528. struct s6e63m0 *lcd = bl_get_data(bd);
  529. if (brightness < MIN_BRIGHTNESS ||
  530. brightness > bd->props.max_brightness) {
  531. dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
  532. MIN_BRIGHTNESS, MAX_BRIGHTNESS);
  533. return -EINVAL;
  534. }
  535. ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
  536. if (ret) {
  537. dev_err(&bd->dev, "lcd brightness setting failed.\n");
  538. return -EIO;
  539. }
  540. return ret;
  541. }
  542. static struct lcd_ops s6e63m0_lcd_ops = {
  543. .set_power = s6e63m0_set_power,
  544. .get_power = s6e63m0_get_power,
  545. };
  546. static const struct backlight_ops s6e63m0_backlight_ops = {
  547. .get_brightness = s6e63m0_get_brightness,
  548. .update_status = s6e63m0_set_brightness,
  549. };
  550. static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
  551. struct device_attribute *attr, char *buf)
  552. {
  553. struct s6e63m0 *lcd = dev_get_drvdata(dev);
  554. char temp[10];
  555. switch (lcd->gamma_mode) {
  556. case 0:
  557. sprintf(temp, "2.2 mode\n");
  558. strcat(buf, temp);
  559. break;
  560. case 1:
  561. sprintf(temp, "1.9 mode\n");
  562. strcat(buf, temp);
  563. break;
  564. case 2:
  565. sprintf(temp, "1.7 mode\n");
  566. strcat(buf, temp);
  567. break;
  568. default:
  569. dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
  570. break;
  571. }
  572. return strlen(buf);
  573. }
  574. static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
  575. struct device_attribute *attr,
  576. const char *buf, size_t len)
  577. {
  578. struct s6e63m0 *lcd = dev_get_drvdata(dev);
  579. struct backlight_device *bd = NULL;
  580. int brightness, rc;
  581. rc = strict_strtoul(buf, 0, (unsigned long *)&lcd->gamma_mode);
  582. if (rc < 0)
  583. return rc;
  584. bd = lcd->bd;
  585. brightness = bd->props.brightness;
  586. switch (lcd->gamma_mode) {
  587. case 0:
  588. _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
  589. break;
  590. case 1:
  591. _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
  592. break;
  593. case 2:
  594. _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
  595. break;
  596. default:
  597. dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
  598. _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
  599. break;
  600. }
  601. return len;
  602. }
  603. static DEVICE_ATTR(gamma_mode, 0644,
  604. s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
  605. static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
  606. struct device_attribute *attr, char *buf)
  607. {
  608. struct s6e63m0 *lcd = dev_get_drvdata(dev);
  609. char temp[3];
  610. sprintf(temp, "%d\n", lcd->gamma_table_count);
  611. strcpy(buf, temp);
  612. return strlen(buf);
  613. }
  614. static DEVICE_ATTR(gamma_table, 0444,
  615. s6e63m0_sysfs_show_gamma_table, NULL);
  616. static int __devinit s6e63m0_probe(struct spi_device *spi)
  617. {
  618. int ret = 0;
  619. struct s6e63m0 *lcd = NULL;
  620. struct lcd_device *ld = NULL;
  621. struct backlight_device *bd = NULL;
  622. lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
  623. if (!lcd)
  624. return -ENOMEM;
  625. /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
  626. spi->bits_per_word = 9;
  627. ret = spi_setup(spi);
  628. if (ret < 0) {
  629. dev_err(&spi->dev, "spi setup failed.\n");
  630. goto out_free_lcd;
  631. }
  632. lcd->spi = spi;
  633. lcd->dev = &spi->dev;
  634. lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
  635. if (!lcd->lcd_pd) {
  636. dev_err(&spi->dev, "platform data is NULL.\n");
  637. goto out_free_lcd;
  638. }
  639. ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
  640. if (IS_ERR(ld)) {
  641. ret = PTR_ERR(ld);
  642. goto out_free_lcd;
  643. }
  644. lcd->ld = ld;
  645. bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
  646. &s6e63m0_backlight_ops, NULL);
  647. if (IS_ERR(bd)) {
  648. ret = PTR_ERR(bd);
  649. goto out_lcd_unregister;
  650. }
  651. bd->props.max_brightness = MAX_BRIGHTNESS;
  652. bd->props.brightness = MAX_BRIGHTNESS;
  653. bd->props.type = BACKLIGHT_RAW;
  654. lcd->bd = bd;
  655. /*
  656. * it gets gamma table count available so it gets user
  657. * know that.
  658. */
  659. lcd->gamma_table_count =
  660. sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
  661. ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
  662. if (ret < 0)
  663. dev_err(&(spi->dev), "failed to add sysfs entries\n");
  664. ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
  665. if (ret < 0)
  666. dev_err(&(spi->dev), "failed to add sysfs entries\n");
  667. /*
  668. * if lcd panel was on from bootloader like u-boot then
  669. * do not lcd on.
  670. */
  671. if (!lcd->lcd_pd->lcd_enabled) {
  672. /*
  673. * if lcd panel was off from bootloader then
  674. * current lcd status is powerdown and then
  675. * it enables lcd panel.
  676. */
  677. lcd->power = FB_BLANK_POWERDOWN;
  678. s6e63m0_power(lcd, FB_BLANK_UNBLANK);
  679. } else
  680. lcd->power = FB_BLANK_UNBLANK;
  681. dev_set_drvdata(&spi->dev, lcd);
  682. dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
  683. return 0;
  684. out_lcd_unregister:
  685. lcd_device_unregister(ld);
  686. out_free_lcd:
  687. kfree(lcd);
  688. return ret;
  689. }
  690. static int __devexit s6e63m0_remove(struct spi_device *spi)
  691. {
  692. struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
  693. s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
  694. device_remove_file(&spi->dev, &dev_attr_gamma_table);
  695. device_remove_file(&spi->dev, &dev_attr_gamma_mode);
  696. backlight_device_unregister(lcd->bd);
  697. lcd_device_unregister(lcd->ld);
  698. kfree(lcd);
  699. return 0;
  700. }
  701. #if defined(CONFIG_PM)
  702. unsigned int before_power;
  703. static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
  704. {
  705. int ret = 0;
  706. struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
  707. dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
  708. before_power = lcd->power;
  709. /*
  710. * when lcd panel is suspend, lcd panel becomes off
  711. * regardless of status.
  712. */
  713. ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
  714. return ret;
  715. }
  716. static int s6e63m0_resume(struct spi_device *spi)
  717. {
  718. int ret = 0;
  719. struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
  720. /*
  721. * after suspended, if lcd panel status is FB_BLANK_UNBLANK
  722. * (at that time, before_power is FB_BLANK_UNBLANK) then
  723. * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
  724. */
  725. if (before_power == FB_BLANK_UNBLANK)
  726. lcd->power = FB_BLANK_POWERDOWN;
  727. dev_dbg(&spi->dev, "before_power = %d\n", before_power);
  728. ret = s6e63m0_power(lcd, before_power);
  729. return ret;
  730. }
  731. #else
  732. #define s6e63m0_suspend NULL
  733. #define s6e63m0_resume NULL
  734. #endif
  735. /* Power down all displays on reboot, poweroff or halt. */
  736. static void s6e63m0_shutdown(struct spi_device *spi)
  737. {
  738. struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
  739. s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
  740. }
  741. static struct spi_driver s6e63m0_driver = {
  742. .driver = {
  743. .name = "s6e63m0",
  744. .bus = &spi_bus_type,
  745. .owner = THIS_MODULE,
  746. },
  747. .probe = s6e63m0_probe,
  748. .remove = __devexit_p(s6e63m0_remove),
  749. .shutdown = s6e63m0_shutdown,
  750. .suspend = s6e63m0_suspend,
  751. .resume = s6e63m0_resume,
  752. };
  753. static int __init s6e63m0_init(void)
  754. {
  755. return spi_register_driver(&s6e63m0_driver);
  756. }
  757. static void __exit s6e63m0_exit(void)
  758. {
  759. spi_unregister_driver(&s6e63m0_driver);
  760. }
  761. module_init(s6e63m0_init);
  762. module_exit(s6e63m0_exit);
  763. MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
  764. MODULE_DESCRIPTION("S6E63M0 LCD Driver");
  765. MODULE_LICENSE("GPL");