^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Power control for Samsung LTV350QV Quarter VGA LCD Panel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2006, 2007 Atmel Corporation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/fb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/lcd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/spi/spi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include "ltv350qv.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) struct ltv350qv {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) struct spi_device *spi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) u8 *buffer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) int power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct lcd_device *ld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * The power-on and power-off sequences are taken from the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * LTV350QV-F04 data sheet from Samsung. The register definitions are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * taken from the S6F2002 command list also from Samsung. Both
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * documents are distributed with the AVR32 Linux BSP CD from Atmel.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * There's still some voodoo going on here, but it's a lot better than
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * in the first incarnation of the driver where all we had was the raw
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * numbers from the initialization sequence.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static int ltv350qv_write_reg(struct ltv350qv *lcd, u8 reg, u16 val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct spi_message msg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) struct spi_transfer index_xfer = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) .len = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) .cs_change = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) struct spi_transfer value_xfer = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) .len = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) spi_message_init(&msg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) /* register index */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) lcd->buffer[0] = LTV_OPC_INDEX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) lcd->buffer[1] = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) lcd->buffer[2] = reg & 0x7f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) index_xfer.tx_buf = lcd->buffer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) spi_message_add_tail(&index_xfer, &msg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) /* register value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) lcd->buffer[4] = LTV_OPC_DATA;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) lcd->buffer[5] = val >> 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) lcd->buffer[6] = val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) value_xfer.tx_buf = lcd->buffer + 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) spi_message_add_tail(&value_xfer, &msg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) return spi_sync(lcd->spi, &msg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) /* The comments are taken straight from the data sheet */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static int ltv350qv_power_on(struct ltv350qv *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) /* Power On Reset Display off State */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, 0x0000))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) usleep_range(15000, 16000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) /* Power Setting Function 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) if (ltv350qv_write_reg(lcd, LTV_PWRCTL2, LTV_VCOML_ENABLE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) goto err_power1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) /* Power Setting Function 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) if (ltv350qv_write_reg(lcd, LTV_PWRCTL1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) | LTV_SUPPLY_CURRENT(5)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) goto err_power2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) msleep(55);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) /* Instruction Setting */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) ret = ltv350qv_write_reg(lcd, LTV_IFCTL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) LTV_NMD | LTV_REV | LTV_NL(0x1d));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) ret |= ltv350qv_write_reg(lcd, LTV_DATACTL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) LTV_DS_SAME | LTV_CHS_480
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) | LTV_DF_RGB | LTV_RGB_BGR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) ret |= ltv350qv_write_reg(lcd, LTV_ENTRY_MODE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) LTV_VSPL_ACTIVE_LOW
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) | LTV_HSPL_ACTIVE_LOW
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) | LTV_DPL_SAMPLE_RISING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) | LTV_EPL_ACTIVE_LOW
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) | LTV_SS_RIGHT_TO_LEFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) ret |= ltv350qv_write_reg(lcd, LTV_GATECTL1, LTV_CLW(3));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) LTV_NW_INV_1LINE | LTV_FWI(3));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) ret |= ltv350qv_write_reg(lcd, LTV_VBP, 0x000a);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) ret |= ltv350qv_write_reg(lcd, LTV_HBP, 0x0021);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) ret |= ltv350qv_write_reg(lcd, LTV_SOTCTL, LTV_SDT(3) | LTV_EQ(0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(0), 0x0103);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(1), 0x0301);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(2), 0x1f0f);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(3), 0x1f0f);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(4), 0x0707);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(5), 0x0307);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(6), 0x0707);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(7), 0x0000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(8), 0x0004);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(9), 0x0000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) goto err_settings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) /* Wait more than 2 frames */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) msleep(20);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) /* Display On Sequence */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) LTV_VCOM_DISABLE | LTV_VCOMOUT_ENABLE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) | LTV_POWER_ON | LTV_DRIVE_CURRENT(5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) | LTV_SUPPLY_CURRENT(5));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) LTV_NW_INV_1LINE | LTV_DSC | LTV_FWI(3));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) goto err_disp_on;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) /* Display should now be ON. Phew. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) err_disp_on:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) * Try to recover. Error handling probably isn't very useful
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) * at this point, just make a best effort to switch the panel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) * off.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) ltv350qv_write_reg(lcd, LTV_PWRCTL1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) | LTV_SUPPLY_CURRENT(5));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) ltv350qv_write_reg(lcd, LTV_GATECTL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) LTV_NW_INV_1LINE | LTV_FWI(3));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) err_settings:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) err_power2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) err_power1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) usleep_range(1000, 1100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) err:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static int ltv350qv_power_off(struct ltv350qv *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) /* Display Off Sequence */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) LTV_VCOM_DISABLE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) | LTV_DRIVE_CURRENT(5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) | LTV_SUPPLY_CURRENT(5));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) LTV_NW_INV_1LINE | LTV_FWI(3));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) /* Power down setting 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) /* Wait at least 1 ms */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) usleep_range(1000, 1100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) /* Power down setting 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) * No point in trying to recover here. If we can't switch the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) * panel off, what are we supposed to do other than inform the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) * user about the failure?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) /* Display power should now be OFF */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) static int ltv350qv_power(struct ltv350qv *lcd, int power)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) ret = ltv350qv_power_on(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) ret = ltv350qv_power_off(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) if (!ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) lcd->power = power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) static int ltv350qv_set_power(struct lcd_device *ld, int power)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) struct ltv350qv *lcd = lcd_get_data(ld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) return ltv350qv_power(lcd, power);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) static int ltv350qv_get_power(struct lcd_device *ld)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) struct ltv350qv *lcd = lcd_get_data(ld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) return lcd->power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) static struct lcd_ops ltv_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) .get_power = ltv350qv_get_power,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) .set_power = ltv350qv_set_power,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) static int ltv350qv_probe(struct spi_device *spi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) struct ltv350qv *lcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) struct lcd_device *ld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) lcd = devm_kzalloc(&spi->dev, sizeof(struct ltv350qv), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) if (!lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) lcd->spi = spi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) lcd->power = FB_BLANK_POWERDOWN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) lcd->buffer = devm_kzalloc(&spi->dev, 8, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) if (!lcd->buffer)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) ld = devm_lcd_device_register(&spi->dev, "ltv350qv", &spi->dev, lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) <v_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) if (IS_ERR(ld))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) return PTR_ERR(ld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) lcd->ld = ld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) spi_set_drvdata(spi, lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) static int ltv350qv_remove(struct spi_device *spi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) struct ltv350qv *lcd = spi_get_drvdata(spi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) #ifdef CONFIG_PM_SLEEP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) static int ltv350qv_suspend(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) struct ltv350qv *lcd = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) return ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) static int ltv350qv_resume(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) struct ltv350qv *lcd = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) return ltv350qv_power(lcd, FB_BLANK_UNBLANK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) static SIMPLE_DEV_PM_OPS(ltv350qv_pm_ops, ltv350qv_suspend, ltv350qv_resume);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) /* Power down all displays on reboot, poweroff or halt */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) static void ltv350qv_shutdown(struct spi_device *spi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) struct ltv350qv *lcd = spi_get_drvdata(spi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) static struct spi_driver ltv350qv_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) .name = "ltv350qv",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) .pm = <v350qv_pm_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) .probe = ltv350qv_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) .remove = ltv350qv_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) .shutdown = ltv350qv_shutdown,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) module_spi_driver(ltv350qv_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) MODULE_ALIAS("spi:ltv350qv");