xref: /openbmc/linux/drivers/gpu/drm/tiny/ili9163.c (revision 63aa5ec6)
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