linux/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * HDMI wrapper
   4 *
   5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
   6 */
   7
   8#define DSS_SUBSYS_NAME "HDMIWP"
   9
  10#include <linux/kernel.h>
  11#include <linux/err.h>
  12#include <linux/io.h>
  13#include <linux/platform_device.h>
  14#include <linux/seq_file.h>
  15
  16#include "omapdss.h"
  17#include "dss.h"
  18#include "hdmi.h"
  19
  20void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s)
  21{
  22#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(wp->base, r))
  23
  24        DUMPREG(HDMI_WP_REVISION);
  25        DUMPREG(HDMI_WP_SYSCONFIG);
  26        DUMPREG(HDMI_WP_IRQSTATUS_RAW);
  27        DUMPREG(HDMI_WP_IRQSTATUS);
  28        DUMPREG(HDMI_WP_IRQENABLE_SET);
  29        DUMPREG(HDMI_WP_IRQENABLE_CLR);
  30        DUMPREG(HDMI_WP_IRQWAKEEN);
  31        DUMPREG(HDMI_WP_PWR_CTRL);
  32        DUMPREG(HDMI_WP_DEBOUNCE);
  33        DUMPREG(HDMI_WP_VIDEO_CFG);
  34        DUMPREG(HDMI_WP_VIDEO_SIZE);
  35        DUMPREG(HDMI_WP_VIDEO_TIMING_H);
  36        DUMPREG(HDMI_WP_VIDEO_TIMING_V);
  37        DUMPREG(HDMI_WP_CLK);
  38        DUMPREG(HDMI_WP_AUDIO_CFG);
  39        DUMPREG(HDMI_WP_AUDIO_CFG2);
  40        DUMPREG(HDMI_WP_AUDIO_CTRL);
  41        DUMPREG(HDMI_WP_AUDIO_DATA);
  42}
  43
  44u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp)
  45{
  46        return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS);
  47}
  48
  49void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus)
  50{
  51        hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus);
  52        /* flush posted write */
  53        hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS);
  54}
  55
  56void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask)
  57{
  58        hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_SET, mask);
  59}
  60
  61void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask)
  62{
  63        hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_CLR, mask);
  64}
  65
  66/* PHY_PWR_CMD */
  67int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val)
  68{
  69        /* Return if already the state */
  70        if (REG_GET(wp->base, HDMI_WP_PWR_CTRL, 5, 4) == val)
  71                return 0;
  72
  73        /* Command for power control of HDMI PHY */
  74        REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 7, 6);
  75
  76        /* Status of the power control of HDMI PHY */
  77        if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 5, 4, val)
  78                        != val) {
  79                DSSERR("Failed to set PHY power mode to %d\n", val);
  80                return -ETIMEDOUT;
  81        }
  82
  83        return 0;
  84}
  85
  86/* PLL_PWR_CMD */
  87int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val)
  88{
  89        /* Command for power control of HDMI PLL */
  90        REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 3, 2);
  91
  92        /* wait till PHY_PWR_STATUS is set */
  93        if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 1, 0, val)
  94                        != val) {
  95                DSSERR("Failed to set PLL_PWR_STATUS\n");
  96                return -ETIMEDOUT;
  97        }
  98
  99        return 0;
 100}
 101
 102int hdmi_wp_video_start(struct hdmi_wp_data *wp)
 103{
 104        REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, true, 31, 31);
 105
 106        return 0;
 107}
 108
 109void hdmi_wp_video_stop(struct hdmi_wp_data *wp)
 110{
 111        int i;
 112
 113        hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE);
 114
 115        REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31);
 116
 117        for (i = 0; i < 50; ++i) {
 118                u32 v;
 119
 120                msleep(20);
 121
 122                v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW);
 123                if (v & HDMI_IRQ_VIDEO_FRAME_DONE)
 124                        return;
 125        }
 126
 127        DSSERR("no HDMI FRAMEDONE when disabling output\n");
 128}
 129
 130void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
 131                const struct hdmi_video_format *video_fmt)
 132{
 133        u32 l = 0;
 134
 135        REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, video_fmt->packing_mode,
 136                10, 8);
 137
 138        l |= FLD_VAL(video_fmt->y_res, 31, 16);
 139        l |= FLD_VAL(video_fmt->x_res, 15, 0);
 140        hdmi_write_reg(wp->base, HDMI_WP_VIDEO_SIZE, l);
 141}
 142
 143void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp,
 144                                    const struct videomode *vm)
 145{
 146        u32 r;
 147        bool vsync_inv, hsync_inv;
 148        DSSDBG("Enter hdmi_wp_video_config_interface\n");
 149
 150        vsync_inv = !!(vm->flags & DISPLAY_FLAGS_VSYNC_LOW);
 151        hsync_inv = !!(vm->flags & DISPLAY_FLAGS_HSYNC_LOW);
 152
 153        r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG);
 154        r = FLD_MOD(r, 1, 7, 7);        /* VSYNC_POL to dispc active high */
 155        r = FLD_MOD(r, 1, 6, 6);        /* HSYNC_POL to dispc active high */
 156        r = FLD_MOD(r, vsync_inv, 5, 5);        /* CORE_VSYNC_INV */
 157        r = FLD_MOD(r, hsync_inv, 4, 4);        /* CORE_HSYNC_INV */
 158        r = FLD_MOD(r, !!(vm->flags & DISPLAY_FLAGS_INTERLACED), 3, 3);
 159        r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
 160        hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r);
 161}
 162
 163void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp,
 164                                 const struct videomode *vm)
 165{
 166        u32 timing_h = 0;
 167        u32 timing_v = 0;
 168        unsigned int hsync_len_offset = 1;
 169
 170        DSSDBG("Enter hdmi_wp_video_config_timing\n");
 171
 172        /*
 173         * On OMAP4 and OMAP5 ES1 the HSW field is programmed as is. On OMAP5
 174         * ES2+ (including DRA7/AM5 SoCs) HSW field is programmed to hsync_len-1.
 175         * However, we don't support OMAP5 ES1 at all, so we can just check for
 176         * OMAP4 here.
 177         */
 178        if (wp->version == 4)
 179                hsync_len_offset = 0;
 180
 181        timing_h |= FLD_VAL(vm->hback_porch, 31, 20);
 182        timing_h |= FLD_VAL(vm->hfront_porch, 19, 8);
 183        timing_h |= FLD_VAL(vm->hsync_len - hsync_len_offset, 7, 0);
 184        hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h);
 185
 186        timing_v |= FLD_VAL(vm->vback_porch, 31, 20);
 187        timing_v |= FLD_VAL(vm->vfront_porch, 19, 8);
 188        timing_v |= FLD_VAL(vm->vsync_len, 7, 0);
 189        hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_V, timing_v);
 190}
 191
 192void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
 193                struct videomode *vm, const struct hdmi_config *param)
 194{
 195        DSSDBG("Enter hdmi_wp_video_init_format\n");
 196
 197        video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
 198        video_fmt->y_res = param->vm.vactive;
 199        video_fmt->x_res = param->vm.hactive;
 200
 201        vm->hback_porch = param->vm.hback_porch;
 202        vm->hfront_porch = param->vm.hfront_porch;
 203        vm->hsync_len = param->vm.hsync_len;
 204        vm->vback_porch = param->vm.vback_porch;
 205        vm->vfront_porch = param->vm.vfront_porch;
 206        vm->vsync_len = param->vm.vsync_len;
 207
 208        vm->flags = param->vm.flags;
 209
 210        if (param->vm.flags & DISPLAY_FLAGS_INTERLACED) {
 211                video_fmt->y_res /= 2;
 212                vm->vback_porch /= 2;
 213                vm->vfront_porch /= 2;
 214                vm->vsync_len /= 2;
 215        }
 216
 217        if (param->vm.flags & DISPLAY_FLAGS_DOUBLECLK) {
 218                video_fmt->x_res *= 2;
 219                vm->hfront_porch *= 2;
 220                vm->hsync_len *= 2;
 221                vm->hback_porch *= 2;
 222        }
 223}
 224
 225void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
 226                struct hdmi_audio_format *aud_fmt)
 227{
 228        u32 r;
 229
 230        DSSDBG("Enter hdmi_wp_audio_config_format\n");
 231
 232        r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG);
 233        if (wp->version == 4) {
 234                r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24);
 235                r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16);
 236        }
 237        r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
 238        r = FLD_MOD(r, aud_fmt->type, 4, 4);
 239        r = FLD_MOD(r, aud_fmt->justification, 3, 3);
 240        r = FLD_MOD(r, aud_fmt->sample_order, 2, 2);
 241        r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
 242        r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
 243        hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r);
 244}
 245
 246void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
 247                struct hdmi_audio_dma *aud_dma)
 248{
 249        u32 r;
 250
 251        DSSDBG("Enter hdmi_wp_audio_config_dma\n");
 252
 253        r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2);
 254        r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
 255        r = FLD_MOD(r, aud_dma->block_size, 7, 0);
 256        hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r);
 257
 258        r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL);
 259        r = FLD_MOD(r, aud_dma->mode, 9, 9);
 260        r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
 261        hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r);
 262}
 263
 264int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable)
 265{
 266        REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 31, 31);
 267
 268        return 0;
 269}
 270
 271int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable)
 272{
 273        REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 30, 30);
 274
 275        return 0;
 276}
 277
 278int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp,
 279                 unsigned int version)
 280{
 281        struct resource *res;
 282
 283        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp");
 284        wp->base = devm_ioremap_resource(&pdev->dev, res);
 285        if (IS_ERR(wp->base))
 286                return PTR_ERR(wp->base);
 287
 288        wp->phys_base = res->start;
 289        wp->version = version;
 290
 291        return 0;
 292}
 293
 294phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp)
 295{
 296        return wp->phys_base + HDMI_WP_AUDIO_DATA;
 297}
 298