s6e63m0.c 18 KB

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