150848e37SDaniel Mack // SPDX-License-Identifier: GPL-2.0+ 250848e37SDaniel Mack 350848e37SDaniel Mack #include <linux/backlight.h> 450848e37SDaniel Mack #include <linux/delay.h> 550848e37SDaniel Mack #include <linux/gpio/consumer.h> 650848e37SDaniel Mack #include <linux/module.h> 750848e37SDaniel Mack #include <linux/property.h> 850848e37SDaniel Mack #include <linux/spi/spi.h> 950848e37SDaniel Mack 1050848e37SDaniel Mack #include <drm/drm_atomic_helper.h> 1150848e37SDaniel Mack #include <drm/drm_drv.h> 12*8ab59da2SThomas Zimmermann #include <drm/drm_fbdev_generic.h> 136eab8224SSam Ravnborg #include <drm/drm_gem_atomic_helper.h> 144a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h> 1550848e37SDaniel Mack #include <drm/drm_mipi_dbi.h> 1650848e37SDaniel Mack #include <drm/drm_modeset_helper.h> 176eab8224SSam Ravnborg 1850848e37SDaniel Mack #include <video/mipi_display.h> 1950848e37SDaniel Mack 2050848e37SDaniel Mack #define ILI9163_FRMCTR1 0xb1 2150848e37SDaniel Mack 2250848e37SDaniel Mack #define ILI9163_PWCTRL1 0xc0 2350848e37SDaniel Mack #define ILI9163_PWCTRL2 0xc1 2450848e37SDaniel Mack #define ILI9163_VMCTRL1 0xc5 2550848e37SDaniel Mack #define ILI9163_VMCTRL2 0xc7 2650848e37SDaniel Mack #define ILI9163_PWCTRLA 0xcb 2750848e37SDaniel Mack #define ILI9163_PWCTRLB 0xcf 2850848e37SDaniel Mack 2950848e37SDaniel Mack #define ILI9163_EN3GAM 0xf2 3050848e37SDaniel Mack 3150848e37SDaniel Mack #define ILI9163_MADCTL_BGR BIT(3) 3250848e37SDaniel Mack #define ILI9163_MADCTL_MV BIT(5) 3350848e37SDaniel Mack #define ILI9163_MADCTL_MX BIT(6) 3450848e37SDaniel Mack #define ILI9163_MADCTL_MY BIT(7) 3550848e37SDaniel Mack 3650848e37SDaniel Mack static void yx240qv29_enable(struct drm_simple_display_pipe *pipe, 3750848e37SDaniel Mack struct drm_crtc_state *crtc_state, 3850848e37SDaniel Mack struct drm_plane_state *plane_state) 3950848e37SDaniel Mack { 4050848e37SDaniel Mack struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 4150848e37SDaniel Mack struct mipi_dbi *dbi = &dbidev->dbi; 4250848e37SDaniel Mack u8 addr_mode; 4350848e37SDaniel Mack int ret, idx; 4450848e37SDaniel Mack 4550848e37SDaniel Mack if (!drm_dev_enter(pipe->crtc.dev, &idx)) 4650848e37SDaniel Mack return; 4750848e37SDaniel Mack 4850848e37SDaniel Mack DRM_DEBUG_KMS("\n"); 4950848e37SDaniel Mack 5050848e37SDaniel Mack ret = mipi_dbi_poweron_conditional_reset(dbidev); 5150848e37SDaniel Mack if (ret < 0) 5250848e37SDaniel Mack goto out_exit; 5350848e37SDaniel Mack if (ret == 1) 5450848e37SDaniel Mack goto out_enable; 5550848e37SDaniel Mack 5650848e37SDaniel Mack /* Gamma */ 5750848e37SDaniel Mack mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x04); 5850848e37SDaniel Mack mipi_dbi_command(dbi, ILI9163_EN3GAM, 0x00); 5950848e37SDaniel Mack 6050848e37SDaniel Mack /* Frame Rate */ 6150848e37SDaniel Mack mipi_dbi_command(dbi, ILI9163_FRMCTR1, 0x0a, 0x14); 6250848e37SDaniel Mack 6350848e37SDaniel Mack /* Power Control */ 6450848e37SDaniel Mack mipi_dbi_command(dbi, ILI9163_PWCTRL1, 0x0a, 0x00); 6550848e37SDaniel Mack mipi_dbi_command(dbi, ILI9163_PWCTRL2, 0x02); 6650848e37SDaniel Mack 6750848e37SDaniel Mack /* VCOM */ 6850848e37SDaniel Mack mipi_dbi_command(dbi, ILI9163_VMCTRL1, 0x2f, 0x3e); 6950848e37SDaniel Mack mipi_dbi_command(dbi, ILI9163_VMCTRL2, 0x40); 7050848e37SDaniel Mack 7150848e37SDaniel Mack /* Memory Access Control */ 7250848e37SDaniel Mack mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); 7350848e37SDaniel Mack 7450848e37SDaniel Mack mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 7550848e37SDaniel Mack msleep(100); 7650848e37SDaniel Mack 7750848e37SDaniel Mack mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 7850848e37SDaniel Mack msleep(100); 7950848e37SDaniel Mack 8050848e37SDaniel Mack out_enable: 8150848e37SDaniel Mack switch (dbidev->rotation) { 8250848e37SDaniel Mack default: 8350848e37SDaniel Mack addr_mode = ILI9163_MADCTL_MX | ILI9163_MADCTL_MY; 8450848e37SDaniel Mack break; 8550848e37SDaniel Mack case 90: 8650848e37SDaniel Mack addr_mode = ILI9163_MADCTL_MX | ILI9163_MADCTL_MV; 8750848e37SDaniel Mack break; 8850848e37SDaniel Mack case 180: 8950848e37SDaniel Mack addr_mode = 0; 9050848e37SDaniel Mack break; 9150848e37SDaniel Mack case 270: 9250848e37SDaniel Mack addr_mode = ILI9163_MADCTL_MY | ILI9163_MADCTL_MV; 9350848e37SDaniel Mack break; 9450848e37SDaniel Mack } 9550848e37SDaniel Mack addr_mode |= ILI9163_MADCTL_BGR; 9650848e37SDaniel Mack mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 9750848e37SDaniel Mack mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 9850848e37SDaniel Mack out_exit: 9950848e37SDaniel Mack drm_dev_exit(idx); 10050848e37SDaniel Mack } 10150848e37SDaniel Mack 10250848e37SDaniel Mack static const struct drm_simple_display_pipe_funcs ili9163_pipe_funcs = { 103216b9bbaSThomas Zimmermann .mode_valid = mipi_dbi_pipe_mode_valid, 10450848e37SDaniel Mack .enable = yx240qv29_enable, 10550848e37SDaniel Mack .disable = mipi_dbi_pipe_disable, 10650848e37SDaniel Mack .update = mipi_dbi_pipe_update, 1076eab8224SSam Ravnborg .prepare_fb = drm_gem_simple_display_pipe_prepare_fb, 10850848e37SDaniel Mack }; 10950848e37SDaniel Mack 11050848e37SDaniel Mack static const struct drm_display_mode yx240qv29_mode = { 11150848e37SDaniel Mack DRM_SIMPLE_MODE(128, 160, 28, 35), 11250848e37SDaniel Mack }; 11350848e37SDaniel Mack 1144a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(ili9163_fops); 11550848e37SDaniel Mack 11650848e37SDaniel Mack static struct drm_driver ili9163_driver = { 11750848e37SDaniel Mack .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 11850848e37SDaniel Mack .fops = &ili9163_fops, 1194a83c26aSDanilo Krummrich DRM_GEM_DMA_DRIVER_OPS_VMAP, 12050848e37SDaniel Mack .debugfs_init = mipi_dbi_debugfs_init, 12150848e37SDaniel Mack .name = "ili9163", 12250848e37SDaniel Mack .desc = "Ilitek ILI9163", 12350848e37SDaniel Mack .date = "20210208", 12450848e37SDaniel Mack .major = 1, 12550848e37SDaniel Mack .minor = 0, 12650848e37SDaniel Mack }; 12750848e37SDaniel Mack 12850848e37SDaniel Mack static const struct of_device_id ili9163_of_match[] = { 12950848e37SDaniel Mack { .compatible = "newhaven,1.8-128160EF" }, 13050848e37SDaniel Mack { } 13150848e37SDaniel Mack }; 13250848e37SDaniel Mack MODULE_DEVICE_TABLE(of, ili9163_of_match); 13350848e37SDaniel Mack 13450848e37SDaniel Mack static const struct spi_device_id ili9163_id[] = { 13550848e37SDaniel Mack { "nhd-1.8-128160EF", 0 }, 13650848e37SDaniel Mack { } 13750848e37SDaniel Mack }; 13850848e37SDaniel Mack MODULE_DEVICE_TABLE(spi, ili9163_id); 13950848e37SDaniel Mack 14050848e37SDaniel Mack static int ili9163_probe(struct spi_device *spi) 14150848e37SDaniel Mack { 14250848e37SDaniel Mack struct device *dev = &spi->dev; 14350848e37SDaniel Mack struct mipi_dbi_dev *dbidev; 14450848e37SDaniel Mack struct drm_device *drm; 14550848e37SDaniel Mack struct mipi_dbi *dbi; 14650848e37SDaniel Mack struct gpio_desc *dc; 14750848e37SDaniel Mack u32 rotation = 0; 14850848e37SDaniel Mack int ret; 14950848e37SDaniel Mack 15050848e37SDaniel Mack dbidev = devm_drm_dev_alloc(dev, &ili9163_driver, 15150848e37SDaniel Mack struct mipi_dbi_dev, drm); 15250848e37SDaniel Mack if (IS_ERR(dbidev)) 15350848e37SDaniel Mack return PTR_ERR(dbidev); 15450848e37SDaniel Mack 15550848e37SDaniel Mack dbi = &dbidev->dbi; 15650848e37SDaniel Mack drm = &dbidev->drm; 15750848e37SDaniel Mack 15850848e37SDaniel Mack spi_set_drvdata(spi, drm); 15950848e37SDaniel Mack 16050848e37SDaniel Mack dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 16150848e37SDaniel Mack if (IS_ERR(dbi->reset)) { 16250848e37SDaniel Mack DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 16350848e37SDaniel Mack return PTR_ERR(dbi->reset); 16450848e37SDaniel Mack } 16550848e37SDaniel Mack 16650848e37SDaniel Mack dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); 16750848e37SDaniel Mack if (IS_ERR(dc)) { 16850848e37SDaniel Mack DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 16950848e37SDaniel Mack return PTR_ERR(dc); 17050848e37SDaniel Mack } 17150848e37SDaniel Mack 17250848e37SDaniel Mack dbidev->backlight = devm_of_find_backlight(dev); 17350848e37SDaniel Mack if (IS_ERR(dbidev->backlight)) 17450848e37SDaniel Mack return PTR_ERR(dbidev->backlight); 17550848e37SDaniel Mack 17650848e37SDaniel Mack device_property_read_u32(dev, "rotation", &rotation); 17750848e37SDaniel Mack 17850848e37SDaniel Mack ret = mipi_dbi_spi_init(spi, dbi, dc); 17950848e37SDaniel Mack if (ret) 18050848e37SDaniel Mack return ret; 18150848e37SDaniel Mack 18250848e37SDaniel Mack ret = mipi_dbi_dev_init(dbidev, &ili9163_pipe_funcs, &yx240qv29_mode, rotation); 18350848e37SDaniel Mack if (ret) 18450848e37SDaniel Mack return ret; 18550848e37SDaniel Mack 18650848e37SDaniel Mack drm_mode_config_reset(drm); 18750848e37SDaniel Mack 18850848e37SDaniel Mack ret = drm_dev_register(drm, 0); 18950848e37SDaniel Mack if (ret) 19050848e37SDaniel Mack return ret; 19150848e37SDaniel Mack 19250848e37SDaniel Mack drm_fbdev_generic_setup(drm, 0); 19350848e37SDaniel Mack 19450848e37SDaniel Mack return 0; 19550848e37SDaniel Mack } 19650848e37SDaniel Mack 197a0386bbaSUwe Kleine-König static void ili9163_remove(struct spi_device *spi) 19850848e37SDaniel Mack { 19950848e37SDaniel Mack struct drm_device *drm = spi_get_drvdata(spi); 20050848e37SDaniel Mack 20150848e37SDaniel Mack drm_dev_unplug(drm); 20250848e37SDaniel Mack drm_atomic_helper_shutdown(drm); 20350848e37SDaniel Mack } 20450848e37SDaniel Mack 20550848e37SDaniel Mack static void ili9163_shutdown(struct spi_device *spi) 20650848e37SDaniel Mack { 20750848e37SDaniel Mack drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 20850848e37SDaniel Mack } 20950848e37SDaniel Mack 21050848e37SDaniel Mack static struct spi_driver ili9163_spi_driver = { 21150848e37SDaniel Mack .driver = { 21250848e37SDaniel Mack .name = "ili9163", 21350848e37SDaniel Mack .of_match_table = ili9163_of_match, 21450848e37SDaniel Mack }, 21550848e37SDaniel Mack .id_table = ili9163_id, 21650848e37SDaniel Mack .probe = ili9163_probe, 21750848e37SDaniel Mack .remove = ili9163_remove, 21850848e37SDaniel Mack .shutdown = ili9163_shutdown, 21950848e37SDaniel Mack }; 22050848e37SDaniel Mack module_spi_driver(ili9163_spi_driver); 22150848e37SDaniel Mack 22250848e37SDaniel Mack MODULE_DESCRIPTION("Ilitek ILI9163 DRM driver"); 22350848e37SDaniel Mack MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>"); 22450848e37SDaniel Mack MODULE_LICENSE("GPL"); 225