video: eliminate unused drivers/video/mb862xx.c
[platform/kernel/u-boot.git] / drivers / video / ihs_video_out.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2017
4  * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
5  *
6  * based on the gdsys osd driver, which is
7  *
8  * (C) Copyright 2010
9  * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.de
10  */
11
12 #include <common.h>
13 #include <display.h>
14 #include <dm.h>
15 #include <log.h>
16 #include <regmap.h>
17 #include <video_osd.h>
18 #include <asm/gpio.h>
19
20 static const uint MAX_X_CHARS = 53;
21 static const uint MAX_Y_CHARS = 26;
22 static const uint MAX_VIDEOMEM_WIDTH = 64;
23 static const uint MAX_VIDEOMEM_HEIGHT = 32;
24 static const uint CHAR_WIDTH = 12;
25 static const uint CHAR_HEIGHT = 18;
26
27 static const u16 BASE_WIDTH_MASK = 0x3f00;
28 static const uint BASE_WIDTH_SHIFT = 8;
29 static const u16 BASE_HEIGTH_MASK = 0x001f;
30 static const uint BASE_HEIGTH_SHIFT;
31
32 struct ihs_video_out_regs {
33         /* Device version register */
34         u16 versions;
35         /* Device feature register */
36         u16 features;
37         /* Device control register */
38         u16 control;
39         /* Register controlling screen size */
40         u16 xy_size;
41         /* Register controlling screen scaling */
42         u16 xy_scale;
43         /* Register controlling screen x position */
44         u16 x_pos;
45         /* Register controlling screen y position */
46         u16 y_pos;
47 };
48
49 #define ihs_video_out_set(map, member, val) \
50         regmap_range_set(map, 1, struct ihs_video_out_regs, member, val)
51
52 #define ihs_video_out_get(map, member, valp) \
53         regmap_range_get(map, 1, struct ihs_video_out_regs, member, valp)
54
55 enum {
56         CONTROL_FILTER_BLACK = (0 << 0),
57         CONTROL_FILTER_ORIGINAL = (1 << 0),
58         CONTROL_FILTER_DARKER = (2 << 0),
59         CONTROL_FILTER_GRAY = (3 << 0),
60
61         CONTROL_MODE_PASSTHROUGH = (0 << 3),
62         CONTROL_MODE_OSD = (1 << 3),
63         CONTROL_MODE_AUTO = (2 << 3),
64         CONTROL_MODE_OFF = (3 << 3),
65
66         CONTROL_ENABLE_OFF = (0 << 6),
67         CONTROL_ENABLE_ON = (1 << 6),
68 };
69
70 struct ihs_video_out_priv {
71         /* Register map for OSD device */
72         struct regmap *map;
73         /* Pointer to video memory */
74         u16 *vidmem;
75         /* Display width in text columns */
76         uint base_width;
77         /* Display height in text rows */
78         uint base_height;
79         /* x-resolution of the display in pixels */
80         uint res_x;
81         /* y-resolution of the display in pixels */
82         uint res_y;
83         /* OSD's sync mode (resolution + frequency) */
84         int sync_src;
85         /* The display port output for this OSD */
86         struct udevice *video_tx;
87         /* The pixel clock generator for the display */
88         struct udevice *clk_gen;
89 };
90
91 static const struct udevice_id ihs_video_out_ids[] = {
92         { .compatible = "gdsys,ihs_video_out" },
93         { }
94 };
95
96 /**
97  * set_control() - Set the control register to a given value
98  *
99  * The current value of sync_src is preserved by the function automatically.
100  *
101  * @dev: the OSD device whose control register to set
102  * @value: the 16-bit value to write to the control register
103  * Return: 0
104  */
105 static int set_control(struct udevice *dev, u16 value)
106 {
107         struct ihs_video_out_priv *priv = dev_get_priv(dev);
108
109         if (priv->sync_src)
110                 value |= ((priv->sync_src & 0x7) << 8);
111
112         ihs_video_out_set(priv->map, control, value);
113
114         return 0;
115 }
116
117 int ihs_video_out_get_info(struct udevice *dev, struct video_osd_info *info)
118 {
119         struct ihs_video_out_priv *priv = dev_get_priv(dev);
120         u16 versions;
121
122         ihs_video_out_get(priv->map, versions, &versions);
123
124         info->width = priv->base_width;
125         info->height = priv->base_height;
126         info->major_version = versions / 100;
127         info->minor_version = versions % 100;
128
129         return 0;
130 }
131
132 int ihs_video_out_set_mem(struct udevice *dev, uint col, uint row, u8 *buf,
133                           size_t buflen, uint count)
134 {
135         struct ihs_video_out_priv *priv = dev_get_priv(dev);
136         int res;
137         uint offset;
138         uint k, rep;
139         u16 data;
140
141         /* Repetitions (controlled via count parmeter) */
142         for (rep = 0; rep < count; ++rep) {
143                 offset = row * priv->base_width + col + rep * (buflen / 2);
144
145                 /* Write a single buffer copy */
146                 for (k = 0; k < buflen / 2; ++k) {
147                         uint max_size = priv->base_width * priv->base_height;
148
149                         if (offset + k >= max_size) {
150                                 debug("%s: Write would be out of OSD bounds\n",
151                                       dev->name);
152                                 return -E2BIG;
153                         }
154
155                         data = buf[2 * k + 1] + 256 * buf[2 * k];
156                         out_le16(priv->vidmem + offset + k, data);
157                 }
158         }
159
160         res = set_control(dev, CONTROL_FILTER_ORIGINAL |
161                                CONTROL_MODE_OSD |
162                                CONTROL_ENABLE_ON);
163         if (res) {
164                 debug("%s: Could not set control register\n", dev->name);
165                 return res;
166         }
167
168         return 0;
169 }
170
171 /**
172  * div2_u16() - Approximately divide a 16-bit number by 2
173  *
174  * @val: The 16-bit value to divide by two
175  * Return: The approximate division of val by two
176  */
177 static inline u16 div2_u16(u16 val)
178 {
179         return (32767 * val) / 65535;
180 }
181
182 int ihs_video_out_set_size(struct udevice *dev, uint col, uint row)
183 {
184         struct ihs_video_out_priv *priv = dev_get_priv(dev);
185
186         if (!col || col > MAX_VIDEOMEM_WIDTH || col > MAX_X_CHARS ||
187             !row || row > MAX_VIDEOMEM_HEIGHT || row > MAX_Y_CHARS) {
188                 debug("%s: Desired OSD size invalid\n", dev->name);
189                 return -EINVAL;
190         }
191
192         ihs_video_out_set(priv->map, xy_size, ((col - 1) << 8) | (row - 1));
193         /* Center OSD on screen */
194         ihs_video_out_set(priv->map, x_pos,
195                           div2_u16(priv->res_x - CHAR_WIDTH * col));
196         ihs_video_out_set(priv->map, y_pos,
197                           div2_u16(priv->res_y - CHAR_HEIGHT * row));
198
199         return 0;
200 }
201
202 int ihs_video_out_print(struct udevice *dev, uint col, uint row, ulong color,
203                         char *text)
204 {
205         int res;
206         u8 buffer[2 * MAX_VIDEOMEM_WIDTH];
207         uint k;
208         uint charcount = strlen(text);
209         uint len = min(charcount, 2 * MAX_VIDEOMEM_WIDTH);
210
211         for (k = 0; k < len; ++k) {
212                 buffer[2 * k] = text[k];
213                 buffer[2 * k + 1] = color;
214         }
215
216         res = ihs_video_out_set_mem(dev, col, row, buffer, 2 * len, 1);
217         if (res < 0) {
218                 debug("%s: Could not write to video memory\n", dev->name);
219                 return res;
220         }
221
222         return 0;
223 }
224
225 static const struct video_osd_ops ihs_video_out_ops = {
226         .get_info = ihs_video_out_get_info,
227         .set_mem = ihs_video_out_set_mem,
228         .set_size = ihs_video_out_set_size,
229         .print = ihs_video_out_print,
230 };
231
232 int ihs_video_out_probe(struct udevice *dev)
233 {
234         struct ihs_video_out_priv *priv = dev_get_priv(dev);
235         struct ofnode_phandle_args phandle_args;
236         const char *mode;
237         u16 features;
238         struct display_timing timing;
239         int res;
240
241         res = regmap_init_mem(dev_ofnode(dev), &priv->map);
242         if (res) {
243                 debug("%s: Could not initialize regmap (err = %d)\n", dev->name,
244                       res);
245                 return res;
246         }
247
248         /* Range with index 2 is video memory */
249         priv->vidmem = regmap_get_range(priv->map, 2);
250
251         mode = dev_read_string(dev, "mode");
252         if (!mode) {
253                 debug("%s: Could not read mode property\n", dev->name);
254                 return -EINVAL;
255         }
256
257         if (!strcmp(mode, "1024_768_60")) {
258                 priv->sync_src = 2;
259                 priv->res_x = 1024;
260                 priv->res_y = 768;
261                 timing.hactive.typ = 1024;
262                 timing.vactive.typ = 768;
263         } else if (!strcmp(mode, "720_400_70")) {
264                 priv->sync_src = 1;
265                 priv->res_x = 720;
266                 priv->res_y = 400;
267                 timing.hactive.typ = 720;
268                 timing.vactive.typ = 400;
269         } else {
270                 priv->sync_src = 0;
271                 priv->res_x = 640;
272                 priv->res_y = 480;
273                 timing.hactive.typ = 640;
274                 timing.vactive.typ = 480;
275         }
276
277         ihs_video_out_get(priv->map, features, &features);
278
279         res = set_control(dev, CONTROL_FILTER_ORIGINAL |
280                                CONTROL_MODE_OSD |
281                                CONTROL_ENABLE_OFF);
282         if (res) {
283                 debug("%s: Could not set control register (err = %d)\n",
284                       dev->name, res);
285                 return res;
286         }
287
288         priv->base_width = ((features & BASE_WIDTH_MASK)
289                             >> BASE_WIDTH_SHIFT) + 1;
290         priv->base_height = ((features & BASE_HEIGTH_MASK)
291                              >> BASE_HEIGTH_SHIFT) + 1;
292
293         res = dev_read_phandle_with_args(dev, "clk_gen", NULL, 0, 0,
294                                          &phandle_args);
295         if (res) {
296                 debug("%s: Could not get clk_gen node (err = %d)\n",
297                       dev->name, res);
298                 return -EINVAL;
299         }
300
301         res = uclass_get_device_by_ofnode(UCLASS_CLK, phandle_args.node,
302                                           &priv->clk_gen);
303         if (res) {
304                 debug("%s: Could not get clk_gen dev (err = %d)\n",
305                       dev->name, res);
306                 return -EINVAL;
307         }
308
309         res = dev_read_phandle_with_args(dev, "video_tx", NULL, 0, 0,
310                                          &phandle_args);
311         if (res) {
312                 debug("%s: Could not get video_tx (err = %d)\n",
313                       dev->name, res);
314                 return -EINVAL;
315         }
316
317         res = uclass_get_device_by_ofnode(UCLASS_DISPLAY, phandle_args.node,
318                                           &priv->video_tx);
319         if (res) {
320                 debug("%s: Could not get video_tx dev (err = %d)\n",
321                       dev->name, res);
322                 return -EINVAL;
323         }
324
325         res = display_enable(priv->video_tx, 8, &timing);
326         if (res && res != -EIO) { /* Ignore missing DP sink error */
327                 debug("%s: Could not enable the display (err = %d)\n",
328                       dev->name, res);
329                 return res;
330         }
331
332         return 0;
333 }
334
335 U_BOOT_DRIVER(ihs_video_out_drv) = {
336         .name           = "ihs_video_out_drv",
337         .id             = UCLASS_VIDEO_OSD,
338         .ops            = &ihs_video_out_ops,
339         .of_match       = ihs_video_out_ids,
340         .probe          = ihs_video_out_probe,
341         .priv_auto      = sizeof(struct ihs_video_out_priv),
342 };