1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * FB driver for the S6D1121 LCD Controller
4  *
5  * Copyright (C) 2013 Roman Rolinsky
6  *
7  * Based on fb_ili9325.c by Noralf Tronnes
8  * Based on ili9325.c by Jeroen Domburg
9  * Init code from UTFT library by Henning Karlsen
10  */
11 
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/delay.h>
17 
18 #include "fbtft.h"
19 
20 #define DRVNAME		"fb_s6d1121"
21 #define WIDTH		240
22 #define HEIGHT		320
23 #define BPP		16
24 #define FPS		20
25 #define DEFAULT_GAMMA	"26 09 24 2C 1F 23 24 25 22 26 25 23 0D 00\n" \
26 			"1C 1A 13 1D 0B 11 12 10 13 15 36 19 00 0D"
27 
init_display(struct fbtft_par * par)28 static int init_display(struct fbtft_par *par)
29 {
30 	par->fbtftops.reset(par);
31 
32 	if (par->gpio.cs)
33 		gpiod_set_value(par->gpio.cs, 0);  /* Activate chip */
34 
35 	/* Initialization sequence from Lib_UTFT */
36 
37 	write_reg(par, 0x0011, 0x2004);
38 	write_reg(par, 0x0013, 0xCC00);
39 	write_reg(par, 0x0015, 0x2600);
40 	write_reg(par, 0x0014, 0x252A);
41 	write_reg(par, 0x0012, 0x0033);
42 	write_reg(par, 0x0013, 0xCC04);
43 	write_reg(par, 0x0013, 0xCC06);
44 	write_reg(par, 0x0013, 0xCC4F);
45 	write_reg(par, 0x0013, 0x674F);
46 	write_reg(par, 0x0011, 0x2003);
47 	write_reg(par, 0x0016, 0x0007);
48 	write_reg(par, 0x0002, 0x0013);
49 	write_reg(par, 0x0003, 0x0003);
50 	write_reg(par, 0x0001, 0x0127);
51 	write_reg(par, 0x0008, 0x0303);
52 	write_reg(par, 0x000A, 0x000B);
53 	write_reg(par, 0x000B, 0x0003);
54 	write_reg(par, 0x000C, 0x0000);
55 	write_reg(par, 0x0041, 0x0000);
56 	write_reg(par, 0x0050, 0x0000);
57 	write_reg(par, 0x0060, 0x0005);
58 	write_reg(par, 0x0070, 0x000B);
59 	write_reg(par, 0x0071, 0x0000);
60 	write_reg(par, 0x0078, 0x0000);
61 	write_reg(par, 0x007A, 0x0000);
62 	write_reg(par, 0x0079, 0x0007);
63 	write_reg(par, 0x0007, 0x0051);
64 	write_reg(par, 0x0007, 0x0053);
65 	write_reg(par, 0x0079, 0x0000);
66 
67 	write_reg(par, 0x0022); /* Write Data to GRAM */
68 
69 	return 0;
70 }
71 
set_addr_win(struct fbtft_par * par,int xs,int ys,int xe,int ye)72 static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
73 {
74 	switch (par->info->var.rotate) {
75 	/* R20h = Horizontal GRAM Start Address */
76 	/* R21h = Vertical GRAM Start Address */
77 	case 0:
78 		write_reg(par, 0x0020, xs);
79 		write_reg(par, 0x0021, ys);
80 		break;
81 	case 180:
82 		write_reg(par, 0x0020, WIDTH - 1 - xs);
83 		write_reg(par, 0x0021, HEIGHT - 1 - ys);
84 		break;
85 	case 270:
86 		write_reg(par, 0x0020, WIDTH - 1 - ys);
87 		write_reg(par, 0x0021, xs);
88 		break;
89 	case 90:
90 		write_reg(par, 0x0020, ys);
91 		write_reg(par, 0x0021, HEIGHT - 1 - xs);
92 		break;
93 	}
94 	write_reg(par, 0x0022); /* Write Data to GRAM */
95 }
96 
set_var(struct fbtft_par * par)97 static int set_var(struct fbtft_par *par)
98 {
99 	switch (par->info->var.rotate) {
100 	/* AM: GRAM update direction */
101 	case 0:
102 		write_reg(par, 0x03, 0x0003 | (par->bgr << 12));
103 		break;
104 	case 180:
105 		write_reg(par, 0x03, 0x0000 | (par->bgr << 12));
106 		break;
107 	case 270:
108 		write_reg(par, 0x03, 0x000A | (par->bgr << 12));
109 		break;
110 	case 90:
111 		write_reg(par, 0x03, 0x0009 | (par->bgr << 12));
112 		break;
113 	}
114 
115 	return 0;
116 }
117 
118 /*
119  * Gamma string format:
120  * PKP0 PKP1 PKP2 PKP3 PKP4 PKP5 PKP6 PKP7 PKP8 PKP9 PKP10 PKP11 VRP0 VRP1
121  * PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 PKN6 PKN7 PRN8 PRN9 PRN10 PRN11 VRN0 VRN1
122  */
123 #define CURVE(num, idx)  curves[(num) * par->gamma.num_values + (idx)]
set_gamma(struct fbtft_par * par,u32 * curves)124 static int set_gamma(struct fbtft_par *par, u32 *curves)
125 {
126 	static const unsigned long mask[] = {
127 		0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
128 		0x3f, 0x3f, 0x1f, 0x1f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
129 		0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x1f, 0x1f,
130 	};
131 	int i, j;
132 
133 	/* apply mask */
134 	for (i = 0; i < 2; i++)
135 		for (j = 0; j < 14; j++)
136 			CURVE(i, j) &= mask[i * par->gamma.num_values + j];
137 
138 	write_reg(par, 0x0030, CURVE(0, 1) << 8 | CURVE(0, 0));
139 	write_reg(par, 0x0031, CURVE(0, 3) << 8 | CURVE(0, 2));
140 	write_reg(par, 0x0032, CURVE(0, 5) << 8 | CURVE(0, 3));
141 	write_reg(par, 0x0033, CURVE(0, 7) << 8 | CURVE(0, 6));
142 	write_reg(par, 0x0034, CURVE(0, 9) << 8 | CURVE(0, 8));
143 	write_reg(par, 0x0035, CURVE(0, 11) << 8 | CURVE(0, 10));
144 
145 	write_reg(par, 0x0036, CURVE(1, 1) << 8 | CURVE(1, 0));
146 	write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2));
147 	write_reg(par, 0x0038, CURVE(1, 5) << 8 | CURVE(1, 4));
148 	write_reg(par, 0x0039, CURVE(1, 7) << 8 | CURVE(1, 6));
149 	write_reg(par, 0x003A, CURVE(1, 9) << 8 | CURVE(1, 8));
150 	write_reg(par, 0x003B, CURVE(1, 11) << 8 | CURVE(1, 10));
151 
152 	write_reg(par, 0x003C, CURVE(0, 13) << 8 | CURVE(0, 12));
153 	write_reg(par, 0x003D, CURVE(1, 13) << 8 | CURVE(1, 12));
154 
155 	return 0;
156 }
157 
158 #undef CURVE
159 
160 static struct fbtft_display display = {
161 	.regwidth = 16,
162 	.width = WIDTH,
163 	.height = HEIGHT,
164 	.bpp = BPP,
165 	.fps = FPS,
166 	.gamma_num = 2,
167 	.gamma_len = 14,
168 	.gamma = DEFAULT_GAMMA,
169 	.fbtftops = {
170 		.init_display = init_display,
171 		.set_addr_win = set_addr_win,
172 		.set_var = set_var,
173 		.set_gamma = set_gamma,
174 	},
175 };
176 
177 FBTFT_REGISTER_DRIVER(DRVNAME, "samsung,s6d1121", &display);
178 
179 MODULE_ALIAS("spi:" DRVNAME);
180 MODULE_ALIAS("platform:" DRVNAME);
181 MODULE_ALIAS("spi:s6d1121");
182 MODULE_ALIAS("platform:s6d1121");
183 
184 MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller");
185 MODULE_AUTHOR("Roman Rolinsky");
186 MODULE_LICENSE("GPL");
187