1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * FB driver for the HX8357D LCD Controller
4 * Copyright (C) 2015 Adafruit Industries
5 *
6 * Based on the HX8347D FB driver
7 * Copyright (C) 2013 Christian Vogelgsang
8 *
9 * Based on driver code found here: https://github.com/watterott/r61505u-Adapter
10 */
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/delay.h>
16#include <video/mipi_display.h>
17
18#include "fbtft.h"
19#include "fb_hx8357d.h"
20
21#define DRVNAME		"fb_hx8357d"
22#define WIDTH		320
23#define HEIGHT		480
24
25static int init_display(struct fbtft_par *par)
26{
27	par->fbtftops.reset(par);
28
29	/* Reset things like Gamma */
30	write_reg(par, MIPI_DCS_SOFT_RESET);
31	usleep_range(5000, 7000);
32
33	/* setextc */
34	write_reg(par, HX8357D_SETC, 0xFF, 0x83, 0x57);
35	msleep(150);
36
37	/* setRGB which also enables SDO */
38	write_reg(par, HX8357_SETRGB, 0x00, 0x00, 0x06, 0x06);
39
40	/* -1.52V */
41	write_reg(par, HX8357D_SETCOM, 0x25);
42
43	/* Normal mode 70Hz, Idle mode 55 Hz */
44	write_reg(par, HX8357_SETOSC, 0x68);
45
46	/* Set Panel - BGR, Gate direction swapped */
47	write_reg(par, HX8357_SETPANEL, 0x05);
48
49	write_reg(par, HX8357_SETPWR1,
50		  0x00,  /* Not deep standby */
51		  0x15,  /* BT */
52		  0x1C,  /* VSPR */
53		  0x1C,  /* VSNR */
54		  0x83,  /* AP */
55		  0xAA);  /* FS */
56
57	write_reg(par, HX8357D_SETSTBA,
58		  0x50,  /* OPON normal */
59		  0x50,  /* OPON idle */
60		  0x01,  /* STBA */
61		  0x3C,  /* STBA */
62		  0x1E,  /* STBA */
63		  0x08);  /* GEN */
64
65	write_reg(par, HX8357D_SETCYC,
66		  0x02,  /* NW 0x02 */
67		  0x40,  /* RTN */
68		  0x00,  /* DIV */
69		  0x2A,  /* DUM */
70		  0x2A,  /* DUM */
71		  0x0D,  /* GDON */
72		  0x78);  /* GDOFF */
73
74	write_reg(par, HX8357D_SETGAMMA,
75		  0x02,
76		  0x0A,
77		  0x11,
78		  0x1d,
79		  0x23,
80		  0x35,
81		  0x41,
82		  0x4b,
83		  0x4b,
84		  0x42,
85		  0x3A,
86		  0x27,
87		  0x1B,
88		  0x08,
89		  0x09,
90		  0x03,
91		  0x02,
92		  0x0A,
93		  0x11,
94		  0x1d,
95		  0x23,
96		  0x35,
97		  0x41,
98		  0x4b,
99		  0x4b,
100		  0x42,
101		  0x3A,
102		  0x27,
103		  0x1B,
104		  0x08,
105		  0x09,
106		  0x03,
107		  0x00,
108		  0x01);
109
110	/* 16 bit */
111	write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
112
113	write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, 0xC0);
114
115	/* TE off */
116	write_reg(par, MIPI_DCS_SET_TEAR_ON, 0x00);
117
118	/* tear line */
119	write_reg(par, MIPI_DCS_SET_TEAR_SCANLINE, 0x00, 0x02);
120
121	/* Exit Sleep */
122	write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
123	msleep(150);
124
125	/* display on */
126	write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
127	usleep_range(5000, 7000);
128
129	return 0;
130}
131
132static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
133{
134	write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
135		  xs >> 8, xs & 0xff,  /* XSTART */
136		  xe >> 8, xe & 0xff); /* XEND */
137
138	write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
139		  ys >> 8, ys & 0xff,  /* YSTART */
140		  ye >> 8, ye & 0xff); /* YEND */
141
142	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
143}
144
145#define HX8357D_MADCTL_MY  0x80
146#define HX8357D_MADCTL_MX  0x40
147#define HX8357D_MADCTL_MV  0x20
148#define HX8357D_MADCTL_ML  0x10
149#define HX8357D_MADCTL_RGB 0x00
150#define HX8357D_MADCTL_BGR 0x08
151#define HX8357D_MADCTL_MH  0x04
152static int set_var(struct fbtft_par *par)
153{
154	u8 val;
155
156	switch (par->info->var.rotate) {
157	case 270:
158		val = HX8357D_MADCTL_MV | HX8357D_MADCTL_MX;
159		break;
160	case 180:
161		val = 0;
162		break;
163	case 90:
164		val = HX8357D_MADCTL_MV | HX8357D_MADCTL_MY;
165		break;
166	default:
167		val = HX8357D_MADCTL_MX | HX8357D_MADCTL_MY;
168		break;
169	}
170
171	val |= (par->bgr ? HX8357D_MADCTL_RGB : HX8357D_MADCTL_BGR);
172
173	/* Memory Access Control */
174	write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, val);
175
176	return 0;
177}
178
179static struct fbtft_display display = {
180	.regwidth = 8,
181	.width = WIDTH,
182	.height = HEIGHT,
183	.gamma_num = 2,
184	.gamma_len = 14,
185	.fbtftops = {
186		.init_display = init_display,
187		.set_addr_win = set_addr_win,
188		.set_var = set_var,
189	},
190};
191
192FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8357d", &display);
193
194MODULE_ALIAS("spi:" DRVNAME);
195MODULE_ALIAS("platform:" DRVNAME);
196MODULE_ALIAS("spi:hx8357d");
197MODULE_ALIAS("platform:hx8357d");
198
199MODULE_DESCRIPTION("FB driver for the HX8357D LCD Controller");
200MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
201MODULE_LICENSE("GPL");
202