linux/drivers/gpu/drm/xilinx/xilinx_osd.c
<<
>>
Prefs
   1/*
   2 * Xilinx OSD support
   3 *
   4 *  Copyright (C) 2013 Xilinx, Inc.
   5 *
   6 *  Author: Hyun Woo Kwon <hyunk@xilinx.com>
   7 *
   8 * This software is licensed under the terms of the GNU General Public
   9 * License version 2, as published by the Free Software Foundation, and
  10 * may be copied, distributed, and modified under those terms.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 */
  17
  18#include <drm/drmP.h>
  19
  20#include <linux/device.h>
  21#include <linux/err.h>
  22#include <linux/io.h>
  23#include <linux/module.h>
  24#include <linux/of_address.h>
  25#include <linux/of_irq.h>
  26#include <linux/slab.h>
  27
  28#include "xilinx_drm_drv.h"
  29
  30#include "xilinx_osd.h"
  31
  32/* registers */
  33#define OSD_CTL 0x000   /* control */
  34#define OSD_SS  0x020   /* screen size */
  35#define OSD_ENC 0x028   /* encoding register */
  36#define OSD_BC0 0x100   /* background color channel 0 */
  37#define OSD_BC1 0x104   /* background color channel 1 */
  38#define OSD_BC2 0x108   /* background color channel 2 */
  39
  40#define OSD_L0C 0x110   /* layer 0 control */
  41
  42/* register offset of layers */
  43#define OSD_LAYER_SIZE  0x10
  44#define OSD_LXC         0x00    /* layer control */
  45#define OSD_LXP         0x04    /* layer position */
  46#define OSD_LXS         0x08    /* layer size */
  47
  48/*osd control register bit definition */
  49#define OSD_CTL_RUE             (1 << 1)        /* osd reg update enable */
  50#define OSD_CTL_EN              (1 << 0)        /* osd enable */
  51
  52/* osd screen size register bit definition */
  53#define OSD_SS_YSIZE_MASK   0x0fff0000  /* vertical height of OSD output */
  54#define OSD_SS_YSIZE_SHIFT  16          /* bit shift of OSD_SS_YSIZE_MASK */
  55#define OSD_SS_XSIZE_MASK   0x00000fff  /* horizontal width of OSD output */
  56
  57/* osd vidoe format mask */
  58#define OSD_VIDEO_FORMAT_MASK   0x0000000f      /* video format */
  59
  60/* osd background color channel 0 */
  61#define OSD_BC0_YG_MASK         0x000000ff      /* Y (luma) or Green */
  62
  63/* osd background color channel 1 */
  64#define OSD_BC1_UCBB_MASK       0x000000ff      /* U (Cb) or Blue */
  65
  66/* osd background color channel 2 */
  67#define OSD_BC2_VCRR_MASK       0x000000ff      /* V(Cr) or Red */
  68
  69/* maximum number of the layers */
  70#define OSD_MAX_NUM_OF_LAYERS   8
  71
  72/* osd layer control (layer 0 through (OSD_MAX_NUM_OF_LAYERS - 1)) */
  73#define OSD_LXC_ALPHA_MASK      0x0fff0000      /* global alpha value */
  74#define OSD_LXC_ALPHA_SHIFT     16              /* bit shift of alpha value */
  75#define OSD_LXC_PRIORITY_MASK   0x00000700      /* layer priority */
  76#define OSD_LXC_PRIORITY_SHIFT  8               /* bit shift of priority */
  77#define OSD_LXC_GALPHAEN        (1 << 1)        /* global alpha enable */
  78#define OSD_LXC_EN              (1 << 0)        /* layer enable */
  79
  80/* osd layer position (layer 0 through (OSD_MAX_NUM_OF_LAYERS - 1)) */
  81#define OSD_LXP_YSTART_MASK     0x0fff0000      /* vert start line */
  82#define OSD_LXP_YSTART_SHIFT    16              /* vert start line bit shift */
  83#define OSD_LXP_XSTART_MASK     0x00000fff      /* horizontal start pixel */
  84
  85/* osd layer size (layer 0 through (OSD_MAX_NUM_OF_LAYERS - 1)) */
  86#define OSD_LXS_YSIZE_MASK      0x0fff0000      /* vert size */
  87#define OSD_LXS_YSIZE_SHIFT     16              /* vertical size bit shift */
  88#define OSD_LXS_XSIZE_MASK      0x00000fff      /* horizontal size of layer */
  89
  90/* osd software reset */
  91#define OSD_RST_RESET   (1 << 31)
  92
  93/**
  94 * struct xilinx_osd_layer - Xilinx OSD layer object
  95 *
  96 * @base: base address
  97 * @id: id
  98 * @avail: available flag
  99 * @osd: osd
 100 */
 101struct xilinx_osd_layer {
 102        void __iomem *base;
 103        int id;
 104        bool avail;
 105        struct xilinx_osd *osd;
 106};
 107
 108/**
 109 * struct xilinx_osd - Xilinx OSD object
 110 *
 111 * @base: base address
 112 * @layers: layers
 113 * @num_layers: number of layers
 114 * @max_width: maximum width
 115 * @format: video format
 116 */
 117struct xilinx_osd {
 118        void __iomem *base;
 119        struct xilinx_osd_layer *layers[OSD_MAX_NUM_OF_LAYERS];
 120        unsigned int num_layers;
 121        unsigned int max_width;
 122        unsigned int format;
 123};
 124
 125/* osd layer operation */
 126/* set layer alpha */
 127void xilinx_osd_layer_set_alpha(struct xilinx_osd_layer *layer, u32 alpha)
 128{
 129        u32 value;
 130
 131        DRM_DEBUG_DRIVER("layer->id: %d\n", layer->id);
 132        DRM_DEBUG_DRIVER("alpha: 0x%08x\n", alpha);
 133
 134        value = xilinx_drm_readl(layer->base, OSD_LXC);
 135        value &= ~OSD_LXC_ALPHA_MASK;
 136        value |= (alpha << OSD_LXC_ALPHA_SHIFT) & OSD_LXC_ALPHA_MASK;
 137        xilinx_drm_writel(layer->base, OSD_LXC, value);
 138}
 139
 140void xilinx_osd_layer_enable_alpha(struct xilinx_osd_layer *layer, bool enable)
 141{
 142        u32 value;
 143
 144        DRM_DEBUG_DRIVER("layer->id: %d\n", layer->id);
 145        DRM_DEBUG_DRIVER("enable: %d\n", enable);
 146
 147        value = xilinx_drm_readl(layer->base, OSD_LXC);
 148        value = enable ? (value | OSD_LXC_GALPHAEN) :
 149                (value & ~OSD_LXC_GALPHAEN);
 150        xilinx_drm_writel(layer->base, OSD_LXC, value);
 151}
 152
 153/* set layer priority */
 154void xilinx_osd_layer_set_priority(struct xilinx_osd_layer *layer, u32 prio)
 155{
 156        u32 value;
 157
 158        DRM_DEBUG_DRIVER("layer->id: %d\n", layer->id);
 159        DRM_DEBUG_DRIVER("prio: %d\n", prio);
 160
 161        value = xilinx_drm_readl(layer->base, OSD_LXC);
 162        value &= ~OSD_LXC_PRIORITY_MASK;
 163        value |= (prio << OSD_LXC_PRIORITY_SHIFT) & OSD_LXC_PRIORITY_MASK;
 164        xilinx_drm_writel(layer->base, OSD_LXC, value);
 165}
 166
 167/* set layer dimension */
 168void xilinx_osd_layer_set_dimension(struct xilinx_osd_layer *layer,
 169                                    u16 xstart, u16 ystart,
 170                                    u16 xsize, u16 ysize)
 171{
 172        u32 value;
 173
 174        DRM_DEBUG_DRIVER("layer->id: %d\n", layer->id);
 175        DRM_DEBUG_DRIVER("w: %d(%d), h: %d(%d)\n",
 176                         xsize, xstart, ysize, ystart);
 177
 178        value = xstart & OSD_LXP_XSTART_MASK;
 179        value |= (ystart << OSD_LXP_YSTART_SHIFT) & OSD_LXP_YSTART_MASK;
 180
 181        xilinx_drm_writel(layer->base, OSD_LXP, value);
 182
 183        value = xsize & OSD_LXS_XSIZE_MASK;
 184        value |= (ysize << OSD_LXS_YSIZE_SHIFT) & OSD_LXS_YSIZE_MASK;
 185
 186        xilinx_drm_writel(layer->base, OSD_LXS, value);
 187}
 188
 189/* enable layer */
 190void xilinx_osd_layer_enable(struct xilinx_osd_layer *layer)
 191{
 192        u32 value;
 193
 194        DRM_DEBUG_DRIVER("layer->id: %d\n", layer->id);
 195
 196        value = xilinx_drm_readl(layer->base, OSD_LXC);
 197        value |= OSD_LXC_EN;
 198        xilinx_drm_writel(layer->base, OSD_LXC, value);
 199}
 200
 201/* disable layer */
 202void xilinx_osd_layer_disable(struct xilinx_osd_layer *layer)
 203{
 204        u32 value;
 205
 206        DRM_DEBUG_DRIVER("layer->id: %d\n", layer->id);
 207
 208        value = xilinx_drm_readl(layer->base, OSD_LXC);
 209        value &= ~OSD_LXC_EN;
 210        xilinx_drm_writel(layer->base, OSD_LXC, value);
 211}
 212
 213/* get an available layer */
 214struct xilinx_osd_layer *xilinx_osd_layer_get(struct xilinx_osd *osd)
 215{
 216        struct xilinx_osd_layer *layer = NULL;
 217        int i;
 218
 219        for (i = 0; i < osd->num_layers; i++) {
 220                if (osd->layers[i]->avail) {
 221                        layer = osd->layers[i];
 222                        layer->avail = false;
 223                        break;
 224                }
 225        }
 226
 227        if (!layer)
 228                return ERR_PTR(-ENODEV);
 229
 230        DRM_DEBUG_DRIVER("layer id: %d\n", i);
 231
 232        return layer;
 233}
 234
 235/* put a layer */
 236void xilinx_osd_layer_put(struct xilinx_osd_layer *layer)
 237{
 238        layer->avail = true;
 239}
 240
 241/* osd operations */
 242/* set osd color */
 243void xilinx_osd_set_color(struct xilinx_osd *osd, u8 r, u8 g, u8 b)
 244{
 245        u32 value;
 246
 247        value = g;
 248        xilinx_drm_writel(osd->base, OSD_BC0, value);
 249        value = b;
 250        xilinx_drm_writel(osd->base, OSD_BC1, value);
 251        value = r;
 252        xilinx_drm_writel(osd->base, OSD_BC2, value);
 253}
 254
 255/* set osd dimension */
 256void xilinx_osd_set_dimension(struct xilinx_osd *osd, u32 width, u32 height)
 257{
 258        u32 value;
 259
 260        DRM_DEBUG_DRIVER("w: %d, h: %d\n", width, height);
 261
 262        value = width | ((height << OSD_SS_YSIZE_SHIFT) & OSD_SS_YSIZE_MASK);
 263        xilinx_drm_writel(osd->base, OSD_SS, value);
 264}
 265
 266/* get osd number of layers */
 267unsigned int xilinx_osd_get_num_layers(struct xilinx_osd *osd)
 268{
 269        return osd->num_layers;
 270}
 271
 272/* get osd max width */
 273unsigned int xilinx_osd_get_max_width(struct xilinx_osd *osd)
 274{
 275        return osd->max_width;
 276}
 277
 278/* get osd color format */
 279unsigned int xilinx_osd_get_format(struct xilinx_osd *osd)
 280{
 281        return osd->format;
 282}
 283
 284/* reset osd */
 285void xilinx_osd_reset(struct xilinx_osd *osd)
 286{
 287        xilinx_drm_writel(osd->base, OSD_CTL, OSD_RST_RESET);
 288}
 289
 290/* enable osd */
 291void xilinx_osd_enable(struct xilinx_osd *osd)
 292{
 293        xilinx_drm_writel(osd->base, OSD_CTL,
 294                          xilinx_drm_readl(osd->base, OSD_CTL) | OSD_CTL_EN);
 295}
 296
 297/* disable osd */
 298void xilinx_osd_disable(struct xilinx_osd *osd)
 299{
 300        xilinx_drm_writel(osd->base, OSD_CTL,
 301                          xilinx_drm_readl(osd->base, OSD_CTL) & ~OSD_CTL_EN);
 302}
 303
 304/* register-update-enable osd */
 305void xilinx_osd_enable_rue(struct xilinx_osd *osd)
 306{
 307        xilinx_drm_writel(osd->base, OSD_CTL,
 308                          xilinx_drm_readl(osd->base, OSD_CTL) | OSD_CTL_RUE);
 309}
 310
 311/* register-update-enable osd */
 312void xilinx_osd_disable_rue(struct xilinx_osd *osd)
 313{
 314        xilinx_drm_writel(osd->base, OSD_CTL,
 315                          xilinx_drm_readl(osd->base, OSD_CTL) & ~OSD_CTL_RUE);
 316}
 317
 318static const struct of_device_id xilinx_osd_of_match[] = {
 319        { .compatible = "xlnx,v-osd-5.01.a" },
 320        { /* end of table */ },
 321};
 322
 323struct xilinx_osd *xilinx_osd_probe(struct device *dev,
 324                                    struct device_node *node)
 325{
 326        struct xilinx_osd *osd;
 327        struct xilinx_osd_layer *layer;
 328        const struct of_device_id *match;
 329        struct resource res;
 330        int i;
 331        int ret;
 332
 333        match = of_match_node(xilinx_osd_of_match, node);
 334        if (!match) {
 335                dev_err(dev, "failed to match the device node\n");
 336                return ERR_PTR(-ENODEV);
 337        }
 338
 339        osd = devm_kzalloc(dev, sizeof(*osd), GFP_KERNEL);
 340        if (!osd)
 341                return ERR_PTR(-ENOMEM);
 342
 343        ret = of_address_to_resource(node, 0, &res);
 344        if (ret) {
 345                dev_err(dev, "failed to of_address_to_resource\n");
 346                return ERR_PTR(ret);
 347        }
 348
 349        osd->base = devm_ioremap_resource(dev, &res);
 350        if (IS_ERR(osd->base))
 351                return ERR_CAST(osd->base);
 352
 353        ret = of_property_read_u32(node, "xlnx,num-layers", &osd->num_layers);
 354        if (ret) {
 355                dev_warn(dev, "failed to get num of layers prop\n");
 356                return ERR_PTR(ret);
 357        }
 358
 359        ret = of_property_read_u32(node, "xlnx,screen-width", &osd->max_width);
 360        if (ret) {
 361                dev_warn(dev, "failed to get screen width prop\n");
 362                return ERR_PTR(ret);
 363        }
 364
 365        /* read the video format set by a user */
 366        osd->format = xilinx_drm_readl(osd->base, OSD_ENC) &
 367                      OSD_VIDEO_FORMAT_MASK;
 368
 369        for (i = 0; i < osd->num_layers; i++) {
 370                layer = devm_kzalloc(dev, sizeof(*layer), GFP_KERNEL);
 371                if (!layer)
 372                        return ERR_PTR(-ENOMEM);
 373
 374                layer->base = osd->base + OSD_L0C + OSD_LAYER_SIZE * i;
 375                layer->id = i;
 376                layer->osd = osd;
 377                layer->avail = true;
 378                osd->layers[i] = layer;
 379        }
 380
 381        return osd;
 382}
 383