1 // SPDX-License-Identifier: GPL-2.0+
4 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
6 * based on the gdsys osd driver, which is
9 * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.de
16 #include <video_osd.h>
19 static const uint MAX_X_CHARS
= 53;
20 static const uint MAX_Y_CHARS
= 26;
21 static const uint MAX_VIDEOMEM_WIDTH
= 64;
22 static const uint MAX_VIDEOMEM_HEIGHT
= 32;
23 static const uint CHAR_WIDTH
= 12;
24 static const uint CHAR_HEIGHT
= 18;
26 static const u16 BASE_WIDTH_MASK
= 0x3f00;
27 static const uint BASE_WIDTH_SHIFT
= 8;
28 static const u16 BASE_HEIGTH_MASK
= 0x001f;
29 static const uint BASE_HEIGTH_SHIFT
;
31 struct ihs_video_out_regs
{
32 /* Device version register */
34 /* Device feature register */
36 /* Device control register */
38 /* Register controlling screen size */
40 /* Register controlling screen scaling */
42 /* Register controlling screen x position */
44 /* Register controlling screen y position */
48 #define ihs_video_out_set(map, member, val) \
49 regmap_range_set(map, 1, struct ihs_video_out_regs, member, val)
51 #define ihs_video_out_get(map, member, valp) \
52 regmap_range_get(map, 1, struct ihs_video_out_regs, member, valp)
55 CONTROL_FILTER_BLACK
= (0 << 0),
56 CONTROL_FILTER_ORIGINAL
= (1 << 0),
57 CONTROL_FILTER_DARKER
= (2 << 0),
58 CONTROL_FILTER_GRAY
= (3 << 0),
60 CONTROL_MODE_PASSTHROUGH
= (0 << 3),
61 CONTROL_MODE_OSD
= (1 << 3),
62 CONTROL_MODE_AUTO
= (2 << 3),
63 CONTROL_MODE_OFF
= (3 << 3),
65 CONTROL_ENABLE_OFF
= (0 << 6),
66 CONTROL_ENABLE_ON
= (1 << 6),
69 struct ihs_video_out_priv
{
70 /* Register map for OSD device */
72 /* Pointer to video memory */
74 /* Display width in text columns */
76 /* Display height in text rows */
78 /* x-resolution of the display in pixels */
80 /* y-resolution of the display in pixels */
82 /* OSD's sync mode (resolution + frequency) */
84 /* The display port output for this OSD */
85 struct udevice
*video_tx
;
86 /* The pixel clock generator for the display */
87 struct udevice
*clk_gen
;
90 static const struct udevice_id ihs_video_out_ids
[] = {
91 { .compatible
= "gdsys,ihs_video_out" },
96 * set_control() - Set the control register to a given value
98 * The current value of sync_src is preserved by the function automatically.
100 * @dev: the OSD device whose control register to set
101 * @value: the 16-bit value to write to the control register
104 static int set_control(struct udevice
*dev
, u16 value
)
106 struct ihs_video_out_priv
*priv
= dev_get_priv(dev
);
109 value
|= ((priv
->sync_src
& 0x7) << 8);
111 ihs_video_out_set(priv
->map
, control
, value
);
116 int ihs_video_out_get_info(struct udevice
*dev
, struct video_osd_info
*info
)
118 struct ihs_video_out_priv
*priv
= dev_get_priv(dev
);
121 ihs_video_out_get(priv
->map
, versions
, &versions
);
123 info
->width
= priv
->base_width
;
124 info
->height
= priv
->base_height
;
125 info
->major_version
= versions
/ 100;
126 info
->minor_version
= versions
% 100;
131 int ihs_video_out_set_mem(struct udevice
*dev
, uint col
, uint row
, u8
*buf
,
132 size_t buflen
, uint count
)
134 struct ihs_video_out_priv
*priv
= dev_get_priv(dev
);
140 /* Repetitions (controlled via count parmeter) */
141 for (rep
= 0; rep
< count
; ++rep
) {
142 offset
= row
* priv
->base_width
+ col
+ rep
* (buflen
/ 2);
144 /* Write a single buffer copy */
145 for (k
= 0; k
< buflen
/ 2; ++k
) {
146 uint max_size
= priv
->base_width
* priv
->base_height
;
148 if (offset
+ k
>= max_size
) {
149 debug("%s: Write would be out of OSD bounds\n",
154 data
= buf
[2 * k
+ 1] + 256 * buf
[2 * k
];
155 out_le16(priv
->vidmem
+ offset
+ k
, data
);
159 res
= set_control(dev
, CONTROL_FILTER_ORIGINAL
|
163 debug("%s: Could not set control register\n", dev
->name
);
171 * div2_u16() - Approximately divide a 16-bit number by 2
173 * @val: The 16-bit value to divide by two
174 * Return: The approximate division of val by two
176 static inline u16
div2_u16(u16 val
)
178 return (32767 * val
) / 65535;
181 int ihs_video_out_set_size(struct udevice
*dev
, uint col
, uint row
)
183 struct ihs_video_out_priv
*priv
= dev_get_priv(dev
);
185 if (!col
|| col
> MAX_VIDEOMEM_WIDTH
|| col
> MAX_X_CHARS
||
186 !row
|| row
> MAX_VIDEOMEM_HEIGHT
|| row
> MAX_Y_CHARS
) {
187 debug("%s: Desired OSD size invalid\n", dev
->name
);
191 ihs_video_out_set(priv
->map
, xy_size
, ((col
- 1) << 8) | (row
- 1));
192 /* Center OSD on screen */
193 ihs_video_out_set(priv
->map
, x_pos
,
194 div2_u16(priv
->res_x
- CHAR_WIDTH
* col
));
195 ihs_video_out_set(priv
->map
, y_pos
,
196 div2_u16(priv
->res_y
- CHAR_HEIGHT
* row
));
201 int ihs_video_out_print(struct udevice
*dev
, uint col
, uint row
, ulong color
,
205 u8 buffer
[2 * MAX_VIDEOMEM_WIDTH
];
207 uint charcount
= strlen(text
);
208 uint len
= min(charcount
, 2 * MAX_VIDEOMEM_WIDTH
);
210 for (k
= 0; k
< len
; ++k
) {
211 buffer
[2 * k
] = text
[k
];
212 buffer
[2 * k
+ 1] = color
;
215 res
= ihs_video_out_set_mem(dev
, col
, row
, buffer
, 2 * len
, 1);
217 debug("%s: Could not write to video memory\n", dev
->name
);
224 static const struct video_osd_ops ihs_video_out_ops
= {
225 .get_info
= ihs_video_out_get_info
,
226 .set_mem
= ihs_video_out_set_mem
,
227 .set_size
= ihs_video_out_set_size
,
228 .print
= ihs_video_out_print
,
231 int ihs_video_out_probe(struct udevice
*dev
)
233 struct ihs_video_out_priv
*priv
= dev_get_priv(dev
);
234 struct ofnode_phandle_args phandle_args
;
237 struct display_timing timing
;
240 res
= regmap_init_mem(dev_ofnode(dev
), &priv
->map
);
242 debug("%s: Could not initialize regmap (err = %d)\n", dev
->name
,
247 /* Range with index 2 is video memory */
248 priv
->vidmem
= regmap_get_range(priv
->map
, 2);
250 mode
= dev_read_string(dev
, "mode");
252 debug("%s: Could not read mode property\n", dev
->name
);
256 if (!strcmp(mode
, "1024_768_60")) {
260 timing
.hactive
.typ
= 1024;
261 timing
.vactive
.typ
= 768;
262 } else if (!strcmp(mode
, "720_400_70")) {
266 timing
.hactive
.typ
= 720;
267 timing
.vactive
.typ
= 400;
272 timing
.hactive
.typ
= 640;
273 timing
.vactive
.typ
= 480;
276 ihs_video_out_get(priv
->map
, features
, &features
);
278 res
= set_control(dev
, CONTROL_FILTER_ORIGINAL
|
282 debug("%s: Could not set control register (err = %d)\n",
287 priv
->base_width
= ((features
& BASE_WIDTH_MASK
)
288 >> BASE_WIDTH_SHIFT
) + 1;
289 priv
->base_height
= ((features
& BASE_HEIGTH_MASK
)
290 >> BASE_HEIGTH_SHIFT
) + 1;
292 res
= dev_read_phandle_with_args(dev
, "clk_gen", NULL
, 0, 0,
295 debug("%s: Could not get clk_gen node (err = %d)\n",
300 res
= uclass_get_device_by_ofnode(UCLASS_CLK
, phandle_args
.node
,
303 debug("%s: Could not get clk_gen dev (err = %d)\n",
308 res
= dev_read_phandle_with_args(dev
, "video_tx", NULL
, 0, 0,
311 debug("%s: Could not get video_tx (err = %d)\n",
316 res
= uclass_get_device_by_ofnode(UCLASS_DISPLAY
, phandle_args
.node
,
319 debug("%s: Could not get video_tx dev (err = %d)\n",
324 res
= display_enable(priv
->video_tx
, 8, &timing
);
325 if (res
&& res
!= -EIO
) { /* Ignore missing DP sink error */
326 debug("%s: Could not enable the display (err = %d)\n",
334 U_BOOT_DRIVER(ihs_video_out_drv
) = {
335 .name
= "ihs_video_out_drv",
336 .id
= UCLASS_VIDEO_OSD
,
337 .ops
= &ihs_video_out_ops
,
338 .of_match
= ihs_video_out_ids
,
339 .probe
= ihs_video_out_probe
,
340 .priv_auto
= sizeof(struct ihs_video_out_priv
),