uboot/drivers/video/ihs_video_out.c
<<
>>
Prefs
   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
  20static const uint MAX_X_CHARS = 53;
  21static const uint MAX_Y_CHARS = 26;
  22static const uint MAX_VIDEOMEM_WIDTH = 64;
  23static const uint MAX_VIDEOMEM_HEIGHT = 32;
  24static const uint CHAR_WIDTH = 12;
  25static const uint CHAR_HEIGHT = 18;
  26
  27static const u16 BASE_WIDTH_MASK = 0x3f00;
  28static const uint BASE_WIDTH_SHIFT = 8;
  29static const u16 BASE_HEIGTH_MASK = 0x001f;
  30static const uint BASE_HEIGTH_SHIFT;
  31
  32struct 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
  55enum {
  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
  70struct 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
  91static 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 */
 105static 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
 117int 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
 132int 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 */
 177static inline u16 div2_u16(u16 val)
 178{
 179        return (32767 * val) / 65535;
 180}
 181
 182int 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
 202int 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
 225static 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
 232int 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
 335U_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};
 343