0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022 #include <linux/bitops.h>
0023 #include <linux/gpio/consumer.h>
0024 #include <linux/module.h>
0025 #include <linux/of_device.h>
0026 #include <linux/regmap.h>
0027 #include <linux/regulator/consumer.h>
0028 #include <linux/spi/spi.h>
0029
0030 #include <video/mipi_display.h>
0031 #include <video/of_videomode.h>
0032 #include <video/videomode.h>
0033
0034 #include <drm/drm_modes.h>
0035 #include <drm/drm_panel.h>
0036
0037 #define ILI9322_CHIP_ID 0x00
0038 #define ILI9322_CHIP_ID_MAGIC 0x96
0039
0040
0041
0042
0043
0044
0045 #define ILI9322_VCOM_AMP 0x01
0046
0047
0048
0049
0050
0051
0052 #define ILI9322_VCOM_HIGH 0x02
0053
0054
0055
0056
0057
0058
0059 #define ILI9322_VREG1_VOLTAGE 0x03
0060
0061
0062 #define ILI9322_ENTRY 0x06
0063
0064 #define ILI9322_ENTRY_HDIR BIT(0)
0065
0066 #define ILI9322_ENTRY_VDIR BIT(1)
0067
0068 #define ILI9322_ENTRY_NTSC (0 << 2)
0069 #define ILI9322_ENTRY_PAL (1 << 2)
0070 #define ILI9322_ENTRY_AUTODETECT (3 << 2)
0071
0072 #define ILI9322_ENTRY_SERIAL_RGB_THROUGH (0 << 4)
0073 #define ILI9322_ENTRY_SERIAL_RGB_ALIGNED (1 << 4)
0074 #define ILI9322_ENTRY_SERIAL_RGB_DUMMY_320X240 (2 << 4)
0075 #define ILI9322_ENTRY_SERIAL_RGB_DUMMY_360X240 (3 << 4)
0076 #define ILI9322_ENTRY_DISABLE_1 (4 << 4)
0077 #define ILI9322_ENTRY_PARALLEL_RGB_THROUGH (5 << 4)
0078 #define ILI9322_ENTRY_PARALLEL_RGB_ALIGNED (6 << 4)
0079 #define ILI9322_ENTRY_YUV_640Y_320CBCR_25_54_MHZ (7 << 4)
0080 #define ILI9322_ENTRY_YUV_720Y_360CBCR_27_MHZ (8 << 4)
0081 #define ILI9322_ENTRY_DISABLE_2 (9 << 4)
0082 #define ILI9322_ENTRY_ITU_R_BT_656_720X360 (10 << 4)
0083 #define ILI9322_ENTRY_ITU_R_BT_656_640X320 (11 << 4)
0084
0085
0086 #define ILI9322_POW_CTRL 0x07
0087 #define ILI9322_POW_CTRL_STB BIT(0)
0088 #define ILI9322_POW_CTRL_VGL BIT(1)
0089 #define ILI9322_POW_CTRL_VGH BIT(2)
0090 #define ILI9322_POW_CTRL_DDVDH BIT(3)
0091 #define ILI9322_POW_CTRL_VCOM BIT(4)
0092 #define ILI9322_POW_CTRL_VCL BIT(5)
0093 #define ILI9322_POW_CTRL_AUTO BIT(6)
0094 #define ILI9322_POW_CTRL_STANDBY (ILI9322_POW_CTRL_VGL | \
0095 ILI9322_POW_CTRL_VGH | \
0096 ILI9322_POW_CTRL_DDVDH | \
0097 ILI9322_POW_CTRL_VCL | \
0098 ILI9322_POW_CTRL_AUTO | \
0099 BIT(7))
0100 #define ILI9322_POW_CTRL_DEFAULT (ILI9322_POW_CTRL_STANDBY | \
0101 ILI9322_POW_CTRL_STB)
0102
0103
0104 #define ILI9322_VBP 0x08
0105
0106
0107 #define ILI9322_HBP 0x09
0108
0109
0110
0111
0112
0113
0114 #define ILI9322_POL 0x0a
0115 #define ILI9322_POL_DCLK BIT(0)
0116 #define ILI9322_POL_HSYNC BIT(1)
0117 #define ILI9322_POL_VSYNC BIT(2)
0118 #define ILI9322_POL_DE BIT(3)
0119
0120
0121
0122
0123
0124
0125 #define ILI9322_POL_YCBCR_MODE BIT(4)
0126
0127 #define ILI9322_POL_FORMULA BIT(5)
0128
0129 #define ILI9322_POL_REV BIT(6)
0130
0131 #define ILI9322_IF_CTRL 0x0b
0132 #define ILI9322_IF_CTRL_HSYNC_VSYNC 0x00
0133 #define ILI9322_IF_CTRL_HSYNC_VSYNC_DE BIT(2)
0134 #define ILI9322_IF_CTRL_DE_ONLY BIT(3)
0135 #define ILI9322_IF_CTRL_SYNC_DISABLED (BIT(2) | BIT(3))
0136 #define ILI9322_IF_CTRL_LINE_INVERSION BIT(0)
0137
0138 #define ILI9322_GLOBAL_RESET 0x04
0139 #define ILI9322_GLOBAL_RESET_ASSERT 0x00
0140
0141
0142
0143
0144
0145
0146 #define ILI9322_GAMMA_1 0x10
0147 #define ILI9322_GAMMA_2 0x11
0148 #define ILI9322_GAMMA_3 0x12
0149 #define ILI9322_GAMMA_4 0x13
0150 #define ILI9322_GAMMA_5 0x14
0151 #define ILI9322_GAMMA_6 0x15
0152 #define ILI9322_GAMMA_7 0x16
0153 #define ILI9322_GAMMA_8 0x17
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163 enum ili9322_input {
0164 ILI9322_INPUT_SRGB_THROUGH = 0x0,
0165 ILI9322_INPUT_SRGB_ALIGNED = 0x1,
0166 ILI9322_INPUT_SRGB_DUMMY_320X240 = 0x2,
0167 ILI9322_INPUT_SRGB_DUMMY_360X240 = 0x3,
0168 ILI9322_INPUT_DISABLED_1 = 0x4,
0169 ILI9322_INPUT_PRGB_THROUGH = 0x5,
0170 ILI9322_INPUT_PRGB_ALIGNED = 0x6,
0171 ILI9322_INPUT_YUV_640X320_YCBCR = 0x7,
0172 ILI9322_INPUT_YUV_720X360_YCBCR = 0x8,
0173 ILI9322_INPUT_DISABLED_2 = 0x9,
0174 ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR = 0xa,
0175 ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR = 0xb,
0176 ILI9322_INPUT_UNKNOWN = 0xc,
0177 };
0178
0179 static const char * const ili9322_inputs[] = {
0180 "8 bit serial RGB through",
0181 "8 bit serial RGB aligned",
0182 "8 bit serial RGB dummy 320x240",
0183 "8 bit serial RGB dummy 360x240",
0184 "disabled 1",
0185 "24 bit parallel RGB through",
0186 "24 bit parallel RGB aligned",
0187 "24 bit YUV 640Y 320CbCr",
0188 "24 bit YUV 720Y 360CbCr",
0189 "disabled 2",
0190 "8 bit ITU-R BT.656 720Y 360CbCr",
0191 "8 bit ITU-R BT.656 640Y 320CbCr",
0192 };
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251 struct ili9322_config {
0252 u32 width_mm;
0253 u32 height_mm;
0254 bool flip_horizontal;
0255 bool flip_vertical;
0256 enum ili9322_input input;
0257 u32 vreg1out_mv;
0258 u32 vcom_high_percent;
0259 u32 vcom_amplitude_percent;
0260 bool dclk_active_high;
0261 bool de_active_high;
0262 bool hsync_active_high;
0263 bool vsync_active_high;
0264 u8 syncmode;
0265 u8 gamma_corr_pos[8];
0266 u8 gamma_corr_neg[8];
0267 };
0268
0269 struct ili9322 {
0270 struct device *dev;
0271 const struct ili9322_config *conf;
0272 struct drm_panel panel;
0273 struct regmap *regmap;
0274 struct regulator_bulk_data supplies[3];
0275 struct gpio_desc *reset_gpio;
0276 enum ili9322_input input;
0277 struct videomode vm;
0278 u8 gamma[8];
0279 u8 vreg1out;
0280 u8 vcom_high;
0281 u8 vcom_amplitude;
0282 };
0283
0284 static inline struct ili9322 *panel_to_ili9322(struct drm_panel *panel)
0285 {
0286 return container_of(panel, struct ili9322, panel);
0287 }
0288
0289 static int ili9322_regmap_spi_write(void *context, const void *data,
0290 size_t count)
0291 {
0292 struct device *dev = context;
0293 struct spi_device *spi = to_spi_device(dev);
0294 u8 buf[2];
0295
0296
0297 memcpy(buf, data, 2);
0298 buf[0] &= ~0x80;
0299
0300 dev_dbg(dev, "WRITE: %02x %02x\n", buf[0], buf[1]);
0301 return spi_write_then_read(spi, buf, 2, NULL, 0);
0302 }
0303
0304 static int ili9322_regmap_spi_read(void *context, const void *reg,
0305 size_t reg_size, void *val, size_t val_size)
0306 {
0307 struct device *dev = context;
0308 struct spi_device *spi = to_spi_device(dev);
0309 u8 buf[1];
0310
0311
0312 memcpy(buf, reg, 1);
0313 dev_dbg(dev, "READ: %02x reg size = %zu, val size = %zu\n",
0314 buf[0], reg_size, val_size);
0315 buf[0] |= 0x80;
0316
0317 return spi_write_then_read(spi, buf, 1, val, 1);
0318 }
0319
0320 static struct regmap_bus ili9322_regmap_bus = {
0321 .write = ili9322_regmap_spi_write,
0322 .read = ili9322_regmap_spi_read,
0323 .reg_format_endian_default = REGMAP_ENDIAN_BIG,
0324 .val_format_endian_default = REGMAP_ENDIAN_BIG,
0325 };
0326
0327 static bool ili9322_volatile_reg(struct device *dev, unsigned int reg)
0328 {
0329 return false;
0330 }
0331
0332 static bool ili9322_writeable_reg(struct device *dev, unsigned int reg)
0333 {
0334
0335 if (reg == 0x00)
0336 return false;
0337 return true;
0338 }
0339
0340 static const struct regmap_config ili9322_regmap_config = {
0341 .reg_bits = 8,
0342 .val_bits = 8,
0343 .max_register = 0x44,
0344 .cache_type = REGCACHE_RBTREE,
0345 .volatile_reg = ili9322_volatile_reg,
0346 .writeable_reg = ili9322_writeable_reg,
0347 };
0348
0349 static int ili9322_init(struct drm_panel *panel, struct ili9322 *ili)
0350 {
0351 u8 reg;
0352 int ret;
0353 int i;
0354
0355
0356 ret = regmap_write(ili->regmap, ILI9322_GLOBAL_RESET,
0357 ILI9322_GLOBAL_RESET_ASSERT);
0358 if (ret) {
0359 dev_err(ili->dev, "can't issue GRESET (%d)\n", ret);
0360 return ret;
0361 }
0362
0363
0364 if (ili->vreg1out != U8_MAX) {
0365 ret = regmap_write(ili->regmap, ILI9322_VREG1_VOLTAGE,
0366 ili->vreg1out);
0367 if (ret) {
0368 dev_err(ili->dev, "can't set up VREG1OUT (%d)\n", ret);
0369 return ret;
0370 }
0371 }
0372
0373 if (ili->vcom_amplitude != U8_MAX) {
0374 ret = regmap_write(ili->regmap, ILI9322_VCOM_AMP,
0375 ili->vcom_amplitude);
0376 if (ret) {
0377 dev_err(ili->dev,
0378 "can't set up VCOM amplitude (%d)\n", ret);
0379 return ret;
0380 }
0381 }
0382
0383 if (ili->vcom_high != U8_MAX) {
0384 ret = regmap_write(ili->regmap, ILI9322_VCOM_HIGH,
0385 ili->vcom_high);
0386 if (ret) {
0387 dev_err(ili->dev, "can't set up VCOM high (%d)\n", ret);
0388 return ret;
0389 }
0390 }
0391
0392
0393 for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) {
0394 ret = regmap_write(ili->regmap, ILI9322_GAMMA_1 + i,
0395 ili->gamma[i]);
0396 if (ret) {
0397 dev_err(ili->dev,
0398 "can't write gamma V%d to 0x%02x (%d)\n",
0399 i + 1, ILI9322_GAMMA_1 + i, ret);
0400 return ret;
0401 }
0402 }
0403
0404
0405
0406
0407
0408 reg = 0;
0409 if (ili->conf->dclk_active_high)
0410 reg = ILI9322_POL_DCLK;
0411 if (ili->conf->de_active_high)
0412 reg |= ILI9322_POL_DE;
0413 if (ili->conf->hsync_active_high)
0414 reg |= ILI9322_POL_HSYNC;
0415 if (ili->conf->vsync_active_high)
0416 reg |= ILI9322_POL_VSYNC;
0417 ret = regmap_write(ili->regmap, ILI9322_POL, reg);
0418 if (ret) {
0419 dev_err(ili->dev, "can't write POL register (%d)\n", ret);
0420 return ret;
0421 }
0422
0423
0424
0425
0426
0427 reg = ili->conf->syncmode;
0428 reg |= ILI9322_IF_CTRL_LINE_INVERSION;
0429 ret = regmap_write(ili->regmap, ILI9322_IF_CTRL, reg);
0430 if (ret) {
0431 dev_err(ili->dev, "can't write IF CTRL register (%d)\n", ret);
0432 return ret;
0433 }
0434
0435
0436 reg = (ili->input << 4);
0437
0438 if (!ili->conf->flip_horizontal)
0439 reg |= ILI9322_ENTRY_HDIR;
0440 if (!ili->conf->flip_vertical)
0441 reg |= ILI9322_ENTRY_VDIR;
0442 reg |= ILI9322_ENTRY_AUTODETECT;
0443 ret = regmap_write(ili->regmap, ILI9322_ENTRY, reg);
0444 if (ret) {
0445 dev_err(ili->dev, "can't write ENTRY reg (%d)\n", ret);
0446 return ret;
0447 }
0448 dev_info(ili->dev, "display is in %s mode, syncmode %02x\n",
0449 ili9322_inputs[ili->input],
0450 ili->conf->syncmode);
0451
0452 dev_info(ili->dev, "initialized display\n");
0453
0454 return 0;
0455 }
0456
0457
0458
0459
0460 static int ili9322_power_on(struct ili9322 *ili)
0461 {
0462 int ret;
0463
0464
0465 gpiod_set_value(ili->reset_gpio, 1);
0466
0467 ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), ili->supplies);
0468 if (ret < 0) {
0469 dev_err(ili->dev, "unable to enable regulators\n");
0470 return ret;
0471 }
0472 msleep(20);
0473
0474
0475 gpiod_set_value(ili->reset_gpio, 0);
0476
0477 msleep(10);
0478
0479 return 0;
0480 }
0481
0482 static int ili9322_power_off(struct ili9322 *ili)
0483 {
0484 return regulator_bulk_disable(ARRAY_SIZE(ili->supplies), ili->supplies);
0485 }
0486
0487 static int ili9322_disable(struct drm_panel *panel)
0488 {
0489 struct ili9322 *ili = panel_to_ili9322(panel);
0490 int ret;
0491
0492 ret = regmap_write(ili->regmap, ILI9322_POW_CTRL,
0493 ILI9322_POW_CTRL_STANDBY);
0494 if (ret) {
0495 dev_err(ili->dev, "unable to go to standby mode\n");
0496 return ret;
0497 }
0498
0499 return 0;
0500 }
0501
0502 static int ili9322_unprepare(struct drm_panel *panel)
0503 {
0504 struct ili9322 *ili = panel_to_ili9322(panel);
0505
0506 return ili9322_power_off(ili);
0507 }
0508
0509 static int ili9322_prepare(struct drm_panel *panel)
0510 {
0511 struct ili9322 *ili = panel_to_ili9322(panel);
0512 int ret;
0513
0514 ret = ili9322_power_on(ili);
0515 if (ret < 0)
0516 return ret;
0517
0518 ret = ili9322_init(panel, ili);
0519 if (ret < 0)
0520 ili9322_unprepare(panel);
0521
0522 return ret;
0523 }
0524
0525 static int ili9322_enable(struct drm_panel *panel)
0526 {
0527 struct ili9322 *ili = panel_to_ili9322(panel);
0528 int ret;
0529
0530 ret = regmap_write(ili->regmap, ILI9322_POW_CTRL,
0531 ILI9322_POW_CTRL_DEFAULT);
0532 if (ret) {
0533 dev_err(ili->dev, "unable to enable panel\n");
0534 return ret;
0535 }
0536
0537 return 0;
0538 }
0539
0540
0541 static const struct drm_display_mode srgb_320x240_mode = {
0542 .clock = 24535,
0543 .hdisplay = 320,
0544 .hsync_start = 320 + 359,
0545 .hsync_end = 320 + 359 + 1,
0546 .htotal = 320 + 359 + 1 + 241,
0547 .vdisplay = 240,
0548 .vsync_start = 240 + 4,
0549 .vsync_end = 240 + 4 + 1,
0550 .vtotal = 262,
0551 .flags = 0,
0552 };
0553
0554 static const struct drm_display_mode srgb_360x240_mode = {
0555 .clock = 27000,
0556 .hdisplay = 360,
0557 .hsync_start = 360 + 35,
0558 .hsync_end = 360 + 35 + 1,
0559 .htotal = 360 + 35 + 1 + 241,
0560 .vdisplay = 240,
0561 .vsync_start = 240 + 21,
0562 .vsync_end = 240 + 21 + 1,
0563 .vtotal = 262,
0564 .flags = 0,
0565 };
0566
0567
0568 static const struct drm_display_mode prgb_320x240_mode = {
0569 .clock = 64000,
0570 .hdisplay = 320,
0571 .hsync_start = 320 + 38,
0572 .hsync_end = 320 + 38 + 1,
0573 .htotal = 320 + 38 + 1 + 50,
0574 .vdisplay = 240,
0575 .vsync_start = 240 + 4,
0576 .vsync_end = 240 + 4 + 1,
0577 .vtotal = 262,
0578 .flags = 0,
0579 };
0580
0581
0582 static const struct drm_display_mode yuv_640x320_mode = {
0583 .clock = 24540,
0584 .hdisplay = 640,
0585 .hsync_start = 640 + 252,
0586 .hsync_end = 640 + 252 + 1,
0587 .htotal = 640 + 252 + 1 + 28,
0588 .vdisplay = 320,
0589 .vsync_start = 320 + 4,
0590 .vsync_end = 320 + 4 + 1,
0591 .vtotal = 320 + 4 + 1 + 18,
0592 .flags = 0,
0593 };
0594
0595 static const struct drm_display_mode yuv_720x360_mode = {
0596 .clock = 27000,
0597 .hdisplay = 720,
0598 .hsync_start = 720 + 252,
0599 .hsync_end = 720 + 252 + 1,
0600 .htotal = 720 + 252 + 1 + 24,
0601 .vdisplay = 360,
0602 .vsync_start = 360 + 4,
0603 .vsync_end = 360 + 4 + 1,
0604 .vtotal = 360 + 4 + 1 + 18,
0605 .flags = 0,
0606 };
0607
0608
0609 static const struct drm_display_mode itu_r_bt_656_640_mode = {
0610 .clock = 24540,
0611 .hdisplay = 640,
0612 .hsync_start = 640 + 3,
0613 .hsync_end = 640 + 3 + 1,
0614 .htotal = 640 + 3 + 1 + 272,
0615 .vdisplay = 480,
0616 .vsync_start = 480 + 4,
0617 .vsync_end = 480 + 4 + 1,
0618 .vtotal = 500,
0619 .flags = 0,
0620 };
0621
0622
0623 static const struct drm_display_mode itu_r_bt_656_720_mode = {
0624 .clock = 27000,
0625 .hdisplay = 720,
0626 .hsync_start = 720 + 3,
0627 .hsync_end = 720 + 3 + 1,
0628 .htotal = 720 + 3 + 1 + 272,
0629 .vdisplay = 480,
0630 .vsync_start = 480 + 4,
0631 .vsync_end = 480 + 4 + 1,
0632 .vtotal = 500,
0633 .flags = 0,
0634 };
0635
0636 static int ili9322_get_modes(struct drm_panel *panel,
0637 struct drm_connector *connector)
0638 {
0639 struct ili9322 *ili = panel_to_ili9322(panel);
0640 struct drm_device *drm = connector->dev;
0641 struct drm_display_mode *mode;
0642 struct drm_display_info *info;
0643
0644 info = &connector->display_info;
0645 info->width_mm = ili->conf->width_mm;
0646 info->height_mm = ili->conf->height_mm;
0647 if (ili->conf->dclk_active_high)
0648 info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
0649 else
0650 info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
0651
0652 if (ili->conf->de_active_high)
0653 info->bus_flags |= DRM_BUS_FLAG_DE_HIGH;
0654 else
0655 info->bus_flags |= DRM_BUS_FLAG_DE_LOW;
0656
0657 switch (ili->input) {
0658 case ILI9322_INPUT_SRGB_DUMMY_320X240:
0659 mode = drm_mode_duplicate(drm, &srgb_320x240_mode);
0660 break;
0661 case ILI9322_INPUT_SRGB_DUMMY_360X240:
0662 mode = drm_mode_duplicate(drm, &srgb_360x240_mode);
0663 break;
0664 case ILI9322_INPUT_PRGB_THROUGH:
0665 case ILI9322_INPUT_PRGB_ALIGNED:
0666 mode = drm_mode_duplicate(drm, &prgb_320x240_mode);
0667 break;
0668 case ILI9322_INPUT_YUV_640X320_YCBCR:
0669 mode = drm_mode_duplicate(drm, &yuv_640x320_mode);
0670 break;
0671 case ILI9322_INPUT_YUV_720X360_YCBCR:
0672 mode = drm_mode_duplicate(drm, &yuv_720x360_mode);
0673 break;
0674 case ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR:
0675 mode = drm_mode_duplicate(drm, &itu_r_bt_656_720_mode);
0676 break;
0677 case ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR:
0678 mode = drm_mode_duplicate(drm, &itu_r_bt_656_640_mode);
0679 break;
0680 default:
0681 mode = NULL;
0682 break;
0683 }
0684 if (!mode) {
0685 dev_err(panel->dev, "bad mode or failed to add mode\n");
0686 return -EINVAL;
0687 }
0688 drm_mode_set_name(mode);
0689
0690
0691
0692
0693 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0694
0695
0696 if (ili->conf->hsync_active_high)
0697 mode->flags |= DRM_MODE_FLAG_PHSYNC;
0698 else
0699 mode->flags |= DRM_MODE_FLAG_NHSYNC;
0700 if (ili->conf->vsync_active_high)
0701 mode->flags |= DRM_MODE_FLAG_PVSYNC;
0702 else
0703 mode->flags |= DRM_MODE_FLAG_NVSYNC;
0704
0705 mode->width_mm = ili->conf->width_mm;
0706 mode->height_mm = ili->conf->height_mm;
0707 drm_mode_probed_add(connector, mode);
0708
0709 return 1;
0710 }
0711
0712 static const struct drm_panel_funcs ili9322_drm_funcs = {
0713 .disable = ili9322_disable,
0714 .unprepare = ili9322_unprepare,
0715 .prepare = ili9322_prepare,
0716 .enable = ili9322_enable,
0717 .get_modes = ili9322_get_modes,
0718 };
0719
0720 static int ili9322_probe(struct spi_device *spi)
0721 {
0722 struct device *dev = &spi->dev;
0723 struct ili9322 *ili;
0724 const struct regmap_config *regmap_config;
0725 u8 gamma;
0726 u32 val;
0727 int ret;
0728 int i;
0729
0730 ili = devm_kzalloc(dev, sizeof(struct ili9322), GFP_KERNEL);
0731 if (!ili)
0732 return -ENOMEM;
0733
0734 spi_set_drvdata(spi, ili);
0735
0736 ili->dev = dev;
0737
0738
0739
0740
0741
0742 ili->conf = of_device_get_match_data(dev);
0743 if (!ili->conf) {
0744 dev_err(dev, "missing device configuration\n");
0745 return -ENODEV;
0746 }
0747
0748 val = ili->conf->vreg1out_mv;
0749 if (!val) {
0750
0751 ili->vreg1out = U8_MAX;
0752 } else {
0753 if (val < 3600) {
0754 dev_err(dev, "too low VREG1OUT\n");
0755 return -EINVAL;
0756 }
0757 if (val > 6000) {
0758 dev_err(dev, "too high VREG1OUT\n");
0759 return -EINVAL;
0760 }
0761 if ((val % 100) != 0) {
0762 dev_err(dev, "VREG1OUT is no even 100 microvolt\n");
0763 return -EINVAL;
0764 }
0765 val -= 3600;
0766 val /= 100;
0767 dev_dbg(dev, "VREG1OUT = 0x%02x\n", val);
0768 ili->vreg1out = val;
0769 }
0770
0771 val = ili->conf->vcom_high_percent;
0772 if (!val) {
0773
0774 ili->vcom_high = U8_MAX;
0775 } else {
0776 if (val < 37) {
0777 dev_err(dev, "too low VCOM high\n");
0778 return -EINVAL;
0779 }
0780 if (val > 100) {
0781 dev_err(dev, "too high VCOM high\n");
0782 return -EINVAL;
0783 }
0784 val -= 37;
0785 dev_dbg(dev, "VCOM high = 0x%02x\n", val);
0786 ili->vcom_high = val;
0787 }
0788
0789 val = ili->conf->vcom_amplitude_percent;
0790 if (!val) {
0791
0792 ili->vcom_high = U8_MAX;
0793 } else {
0794 if (val < 70) {
0795 dev_err(dev, "too low VCOM amplitude\n");
0796 return -EINVAL;
0797 }
0798 if (val > 132) {
0799 dev_err(dev, "too high VCOM amplitude\n");
0800 return -EINVAL;
0801 }
0802 val -= 70;
0803 val >>= 1;
0804 dev_dbg(dev, "VCOM amplitude = 0x%02x\n", val);
0805 ili->vcom_amplitude = val;
0806 }
0807
0808 for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) {
0809 val = ili->conf->gamma_corr_neg[i];
0810 if (val > 15) {
0811 dev_err(dev, "negative gamma %u > 15, capping\n", val);
0812 val = 15;
0813 }
0814 gamma = val << 4;
0815 val = ili->conf->gamma_corr_pos[i];
0816 if (val > 15) {
0817 dev_err(dev, "positive gamma %u > 15, capping\n", val);
0818 val = 15;
0819 }
0820 gamma |= val;
0821 ili->gamma[i] = gamma;
0822 dev_dbg(dev, "gamma V%d: 0x%02x\n", i + 1, gamma);
0823 }
0824
0825 ili->supplies[0].supply = "vcc";
0826 ili->supplies[1].supply = "iovcc";
0827 ili->supplies[2].supply = "vci";
0828 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies),
0829 ili->supplies);
0830 if (ret < 0)
0831 return ret;
0832 ret = regulator_set_voltage(ili->supplies[0].consumer,
0833 2700000, 3600000);
0834 if (ret)
0835 return ret;
0836 ret = regulator_set_voltage(ili->supplies[1].consumer,
0837 1650000, 3600000);
0838 if (ret)
0839 return ret;
0840 ret = regulator_set_voltage(ili->supplies[2].consumer,
0841 2700000, 3600000);
0842 if (ret)
0843 return ret;
0844
0845 ili->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
0846 if (IS_ERR(ili->reset_gpio)) {
0847 dev_err(dev, "failed to get RESET GPIO\n");
0848 return PTR_ERR(ili->reset_gpio);
0849 }
0850
0851 spi->bits_per_word = 8;
0852 ret = spi_setup(spi);
0853 if (ret < 0) {
0854 dev_err(dev, "spi setup failed.\n");
0855 return ret;
0856 }
0857 regmap_config = &ili9322_regmap_config;
0858 ili->regmap = devm_regmap_init(dev, &ili9322_regmap_bus, dev,
0859 regmap_config);
0860 if (IS_ERR(ili->regmap)) {
0861 dev_err(dev, "failed to allocate register map\n");
0862 return PTR_ERR(ili->regmap);
0863 }
0864
0865 ret = regmap_read(ili->regmap, ILI9322_CHIP_ID, &val);
0866 if (ret) {
0867 dev_err(dev, "can't get chip ID (%d)\n", ret);
0868 return ret;
0869 }
0870 if (val != ILI9322_CHIP_ID_MAGIC) {
0871 dev_err(dev, "chip ID 0x%0x2, expected 0x%02x\n", val,
0872 ILI9322_CHIP_ID_MAGIC);
0873 return -ENODEV;
0874 }
0875
0876
0877 if (ili->conf->input == ILI9322_INPUT_UNKNOWN) {
0878 ret = regmap_read(ili->regmap, ILI9322_ENTRY, &val);
0879 if (ret) {
0880 dev_err(dev, "can't get entry setting (%d)\n", ret);
0881 return ret;
0882 }
0883
0884 ili->input = (val >> 4) & 0x0f;
0885 if (ili->input >= ILI9322_INPUT_UNKNOWN)
0886 ili->input = ILI9322_INPUT_UNKNOWN;
0887 } else {
0888 ili->input = ili->conf->input;
0889 }
0890
0891 drm_panel_init(&ili->panel, dev, &ili9322_drm_funcs,
0892 DRM_MODE_CONNECTOR_DPI);
0893
0894 drm_panel_add(&ili->panel);
0895
0896 return 0;
0897 }
0898
0899 static void ili9322_remove(struct spi_device *spi)
0900 {
0901 struct ili9322 *ili = spi_get_drvdata(spi);
0902
0903 ili9322_power_off(ili);
0904 drm_panel_remove(&ili->panel);
0905 }
0906
0907
0908
0909
0910 static const struct ili9322_config ili9322_dir_685 = {
0911 .width_mm = 65,
0912 .height_mm = 50,
0913 .input = ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR,
0914 .vreg1out_mv = 4600,
0915 .vcom_high_percent = 91,
0916 .vcom_amplitude_percent = 114,
0917 .syncmode = ILI9322_IF_CTRL_SYNC_DISABLED,
0918 .dclk_active_high = true,
0919 .gamma_corr_neg = { 0xa, 0x5, 0x7, 0x7, 0x7, 0x5, 0x1, 0x6 },
0920 .gamma_corr_pos = { 0x7, 0x7, 0x3, 0x2, 0x3, 0x5, 0x7, 0x2 },
0921 };
0922
0923 static const struct of_device_id ili9322_of_match[] = {
0924 {
0925 .compatible = "dlink,dir-685-panel",
0926 .data = &ili9322_dir_685,
0927 },
0928 {
0929 .compatible = "ilitek,ili9322",
0930 .data = NULL,
0931 },
0932 { }
0933 };
0934 MODULE_DEVICE_TABLE(of, ili9322_of_match);
0935
0936 static struct spi_driver ili9322_driver = {
0937 .probe = ili9322_probe,
0938 .remove = ili9322_remove,
0939 .driver = {
0940 .name = "panel-ilitek-ili9322",
0941 .of_match_table = ili9322_of_match,
0942 },
0943 };
0944 module_spi_driver(ili9322_driver);
0945
0946 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
0947 MODULE_DESCRIPTION("ILI9322 LCD panel driver");
0948 MODULE_LICENSE("GPL v2");