Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Toppoly TD028TTEC1 panel support
0004  *
0005  * Copyright (C) 2008 Nokia Corporation
0006  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
0007  *
0008  * Neo 1973 code (jbt6k74.c):
0009  * Copyright (C) 2006-2007 by OpenMoko, Inc.
0010  * Author: Harald Welte <laforge@openmoko.org>
0011  *
0012  * Ported and adapted from Neo 1973 U-Boot by:
0013  * H. Nikolaus Schaller <hns@goldelico.com>
0014  */
0015 
0016 #include <linux/module.h>
0017 #include <linux/delay.h>
0018 #include <linux/spi/spi.h>
0019 #include <linux/gpio.h>
0020 #include <video/omapfb_dss.h>
0021 
0022 struct panel_drv_data {
0023     struct omap_dss_device dssdev;
0024     struct omap_dss_device *in;
0025 
0026     int data_lines;
0027 
0028     struct omap_video_timings videomode;
0029 
0030     struct spi_device *spi_dev;
0031 };
0032 
0033 static const struct omap_video_timings td028ttec1_panel_timings = {
0034     .x_res      = 480,
0035     .y_res      = 640,
0036     .pixelclock = 22153000,
0037     .hfp        = 24,
0038     .hsw        = 8,
0039     .hbp        = 8,
0040     .vfp        = 4,
0041     .vsw        = 2,
0042     .vbp        = 2,
0043 
0044     .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
0045     .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
0046 
0047     .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
0048     .de_level   = OMAPDSS_SIG_ACTIVE_HIGH,
0049     .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
0050 };
0051 
0052 #define JBT_COMMAND 0x000
0053 #define JBT_DATA    0x100
0054 
0055 static int jbt_ret_write_0(struct panel_drv_data *ddata, u8 reg)
0056 {
0057     int rc;
0058     u16 tx_buf = JBT_COMMAND | reg;
0059 
0060     rc = spi_write(ddata->spi_dev, (u8 *)&tx_buf,
0061             1*sizeof(u16));
0062     if (rc != 0)
0063         dev_err(&ddata->spi_dev->dev,
0064             "jbt_ret_write_0 spi_write ret %d\n", rc);
0065 
0066     return rc;
0067 }
0068 
0069 static int jbt_reg_write_1(struct panel_drv_data *ddata, u8 reg, u8 data)
0070 {
0071     int rc;
0072     u16 tx_buf[2];
0073 
0074     tx_buf[0] = JBT_COMMAND | reg;
0075     tx_buf[1] = JBT_DATA | data;
0076     rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
0077             2*sizeof(u16));
0078     if (rc != 0)
0079         dev_err(&ddata->spi_dev->dev,
0080             "jbt_reg_write_1 spi_write ret %d\n", rc);
0081 
0082     return rc;
0083 }
0084 
0085 static int jbt_reg_write_2(struct panel_drv_data *ddata, u8 reg, u16 data)
0086 {
0087     int rc;
0088     u16 tx_buf[3];
0089 
0090     tx_buf[0] = JBT_COMMAND | reg;
0091     tx_buf[1] = JBT_DATA | (data >> 8);
0092     tx_buf[2] = JBT_DATA | (data & 0xff);
0093 
0094     rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
0095             3*sizeof(u16));
0096 
0097     if (rc != 0)
0098         dev_err(&ddata->spi_dev->dev,
0099             "jbt_reg_write_2 spi_write ret %d\n", rc);
0100 
0101     return rc;
0102 }
0103 
0104 enum jbt_register {
0105     JBT_REG_SLEEP_IN        = 0x10,
0106     JBT_REG_SLEEP_OUT       = 0x11,
0107 
0108     JBT_REG_DISPLAY_OFF     = 0x28,
0109     JBT_REG_DISPLAY_ON      = 0x29,
0110 
0111     JBT_REG_RGB_FORMAT      = 0x3a,
0112     JBT_REG_QUAD_RATE       = 0x3b,
0113 
0114     JBT_REG_POWER_ON_OFF        = 0xb0,
0115     JBT_REG_BOOSTER_OP      = 0xb1,
0116     JBT_REG_BOOSTER_MODE        = 0xb2,
0117     JBT_REG_BOOSTER_FREQ        = 0xb3,
0118     JBT_REG_OPAMP_SYSCLK        = 0xb4,
0119     JBT_REG_VSC_VOLTAGE     = 0xb5,
0120     JBT_REG_VCOM_VOLTAGE        = 0xb6,
0121     JBT_REG_EXT_DISPL       = 0xb7,
0122     JBT_REG_OUTPUT_CONTROL      = 0xb8,
0123     JBT_REG_DCCLK_DCEV      = 0xb9,
0124     JBT_REG_DISPLAY_MODE1       = 0xba,
0125     JBT_REG_DISPLAY_MODE2       = 0xbb,
0126     JBT_REG_DISPLAY_MODE        = 0xbc,
0127     JBT_REG_ASW_SLEW        = 0xbd,
0128     JBT_REG_DUMMY_DISPLAY       = 0xbe,
0129     JBT_REG_DRIVE_SYSTEM        = 0xbf,
0130 
0131     JBT_REG_SLEEP_OUT_FR_A      = 0xc0,
0132     JBT_REG_SLEEP_OUT_FR_B      = 0xc1,
0133     JBT_REG_SLEEP_OUT_FR_C      = 0xc2,
0134     JBT_REG_SLEEP_IN_LCCNT_D    = 0xc3,
0135     JBT_REG_SLEEP_IN_LCCNT_E    = 0xc4,
0136     JBT_REG_SLEEP_IN_LCCNT_F    = 0xc5,
0137     JBT_REG_SLEEP_IN_LCCNT_G    = 0xc6,
0138 
0139     JBT_REG_GAMMA1_FINE_1       = 0xc7,
0140     JBT_REG_GAMMA1_FINE_2       = 0xc8,
0141     JBT_REG_GAMMA1_INCLINATION  = 0xc9,
0142     JBT_REG_GAMMA1_BLUE_OFFSET  = 0xca,
0143 
0144     JBT_REG_BLANK_CONTROL       = 0xcf,
0145     JBT_REG_BLANK_TH_TV     = 0xd0,
0146     JBT_REG_CKV_ON_OFF      = 0xd1,
0147     JBT_REG_CKV_1_2         = 0xd2,
0148     JBT_REG_OEV_TIMING      = 0xd3,
0149     JBT_REG_ASW_TIMING_1        = 0xd4,
0150     JBT_REG_ASW_TIMING_2        = 0xd5,
0151 
0152     JBT_REG_HCLOCK_VGA      = 0xec,
0153     JBT_REG_HCLOCK_QVGA     = 0xed,
0154 };
0155 
0156 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
0157 
0158 static int td028ttec1_panel_connect(struct omap_dss_device *dssdev)
0159 {
0160     struct panel_drv_data *ddata = to_panel_data(dssdev);
0161     struct omap_dss_device *in = ddata->in;
0162     int r;
0163 
0164     if (omapdss_device_is_connected(dssdev))
0165         return 0;
0166 
0167     r = in->ops.dpi->connect(in, dssdev);
0168     if (r)
0169         return r;
0170 
0171     return 0;
0172 }
0173 
0174 static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev)
0175 {
0176     struct panel_drv_data *ddata = to_panel_data(dssdev);
0177     struct omap_dss_device *in = ddata->in;
0178 
0179     if (!omapdss_device_is_connected(dssdev))
0180         return;
0181 
0182     in->ops.dpi->disconnect(in, dssdev);
0183 }
0184 
0185 static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
0186 {
0187     struct panel_drv_data *ddata = to_panel_data(dssdev);
0188     struct omap_dss_device *in = ddata->in;
0189     int r;
0190 
0191     if (!omapdss_device_is_connected(dssdev))
0192         return -ENODEV;
0193 
0194     if (omapdss_device_is_enabled(dssdev))
0195         return 0;
0196 
0197     if (ddata->data_lines)
0198         in->ops.dpi->set_data_lines(in, ddata->data_lines);
0199     in->ops.dpi->set_timings(in, &ddata->videomode);
0200 
0201     r = in->ops.dpi->enable(in);
0202     if (r)
0203         return r;
0204 
0205     dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
0206         dssdev->state);
0207 
0208     /* three times command zero */
0209     r |= jbt_ret_write_0(ddata, 0x00);
0210     usleep_range(1000, 2000);
0211     r |= jbt_ret_write_0(ddata, 0x00);
0212     usleep_range(1000, 2000);
0213     r |= jbt_ret_write_0(ddata, 0x00);
0214     usleep_range(1000, 2000);
0215 
0216     if (r) {
0217         dev_warn(dssdev->dev, "transfer error\n");
0218         goto transfer_err;
0219     }
0220 
0221     /* deep standby out */
0222     r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x17);
0223 
0224     /* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
0225     r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE, 0x80);
0226 
0227     /* Quad mode off */
0228     r |= jbt_reg_write_1(ddata, JBT_REG_QUAD_RATE, 0x00);
0229 
0230     /* AVDD on, XVDD on */
0231     r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x16);
0232 
0233     /* Output control */
0234     r |= jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9);
0235 
0236     /* Sleep mode off */
0237     r |= jbt_ret_write_0(ddata, JBT_REG_SLEEP_OUT);
0238 
0239     /* at this point we have like 50% grey */
0240 
0241     /* initialize register set */
0242     r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE1, 0x01);
0243     r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE2, 0x00);
0244     r |= jbt_reg_write_1(ddata, JBT_REG_RGB_FORMAT, 0x60);
0245     r |= jbt_reg_write_1(ddata, JBT_REG_DRIVE_SYSTEM, 0x10);
0246     r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_OP, 0x56);
0247     r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_MODE, 0x33);
0248     r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
0249     r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
0250     r |= jbt_reg_write_1(ddata, JBT_REG_OPAMP_SYSCLK, 0x02);
0251     r |= jbt_reg_write_1(ddata, JBT_REG_VSC_VOLTAGE, 0x2b);
0252     r |= jbt_reg_write_1(ddata, JBT_REG_VCOM_VOLTAGE, 0x40);
0253     r |= jbt_reg_write_1(ddata, JBT_REG_EXT_DISPL, 0x03);
0254     r |= jbt_reg_write_1(ddata, JBT_REG_DCCLK_DCEV, 0x04);
0255     /*
0256      * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
0257      * to avoid red / blue flicker
0258      */
0259     r |= jbt_reg_write_1(ddata, JBT_REG_ASW_SLEW, 0x04);
0260     r |= jbt_reg_write_1(ddata, JBT_REG_DUMMY_DISPLAY, 0x00);
0261 
0262     r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11);
0263     r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11);
0264     r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11);
0265     r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
0266     r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
0267     r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
0268     r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
0269 
0270     r |= jbt_reg_write_2(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533);
0271     r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_FINE_2, 0x00);
0272     r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00);
0273     r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
0274 
0275     r |= jbt_reg_write_2(ddata, JBT_REG_HCLOCK_VGA, 0x1f0);
0276     r |= jbt_reg_write_1(ddata, JBT_REG_BLANK_CONTROL, 0x02);
0277     r |= jbt_reg_write_2(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
0278 
0279     r |= jbt_reg_write_1(ddata, JBT_REG_CKV_ON_OFF, 0x01);
0280     r |= jbt_reg_write_2(ddata, JBT_REG_CKV_1_2, 0x0000);
0281 
0282     r |= jbt_reg_write_2(ddata, JBT_REG_OEV_TIMING, 0x0d0e);
0283     r |= jbt_reg_write_2(ddata, JBT_REG_ASW_TIMING_1, 0x11a4);
0284     r |= jbt_reg_write_1(ddata, JBT_REG_ASW_TIMING_2, 0x0e);
0285 
0286     r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON);
0287 
0288     dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
0289 
0290 transfer_err:
0291 
0292     return r ? -EIO : 0;
0293 }
0294 
0295 static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
0296 {
0297     struct panel_drv_data *ddata = to_panel_data(dssdev);
0298     struct omap_dss_device *in = ddata->in;
0299 
0300     if (!omapdss_device_is_enabled(dssdev))
0301         return;
0302 
0303     dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
0304 
0305     jbt_ret_write_0(ddata, JBT_REG_DISPLAY_OFF);
0306     jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
0307     jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN);
0308     jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00);
0309 
0310     in->ops.dpi->disable(in);
0311 
0312     dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
0313 }
0314 
0315 static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev,
0316         struct omap_video_timings *timings)
0317 {
0318     struct panel_drv_data *ddata = to_panel_data(dssdev);
0319     struct omap_dss_device *in = ddata->in;
0320 
0321     ddata->videomode = *timings;
0322     dssdev->panel.timings = *timings;
0323 
0324     in->ops.dpi->set_timings(in, timings);
0325 }
0326 
0327 static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
0328         struct omap_video_timings *timings)
0329 {
0330     struct panel_drv_data *ddata = to_panel_data(dssdev);
0331 
0332     *timings = ddata->videomode;
0333 }
0334 
0335 static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev,
0336         struct omap_video_timings *timings)
0337 {
0338     struct panel_drv_data *ddata = to_panel_data(dssdev);
0339     struct omap_dss_device *in = ddata->in;
0340 
0341     return in->ops.dpi->check_timings(in, timings);
0342 }
0343 
0344 static struct omap_dss_driver td028ttec1_ops = {
0345     .connect    = td028ttec1_panel_connect,
0346     .disconnect = td028ttec1_panel_disconnect,
0347 
0348     .enable     = td028ttec1_panel_enable,
0349     .disable    = td028ttec1_panel_disable,
0350 
0351     .set_timings    = td028ttec1_panel_set_timings,
0352     .get_timings    = td028ttec1_panel_get_timings,
0353     .check_timings  = td028ttec1_panel_check_timings,
0354 };
0355 
0356 static int td028ttec1_probe_of(struct spi_device *spi)
0357 {
0358     struct device_node *node = spi->dev.of_node;
0359     struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
0360     struct omap_dss_device *in;
0361 
0362     in = omapdss_of_find_source_for_first_ep(node);
0363     if (IS_ERR(in)) {
0364         dev_err(&spi->dev, "failed to find video source\n");
0365         return PTR_ERR(in);
0366     }
0367 
0368     ddata->in = in;
0369 
0370     return 0;
0371 }
0372 
0373 static int td028ttec1_panel_probe(struct spi_device *spi)
0374 {
0375     struct panel_drv_data *ddata;
0376     struct omap_dss_device *dssdev;
0377     int r;
0378 
0379     dev_dbg(&spi->dev, "%s\n", __func__);
0380 
0381     if (!spi->dev.of_node)
0382         return -ENODEV;
0383 
0384     spi->bits_per_word = 9;
0385     spi->mode = SPI_MODE_3;
0386 
0387     r = spi_setup(spi);
0388     if (r < 0) {
0389         dev_err(&spi->dev, "spi_setup failed: %d\n", r);
0390         return r;
0391     }
0392 
0393     ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
0394     if (ddata == NULL)
0395         return -ENOMEM;
0396 
0397     dev_set_drvdata(&spi->dev, ddata);
0398 
0399     ddata->spi_dev = spi;
0400 
0401     r = td028ttec1_probe_of(spi);
0402     if (r)
0403         return r;
0404 
0405     ddata->videomode = td028ttec1_panel_timings;
0406 
0407     dssdev = &ddata->dssdev;
0408     dssdev->dev = &spi->dev;
0409     dssdev->driver = &td028ttec1_ops;
0410     dssdev->type = OMAP_DISPLAY_TYPE_DPI;
0411     dssdev->owner = THIS_MODULE;
0412     dssdev->panel.timings = ddata->videomode;
0413     dssdev->phy.dpi.data_lines = ddata->data_lines;
0414 
0415     r = omapdss_register_display(dssdev);
0416     if (r) {
0417         dev_err(&spi->dev, "Failed to register panel\n");
0418         goto err_reg;
0419     }
0420 
0421     return 0;
0422 
0423 err_reg:
0424     omap_dss_put_device(ddata->in);
0425     return r;
0426 }
0427 
0428 static void td028ttec1_panel_remove(struct spi_device *spi)
0429 {
0430     struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
0431     struct omap_dss_device *dssdev = &ddata->dssdev;
0432     struct omap_dss_device *in = ddata->in;
0433 
0434     dev_dbg(&ddata->spi_dev->dev, "%s\n", __func__);
0435 
0436     omapdss_unregister_display(dssdev);
0437 
0438     td028ttec1_panel_disable(dssdev);
0439     td028ttec1_panel_disconnect(dssdev);
0440 
0441     omap_dss_put_device(in);
0442 }
0443 
0444 static const struct of_device_id td028ttec1_of_match[] = {
0445     { .compatible = "omapdss,tpo,td028ttec1", },
0446     /* keep to not break older DTB */
0447     { .compatible = "omapdss,toppoly,td028ttec1", },
0448     {},
0449 };
0450 
0451 MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
0452 
0453 static const struct spi_device_id td028ttec1_ids[] = {
0454     { "toppoly,td028ttec1", 0 },
0455     { "tpo,td028ttec1", 0},
0456     { /* sentinel */ }
0457 };
0458 
0459 MODULE_DEVICE_TABLE(spi, td028ttec1_ids);
0460 
0461 static struct spi_driver td028ttec1_spi_driver = {
0462     .probe      = td028ttec1_panel_probe,
0463     .remove     = td028ttec1_panel_remove,
0464     .id_table   = td028ttec1_ids,
0465 
0466     .driver         = {
0467         .name   = "panel-tpo-td028ttec1",
0468         .of_match_table = td028ttec1_of_match,
0469         .suppress_bind_attrs = true,
0470     },
0471 };
0472 
0473 module_spi_driver(td028ttec1_spi_driver);
0474 
0475 MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
0476 MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
0477 MODULE_LICENSE("GPL");