1 // SPDX-License-Identifier: GPL-2.0+
6 * based on the gdsys osd driver, which is
17 #include <video_osd.h>
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;
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;
32 struct ihs_video_out_regs {
33 /* Device version register */
35 /* Device feature register */
37 /* Device control register */
39 /* Register controlling screen size */
41 /* Register controlling screen scaling */
43 /* Register controlling screen x position */
45 /* Register controlling screen y position */
49 #define ihs_video_out_set(map, member, val) \
50 regmap_range_set(map, 1, struct ihs_video_out_regs, member, val)
52 #define ihs_video_out_get(map, member, valp) \
53 regmap_range_get(map, 1, struct ihs_video_out_regs, member, valp)
56 CONTROL_FILTER_BLACK = (0 << 0),
57 CONTROL_FILTER_ORIGINAL = (1 << 0),
58 CONTROL_FILTER_DARKER = (2 << 0),
59 CONTROL_FILTER_GRAY = (3 << 0),
61 CONTROL_MODE_PASSTHROUGH = (0 << 3),
62 CONTROL_MODE_OSD = (1 << 3),
63 CONTROL_MODE_AUTO = (2 << 3),
64 CONTROL_MODE_OFF = (3 << 3),
66 CONTROL_ENABLE_OFF = (0 << 6),
67 CONTROL_ENABLE_ON = (1 << 6),
70 struct ihs_video_out_priv {
71 /* Register map for OSD device */
73 /* Pointer to video memory */
75 /* Display width in text columns */
77 /* Display height in text rows */
79 /* x-resolution of the display in pixels */
81 /* y-resolution of the display in pixels */
83 /* OSD's sync mode (resolution + frequency) */
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;
91 static const struct udevice_id ihs_video_out_ids[] = {
92 { .compatible = "gdsys,ihs_video_out" },
97 * set_control() - Set the control register to a given value
99 * The current value of sync_src is preserved by the function automatically.
101 * @dev: the OSD device whose control register to set
102 * @value: the 16-bit value to write to the control register
105 static int set_control(struct udevice *dev, u16 value)
107 struct ihs_video_out_priv *priv = dev_get_priv(dev);
110 value |= ((priv->sync_src & 0x7) << 8);
112 ihs_video_out_set(priv->map, control, value);
117 int ihs_video_out_get_info(struct udevice *dev, struct video_osd_info *info)
119 struct ihs_video_out_priv *priv = dev_get_priv(dev);
122 ihs_video_out_get(priv->map, versions, &versions);
124 info->width = priv->base_width;
125 info->height = priv->base_height;
126 info->major_version = versions / 100;
127 info->minor_version = versions % 100;
132 int ihs_video_out_set_mem(struct udevice *dev, uint col, uint row, u8 *buf,
133 size_t buflen, uint count)
135 struct ihs_video_out_priv *priv = dev_get_priv(dev);
141 /* Repetitions (controlled via count parmeter) */
142 for (rep = 0; rep < count; ++rep) {
143 offset = row * priv->base_width + col + rep * (buflen / 2);
145 /* Write a single buffer copy */
146 for (k = 0; k < buflen / 2; ++k) {
147 uint max_size = priv->base_width * priv->base_height;
149 if (offset + k >= max_size) {
150 debug("%s: Write would be out of OSD bounds\n",
155 data = buf[2 * k + 1] + 256 * buf[2 * k];
156 out_le16(priv->vidmem + offset + k, data);
160 res = set_control(dev, CONTROL_FILTER_ORIGINAL |
164 debug("%s: Could not set control register\n", dev->name);
172 * div2_u16() - Approximately divide a 16-bit number by 2
174 * @val: The 16-bit value to divide by two
175 * Return: The approximate division of val by two
177 static inline u16 div2_u16(u16 val)
179 return (32767 * val) / 65535;
182 int ihs_video_out_set_size(struct udevice *dev, uint col, uint row)
184 struct ihs_video_out_priv *priv = dev_get_priv(dev);
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);
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));
202 int ihs_video_out_print(struct udevice *dev, uint col, uint row, ulong color,
206 u8 buffer[2 * MAX_VIDEOMEM_WIDTH];
208 uint charcount = strlen(text);
209 uint len = min(charcount, 2 * MAX_VIDEOMEM_WIDTH);
211 for (k = 0; k < len; ++k) {
212 buffer[2 * k] = text[k];
213 buffer[2 * k + 1] = color;
216 res = ihs_video_out_set_mem(dev, col, row, buffer, 2 * len, 1);
218 debug("%s: Could not write to video memory\n", dev->name);
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,
232 int ihs_video_out_probe(struct udevice *dev)
234 struct ihs_video_out_priv *priv = dev_get_priv(dev);
235 struct ofnode_phandle_args phandle_args;
238 struct display_timing timing;
241 res = regmap_init_mem(dev_ofnode(dev), &priv->map);
243 debug("%s: Could not initialize regmap (err = %d)\n", dev->name,
248 /* Range with index 2 is video memory */
249 priv->vidmem = regmap_get_range(priv->map, 2);
251 mode = dev_read_string(dev, "mode");
253 debug("%s: Could not read mode property\n", dev->name);
257 if (!strcmp(mode, "1024_768_60")) {
261 timing.hactive.typ = 1024;
262 timing.vactive.typ = 768;
263 } else if (!strcmp(mode, "720_400_70")) {
267 timing.hactive.typ = 720;
268 timing.vactive.typ = 400;
273 timing.hactive.typ = 640;
274 timing.vactive.typ = 480;
277 ihs_video_out_get(priv->map, features, &features);
279 res = set_control(dev, CONTROL_FILTER_ORIGINAL |
283 debug("%s: Could not set control register (err = %d)\n",
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;
293 res = dev_read_phandle_with_args(dev, "clk_gen", NULL, 0, 0,
296 debug("%s: Could not get clk_gen node (err = %d)\n",
301 res = uclass_get_device_by_ofnode(UCLASS_CLK, phandle_args.node,
304 debug("%s: Could not get clk_gen dev (err = %d)\n",
309 res = dev_read_phandle_with_args(dev, "video_tx", NULL, 0, 0,
312 debug("%s: Could not get video_tx (err = %d)\n",
317 res = uclass_get_device_by_ofnode(UCLASS_DISPLAY, phandle_args.node,
320 debug("%s: Could not get video_tx dev (err = %d)\n",
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",
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_alloc_size = sizeof(struct ihs_video_out_priv),