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> 1250848e37SDaniel Mack #include <drm/drm_fb_helper.h> 136eab8224SSam Ravnborg #include <drm/drm_gem_atomic_helper.h> 1450848e37SDaniel Mack #include <drm/drm_gem_cma_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 = { 10350848e37SDaniel Mack .enable = yx240qv29_enable, 10450848e37SDaniel Mack .disable = mipi_dbi_pipe_disable, 10550848e37SDaniel Mack .update = mipi_dbi_pipe_update, 1066eab8224SSam Ravnborg .prepare_fb = drm_gem_simple_display_pipe_prepare_fb, 10750848e37SDaniel Mack }; 10850848e37SDaniel Mack 10950848e37SDaniel Mack static const struct drm_display_mode yx240qv29_mode = { 11050848e37SDaniel Mack DRM_SIMPLE_MODE(128, 160, 28, 35), 11150848e37SDaniel Mack }; 11250848e37SDaniel Mack 11350848e37SDaniel Mack DEFINE_DRM_GEM_CMA_FOPS(ili9163_fops); 11450848e37SDaniel Mack 11550848e37SDaniel Mack static struct drm_driver ili9163_driver = { 11650848e37SDaniel Mack .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 11750848e37SDaniel Mack .fops = &ili9163_fops, 11850848e37SDaniel Mack DRM_GEM_CMA_DRIVER_OPS_VMAP, 11950848e37SDaniel Mack .debugfs_init = mipi_dbi_debugfs_init, 12050848e37SDaniel Mack .name = "ili9163", 12150848e37SDaniel Mack .desc = "Ilitek ILI9163", 12250848e37SDaniel Mack .date = "20210208", 12350848e37SDaniel Mack .major = 1, 12450848e37SDaniel Mack .minor = 0, 12550848e37SDaniel Mack }; 12650848e37SDaniel Mack 12750848e37SDaniel Mack static const struct of_device_id ili9163_of_match[] = { 12850848e37SDaniel Mack { .compatible = "newhaven,1.8-128160EF" }, 12950848e37SDaniel Mack { } 13050848e37SDaniel Mack }; 13150848e37SDaniel Mack MODULE_DEVICE_TABLE(of, ili9163_of_match); 13250848e37SDaniel Mack 13350848e37SDaniel Mack static const struct spi_device_id ili9163_id[] = { 13450848e37SDaniel Mack { "nhd-1.8-128160EF", 0 }, 13550848e37SDaniel Mack { } 13650848e37SDaniel Mack }; 13750848e37SDaniel Mack MODULE_DEVICE_TABLE(spi, ili9163_id); 13850848e37SDaniel Mack 13950848e37SDaniel Mack static int ili9163_probe(struct spi_device *spi) 14050848e37SDaniel Mack { 14150848e37SDaniel Mack struct device *dev = &spi->dev; 14250848e37SDaniel Mack struct mipi_dbi_dev *dbidev; 14350848e37SDaniel Mack struct drm_device *drm; 14450848e37SDaniel Mack struct mipi_dbi *dbi; 14550848e37SDaniel Mack struct gpio_desc *dc; 14650848e37SDaniel Mack u32 rotation = 0; 14750848e37SDaniel Mack int ret; 14850848e37SDaniel Mack 14950848e37SDaniel Mack dbidev = devm_drm_dev_alloc(dev, &ili9163_driver, 15050848e37SDaniel Mack struct mipi_dbi_dev, drm); 15150848e37SDaniel Mack if (IS_ERR(dbidev)) 15250848e37SDaniel Mack return PTR_ERR(dbidev); 15350848e37SDaniel Mack 15450848e37SDaniel Mack dbi = &dbidev->dbi; 15550848e37SDaniel Mack drm = &dbidev->drm; 15650848e37SDaniel Mack 15750848e37SDaniel Mack spi_set_drvdata(spi, drm); 15850848e37SDaniel Mack 15950848e37SDaniel Mack dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 16050848e37SDaniel Mack if (IS_ERR(dbi->reset)) { 16150848e37SDaniel Mack DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 16250848e37SDaniel Mack return PTR_ERR(dbi->reset); 16350848e37SDaniel Mack } 16450848e37SDaniel Mack 16550848e37SDaniel Mack dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); 16650848e37SDaniel Mack if (IS_ERR(dc)) { 16750848e37SDaniel Mack DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 16850848e37SDaniel Mack return PTR_ERR(dc); 16950848e37SDaniel Mack } 17050848e37SDaniel Mack 17150848e37SDaniel Mack dbidev->backlight = devm_of_find_backlight(dev); 17250848e37SDaniel Mack if (IS_ERR(dbidev->backlight)) 17350848e37SDaniel Mack return PTR_ERR(dbidev->backlight); 17450848e37SDaniel Mack 17550848e37SDaniel Mack device_property_read_u32(dev, "rotation", &rotation); 17650848e37SDaniel Mack 17750848e37SDaniel Mack ret = mipi_dbi_spi_init(spi, dbi, dc); 17850848e37SDaniel Mack if (ret) 17950848e37SDaniel Mack return ret; 18050848e37SDaniel Mack 18150848e37SDaniel Mack ret = mipi_dbi_dev_init(dbidev, &ili9163_pipe_funcs, &yx240qv29_mode, rotation); 18250848e37SDaniel Mack if (ret) 18350848e37SDaniel Mack return ret; 18450848e37SDaniel Mack 18550848e37SDaniel Mack drm_mode_config_reset(drm); 18650848e37SDaniel Mack 18750848e37SDaniel Mack ret = drm_dev_register(drm, 0); 18850848e37SDaniel Mack if (ret) 18950848e37SDaniel Mack return ret; 19050848e37SDaniel Mack 19150848e37SDaniel Mack drm_fbdev_generic_setup(drm, 0); 19250848e37SDaniel Mack 19350848e37SDaniel Mack return 0; 19450848e37SDaniel Mack } 19550848e37SDaniel Mack 196*a0386bbaSUwe Kleine-König static void ili9163_remove(struct spi_device *spi) 19750848e37SDaniel Mack { 19850848e37SDaniel Mack struct drm_device *drm = spi_get_drvdata(spi); 19950848e37SDaniel Mack 20050848e37SDaniel Mack drm_dev_unplug(drm); 20150848e37SDaniel Mack drm_atomic_helper_shutdown(drm); 20250848e37SDaniel Mack } 20350848e37SDaniel Mack 20450848e37SDaniel Mack static void ili9163_shutdown(struct spi_device *spi) 20550848e37SDaniel Mack { 20650848e37SDaniel Mack drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 20750848e37SDaniel Mack } 20850848e37SDaniel Mack 20950848e37SDaniel Mack static struct spi_driver ili9163_spi_driver = { 21050848e37SDaniel Mack .driver = { 21150848e37SDaniel Mack .name = "ili9163", 21250848e37SDaniel Mack .of_match_table = ili9163_of_match, 21350848e37SDaniel Mack }, 21450848e37SDaniel Mack .id_table = ili9163_id, 21550848e37SDaniel Mack .probe = ili9163_probe, 21650848e37SDaniel Mack .remove = ili9163_remove, 21750848e37SDaniel Mack .shutdown = ili9163_shutdown, 21850848e37SDaniel Mack }; 21950848e37SDaniel Mack module_spi_driver(ili9163_spi_driver); 22050848e37SDaniel Mack 22150848e37SDaniel Mack MODULE_DESCRIPTION("Ilitek ILI9163 DRM driver"); 22250848e37SDaniel Mack MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>"); 22350848e37SDaniel Mack MODULE_LICENSE("GPL"); 224