0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/delay.h>
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/module.h>
0011 #include <linux/property.h>
0012 #include <linux/spi/spi.h>
0013 #include <video/mipi_display.h>
0014
0015 #include <drm/drm_atomic_helper.h>
0016 #include <drm/drm_damage_helper.h>
0017 #include <drm/drm_drv.h>
0018 #include <drm/drm_fb_cma_helper.h>
0019 #include <drm/drm_fb_helper.h>
0020 #include <drm/drm_format_helper.h>
0021 #include <drm/drm_framebuffer.h>
0022 #include <drm/drm_gem_atomic_helper.h>
0023 #include <drm/drm_gem_cma_helper.h>
0024 #include <drm/drm_gem_framebuffer_helper.h>
0025 #include <drm/drm_managed.h>
0026 #include <drm/drm_mipi_dbi.h>
0027 #include <drm/drm_rect.h>
0028
0029
0030 #define ST7586_DISP_MODE_GRAY 0x38
0031 #define ST7586_DISP_MODE_MONO 0x39
0032 #define ST7586_ENABLE_DDRAM 0x3a
0033 #define ST7586_SET_DISP_DUTY 0xb0
0034 #define ST7586_SET_PART_DISP 0xb4
0035 #define ST7586_SET_NLINE_INV 0xb5
0036 #define ST7586_SET_VOP 0xc0
0037 #define ST7586_SET_BIAS_SYSTEM 0xc3
0038 #define ST7586_SET_BOOST_LEVEL 0xc4
0039 #define ST7586_SET_VOP_OFFSET 0xc7
0040 #define ST7586_ENABLE_ANALOG 0xd0
0041 #define ST7586_AUTO_READ_CTRL 0xd7
0042 #define ST7586_OTP_RW_CTRL 0xe0
0043 #define ST7586_OTP_CTRL_OUT 0xe1
0044 #define ST7586_OTP_READ 0xe3
0045
0046 #define ST7586_DISP_CTRL_MX BIT(6)
0047 #define ST7586_DISP_CTRL_MY BIT(7)
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063 static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 };
0064
0065 static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
0066 struct drm_framebuffer *fb,
0067 struct drm_rect *clip)
0068 {
0069 size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1);
0070 unsigned int x, y;
0071 u8 *src, *buf, val;
0072
0073 buf = kmalloc(len, GFP_KERNEL);
0074 if (!buf)
0075 return;
0076
0077 drm_fb_xrgb8888_to_gray8(buf, 0, vaddr, fb, clip);
0078 src = buf;
0079
0080 for (y = clip->y1; y < clip->y2; y++) {
0081 for (x = clip->x1; x < clip->x2; x += 3) {
0082 val = st7586_lookup[*src++ >> 6] << 5;
0083 val |= st7586_lookup[*src++ >> 6] << 2;
0084 val |= st7586_lookup[*src++ >> 6] >> 1;
0085 *dst++ = val;
0086 }
0087 }
0088
0089 kfree(buf);
0090 }
0091
0092 static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
0093 struct drm_rect *clip)
0094 {
0095 struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
0096 void *src = cma_obj->vaddr;
0097 int ret = 0;
0098
0099 ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
0100 if (ret)
0101 return ret;
0102
0103 st7586_xrgb8888_to_gray332(dst, src, fb, clip);
0104
0105 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
0106
0107 return 0;
0108 }
0109
0110 static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
0111 {
0112 struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
0113 struct mipi_dbi *dbi = &dbidev->dbi;
0114 int start, end, idx, ret = 0;
0115
0116 if (!drm_dev_enter(fb->dev, &idx))
0117 return;
0118
0119
0120 rect->x1 = rounddown(rect->x1, 3);
0121 rect->x2 = roundup(rect->x2, 3);
0122
0123 DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
0124
0125 ret = st7586_buf_copy(dbidev->tx_buf, fb, rect);
0126 if (ret)
0127 goto err_msg;
0128
0129
0130 start = rect->x1 / 3;
0131 end = rect->x2 / 3;
0132
0133 mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS,
0134 (start >> 8) & 0xFF, start & 0xFF,
0135 (end >> 8) & 0xFF, (end - 1) & 0xFF);
0136 mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS,
0137 (rect->y1 >> 8) & 0xFF, rect->y1 & 0xFF,
0138 (rect->y2 >> 8) & 0xFF, (rect->y2 - 1) & 0xFF);
0139
0140 ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START,
0141 (u8 *)dbidev->tx_buf,
0142 (end - start) * (rect->y2 - rect->y1));
0143 err_msg:
0144 if (ret)
0145 dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
0146
0147 drm_dev_exit(idx);
0148 }
0149
0150 static void st7586_pipe_update(struct drm_simple_display_pipe *pipe,
0151 struct drm_plane_state *old_state)
0152 {
0153 struct drm_plane_state *state = pipe->plane.state;
0154 struct drm_rect rect;
0155
0156 if (!pipe->crtc.state->active)
0157 return;
0158
0159 if (drm_atomic_helper_damage_merged(old_state, state, &rect))
0160 st7586_fb_dirty(state->fb, &rect);
0161 }
0162
0163 static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
0164 struct drm_crtc_state *crtc_state,
0165 struct drm_plane_state *plane_state)
0166 {
0167 struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
0168 struct drm_framebuffer *fb = plane_state->fb;
0169 struct mipi_dbi *dbi = &dbidev->dbi;
0170 struct drm_rect rect = {
0171 .x1 = 0,
0172 .x2 = fb->width,
0173 .y1 = 0,
0174 .y2 = fb->height,
0175 };
0176 int idx, ret;
0177 u8 addr_mode;
0178
0179 if (!drm_dev_enter(pipe->crtc.dev, &idx))
0180 return;
0181
0182 DRM_DEBUG_KMS("\n");
0183
0184 ret = mipi_dbi_poweron_reset(dbidev);
0185 if (ret)
0186 goto out_exit;
0187
0188 mipi_dbi_command(dbi, ST7586_AUTO_READ_CTRL, 0x9f);
0189 mipi_dbi_command(dbi, ST7586_OTP_RW_CTRL, 0x00);
0190
0191 msleep(10);
0192
0193 mipi_dbi_command(dbi, ST7586_OTP_READ);
0194
0195 msleep(20);
0196
0197 mipi_dbi_command(dbi, ST7586_OTP_CTRL_OUT);
0198 mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
0199 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
0200
0201 msleep(50);
0202
0203 mipi_dbi_command(dbi, ST7586_SET_VOP_OFFSET, 0x00);
0204 mipi_dbi_command(dbi, ST7586_SET_VOP, 0xe3, 0x00);
0205 mipi_dbi_command(dbi, ST7586_SET_BIAS_SYSTEM, 0x02);
0206 mipi_dbi_command(dbi, ST7586_SET_BOOST_LEVEL, 0x04);
0207 mipi_dbi_command(dbi, ST7586_ENABLE_ANALOG, 0x1d);
0208 mipi_dbi_command(dbi, ST7586_SET_NLINE_INV, 0x00);
0209 mipi_dbi_command(dbi, ST7586_DISP_MODE_GRAY);
0210 mipi_dbi_command(dbi, ST7586_ENABLE_DDRAM, 0x02);
0211
0212 switch (dbidev->rotation) {
0213 default:
0214 addr_mode = 0x00;
0215 break;
0216 case 90:
0217 addr_mode = ST7586_DISP_CTRL_MY;
0218 break;
0219 case 180:
0220 addr_mode = ST7586_DISP_CTRL_MX | ST7586_DISP_CTRL_MY;
0221 break;
0222 case 270:
0223 addr_mode = ST7586_DISP_CTRL_MX;
0224 break;
0225 }
0226 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
0227
0228 mipi_dbi_command(dbi, ST7586_SET_DISP_DUTY, 0x7f);
0229 mipi_dbi_command(dbi, ST7586_SET_PART_DISP, 0xa0);
0230 mipi_dbi_command(dbi, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x00, 0x77);
0231 mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE);
0232
0233 msleep(100);
0234
0235 st7586_fb_dirty(fb, &rect);
0236
0237 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
0238 out_exit:
0239 drm_dev_exit(idx);
0240 }
0241
0242 static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
0243 {
0244 struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
0245
0246
0247
0248
0249
0250
0251
0252
0253 DRM_DEBUG_KMS("\n");
0254
0255 mipi_dbi_command(&dbidev->dbi, MIPI_DCS_SET_DISPLAY_OFF);
0256 }
0257
0258 static const u32 st7586_formats[] = {
0259 DRM_FORMAT_XRGB8888,
0260 };
0261
0262 static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
0263 .enable = st7586_pipe_enable,
0264 .disable = st7586_pipe_disable,
0265 .update = st7586_pipe_update,
0266 };
0267
0268 static const struct drm_display_mode st7586_mode = {
0269 DRM_SIMPLE_MODE(178, 128, 37, 27),
0270 };
0271
0272 DEFINE_DRM_GEM_CMA_FOPS(st7586_fops);
0273
0274 static const struct drm_driver st7586_driver = {
0275 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
0276 .fops = &st7586_fops,
0277 DRM_GEM_CMA_DRIVER_OPS_VMAP,
0278 .debugfs_init = mipi_dbi_debugfs_init,
0279 .name = "st7586",
0280 .desc = "Sitronix ST7586",
0281 .date = "20170801",
0282 .major = 1,
0283 .minor = 0,
0284 };
0285
0286 static const struct of_device_id st7586_of_match[] = {
0287 { .compatible = "lego,ev3-lcd" },
0288 {},
0289 };
0290 MODULE_DEVICE_TABLE(of, st7586_of_match);
0291
0292 static const struct spi_device_id st7586_id[] = {
0293 { "ev3-lcd", 0 },
0294 { },
0295 };
0296 MODULE_DEVICE_TABLE(spi, st7586_id);
0297
0298 static int st7586_probe(struct spi_device *spi)
0299 {
0300 struct device *dev = &spi->dev;
0301 struct mipi_dbi_dev *dbidev;
0302 struct drm_device *drm;
0303 struct mipi_dbi *dbi;
0304 struct gpio_desc *a0;
0305 u32 rotation = 0;
0306 size_t bufsize;
0307 int ret;
0308
0309 dbidev = devm_drm_dev_alloc(dev, &st7586_driver,
0310 struct mipi_dbi_dev, drm);
0311 if (IS_ERR(dbidev))
0312 return PTR_ERR(dbidev);
0313
0314 dbi = &dbidev->dbi;
0315 drm = &dbidev->drm;
0316
0317 bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay;
0318
0319 dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
0320 if (IS_ERR(dbi->reset))
0321 return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
0322
0323 a0 = devm_gpiod_get(dev, "a0", GPIOD_OUT_LOW);
0324 if (IS_ERR(a0))
0325 return dev_err_probe(dev, PTR_ERR(a0), "Failed to get GPIO 'a0'\n");
0326
0327 device_property_read_u32(dev, "rotation", &rotation);
0328
0329 ret = mipi_dbi_spi_init(spi, dbi, a0);
0330 if (ret)
0331 return ret;
0332
0333
0334 dbi->read_commands = NULL;
0335
0336 ret = mipi_dbi_dev_init_with_formats(dbidev, &st7586_pipe_funcs,
0337 st7586_formats, ARRAY_SIZE(st7586_formats),
0338 &st7586_mode, rotation, bufsize);
0339 if (ret)
0340 return ret;
0341
0342
0343
0344
0345
0346
0347
0348
0349 dbi->swap_bytes = true;
0350
0351 drm_mode_config_reset(drm);
0352
0353 ret = drm_dev_register(drm, 0);
0354 if (ret)
0355 return ret;
0356
0357 spi_set_drvdata(spi, drm);
0358
0359 drm_fbdev_generic_setup(drm, 0);
0360
0361 return 0;
0362 }
0363
0364 static void st7586_remove(struct spi_device *spi)
0365 {
0366 struct drm_device *drm = spi_get_drvdata(spi);
0367
0368 drm_dev_unplug(drm);
0369 drm_atomic_helper_shutdown(drm);
0370 }
0371
0372 static void st7586_shutdown(struct spi_device *spi)
0373 {
0374 drm_atomic_helper_shutdown(spi_get_drvdata(spi));
0375 }
0376
0377 static struct spi_driver st7586_spi_driver = {
0378 .driver = {
0379 .name = "st7586",
0380 .owner = THIS_MODULE,
0381 .of_match_table = st7586_of_match,
0382 },
0383 .id_table = st7586_id,
0384 .probe = st7586_probe,
0385 .remove = st7586_remove,
0386 .shutdown = st7586_shutdown,
0387 };
0388 module_spi_driver(st7586_spi_driver);
0389
0390 MODULE_DESCRIPTION("Sitronix ST7586 DRM driver");
0391 MODULE_AUTHOR("David Lechner <david@lechnology.com>");
0392 MODULE_LICENSE("GPL");