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