linux/drivers/gpu/drm/sti/sti_tvout.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) STMicroelectronics SA 2014
   3 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
   4 *          Vincent Abriou <vincent.abriou@st.com>
   5 *          for STMicroelectronics.
   6 * License terms:  GNU General Public License (GPL), version 2
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/component.h>
  11#include <linux/module.h>
  12#include <linux/of_platform.h>
  13#include <linux/platform_device.h>
  14#include <linux/reset.h>
  15#include <linux/seq_file.h>
  16
  17#include <drm/drmP.h>
  18#include <drm/drm_crtc_helper.h>
  19
  20#include "sti_crtc.h"
  21#include "sti_drv.h"
  22#include "sti_vtg.h"
  23
  24/* glue registers */
  25#define TVO_CSC_MAIN_M0                  0x000
  26#define TVO_CSC_MAIN_M1                  0x004
  27#define TVO_CSC_MAIN_M2                  0x008
  28#define TVO_CSC_MAIN_M3                  0x00c
  29#define TVO_CSC_MAIN_M4                  0x010
  30#define TVO_CSC_MAIN_M5                  0x014
  31#define TVO_CSC_MAIN_M6                  0x018
  32#define TVO_CSC_MAIN_M7                  0x01c
  33#define TVO_MAIN_IN_VID_FORMAT           0x030
  34#define TVO_CSC_AUX_M0                   0x100
  35#define TVO_CSC_AUX_M1                   0x104
  36#define TVO_CSC_AUX_M2                   0x108
  37#define TVO_CSC_AUX_M3                   0x10c
  38#define TVO_CSC_AUX_M4                   0x110
  39#define TVO_CSC_AUX_M5                   0x114
  40#define TVO_CSC_AUX_M6                   0x118
  41#define TVO_CSC_AUX_M7                   0x11c
  42#define TVO_AUX_IN_VID_FORMAT            0x130
  43#define TVO_VIP_HDF                      0x400
  44#define TVO_HD_SYNC_SEL                  0x418
  45#define TVO_HD_DAC_CFG_OFF               0x420
  46#define TVO_VIP_HDMI                     0x500
  47#define TVO_HDMI_FORCE_COLOR_0           0x504
  48#define TVO_HDMI_FORCE_COLOR_1           0x508
  49#define TVO_HDMI_CLIP_VALUE_B_CB         0x50c
  50#define TVO_HDMI_CLIP_VALUE_Y_G          0x510
  51#define TVO_HDMI_CLIP_VALUE_R_CR         0x514
  52#define TVO_HDMI_SYNC_SEL                0x518
  53#define TVO_HDMI_DFV_OBS                 0x540
  54#define TVO_VIP_DVO                      0x600
  55#define TVO_DVO_SYNC_SEL                 0x618
  56#define TVO_DVO_CONFIG                   0x620
  57
  58#define TVO_IN_FMT_SIGNED                BIT(0)
  59#define TVO_SYNC_EXT                     BIT(4)
  60
  61#define TVO_VIP_REORDER_R_SHIFT          24
  62#define TVO_VIP_REORDER_G_SHIFT          20
  63#define TVO_VIP_REORDER_B_SHIFT          16
  64#define TVO_VIP_REORDER_MASK             0x3
  65#define TVO_VIP_REORDER_Y_G_SEL          0
  66#define TVO_VIP_REORDER_CB_B_SEL         1
  67#define TVO_VIP_REORDER_CR_R_SEL         2
  68
  69#define TVO_VIP_CLIP_SHIFT               8
  70#define TVO_VIP_CLIP_MASK                0x7
  71#define TVO_VIP_CLIP_DISABLED            0
  72#define TVO_VIP_CLIP_EAV_SAV             1
  73#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2
  74#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3
  75#define TVO_VIP_CLIP_PROG_RANGE          4
  76
  77#define TVO_VIP_RND_SHIFT                4
  78#define TVO_VIP_RND_MASK                 0x3
  79#define TVO_VIP_RND_8BIT_ROUNDED         0
  80#define TVO_VIP_RND_10BIT_ROUNDED        1
  81#define TVO_VIP_RND_12BIT_ROUNDED        2
  82
  83#define TVO_VIP_SEL_INPUT_MASK           0xf
  84#define TVO_VIP_SEL_INPUT_MAIN           0x0
  85#define TVO_VIP_SEL_INPUT_AUX            0x8
  86#define TVO_VIP_SEL_INPUT_FORCE_COLOR    0xf
  87#define TVO_VIP_SEL_INPUT_BYPASS_MASK    0x1
  88#define TVO_VIP_SEL_INPUT_BYPASSED       1
  89
  90#define TVO_SYNC_MAIN_VTG_SET_REF        0x00
  91#define TVO_SYNC_AUX_VTG_SET_REF         0x10
  92
  93#define TVO_SYNC_HD_DCS_SHIFT            8
  94
  95#define TVO_SYNC_DVO_PAD_HSYNC_SHIFT     8
  96#define TVO_SYNC_DVO_PAD_VSYNC_SHIFT     16
  97
  98#define ENCODER_CRTC_MASK                (BIT(0) | BIT(1))
  99
 100#define TVO_MIN_HD_HEIGHT                720
 101
 102/* enum listing the supported output data format */
 103enum sti_tvout_video_out_type {
 104        STI_TVOUT_VIDEO_OUT_RGB,
 105        STI_TVOUT_VIDEO_OUT_YUV,
 106};
 107
 108struct sti_tvout {
 109        struct device *dev;
 110        struct drm_device *drm_dev;
 111        void __iomem *regs;
 112        struct reset_control *reset;
 113        struct drm_encoder *hdmi;
 114        struct drm_encoder *hda;
 115        struct drm_encoder *dvo;
 116        bool debugfs_registered;
 117};
 118
 119struct sti_tvout_encoder {
 120        struct drm_encoder encoder;
 121        struct sti_tvout *tvout;
 122};
 123
 124#define to_sti_tvout_encoder(x) \
 125        container_of(x, struct sti_tvout_encoder, encoder)
 126
 127#define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout
 128
 129/* preformatter conversion matrix */
 130static const u32 rgb_to_ycbcr_601[8] = {
 131        0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,
 132        0x0000082E, 0x00002000, 0x00002000, 0x00000000
 133};
 134
 135/* 709 RGB to YCbCr */
 136static const u32 rgb_to_ycbcr_709[8] = {
 137        0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,
 138        0x0000082F, 0x00002000, 0x00002000, 0x00000000
 139};
 140
 141static u32 tvout_read(struct sti_tvout *tvout, int offset)
 142{
 143        return readl(tvout->regs + offset);
 144}
 145
 146static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)
 147{
 148        writel(val, tvout->regs + offset);
 149}
 150
 151/**
 152 * Set the clipping mode of a VIP
 153 *
 154 * @tvout: tvout structure
 155 * @reg: register to set
 156 * @cr_r:
 157 * @y_g:
 158 * @cb_b:
 159 */
 160static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg,
 161                                      u32 cr_r, u32 y_g, u32 cb_b)
 162{
 163        u32 val = tvout_read(tvout, reg);
 164
 165        val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);
 166        val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);
 167        val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);
 168        val |= cr_r << TVO_VIP_REORDER_R_SHIFT;
 169        val |= y_g << TVO_VIP_REORDER_G_SHIFT;
 170        val |= cb_b << TVO_VIP_REORDER_B_SHIFT;
 171
 172        tvout_write(tvout, val, reg);
 173}
 174
 175/**
 176 * Set the clipping mode of a VIP
 177 *
 178 * @tvout: tvout structure
 179 * @reg: register to set
 180 * @range: clipping range
 181 */
 182static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range)
 183{
 184        u32 val = tvout_read(tvout, reg);
 185
 186        val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);
 187        val |= range << TVO_VIP_CLIP_SHIFT;
 188        tvout_write(tvout, val, reg);
 189}
 190
 191/**
 192 * Set the rounded value of a VIP
 193 *
 194 * @tvout: tvout structure
 195 * @reg: register to set
 196 * @rnd: rounded val per component
 197 */
 198static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd)
 199{
 200        u32 val = tvout_read(tvout, reg);
 201
 202        val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);
 203        val |= rnd << TVO_VIP_RND_SHIFT;
 204        tvout_write(tvout, val, reg);
 205}
 206
 207/**
 208 * Select the VIP input
 209 *
 210 * @tvout: tvout structure
 211 * @reg: register to set
 212 * @main_path: main or auxiliary path
 213 * @sel_input: selected_input (main/aux + conv)
 214 */
 215static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
 216                                    int reg,
 217                                    bool main_path,
 218                                    enum sti_tvout_video_out_type video_out)
 219{
 220        u32 sel_input;
 221        u32 val = tvout_read(tvout, reg);
 222
 223        if (main_path)
 224                sel_input = TVO_VIP_SEL_INPUT_MAIN;
 225        else
 226                sel_input = TVO_VIP_SEL_INPUT_AUX;
 227
 228        switch (video_out) {
 229        case STI_TVOUT_VIDEO_OUT_RGB:
 230                sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;
 231                break;
 232        case STI_TVOUT_VIDEO_OUT_YUV:
 233                sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;
 234                break;
 235        }
 236
 237        /* on stih407 chip the sel_input bypass mode logic is inverted */
 238        sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
 239
 240        val &= ~TVO_VIP_SEL_INPUT_MASK;
 241        val |= sel_input;
 242        tvout_write(tvout, val, reg);
 243}
 244
 245/**
 246 * Select the input video signed or unsigned
 247 *
 248 * @tvout: tvout structure
 249 * @reg: register to set
 250 * @in_vid_signed: used video input format
 251 */
 252static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout,
 253                int reg, u32 in_vid_fmt)
 254{
 255        u32 val = tvout_read(tvout, reg);
 256
 257        val &= ~TVO_IN_FMT_SIGNED;
 258        val |= in_vid_fmt;
 259        tvout_write(tvout, val, reg);
 260}
 261
 262/**
 263 * Set preformatter matrix
 264 *
 265 * @tvout: tvout structure
 266 * @mode: display mode structure
 267 */
 268static void tvout_preformatter_set_matrix(struct sti_tvout *tvout,
 269                                          struct drm_display_mode *mode)
 270{
 271        unsigned int i;
 272        const u32 *pf_matrix;
 273
 274        if (mode->vdisplay >= TVO_MIN_HD_HEIGHT)
 275                pf_matrix = rgb_to_ycbcr_709;
 276        else
 277                pf_matrix = rgb_to_ycbcr_601;
 278
 279        for (i = 0; i < 8; i++) {
 280                tvout_write(tvout, *(pf_matrix + i),
 281                            TVO_CSC_MAIN_M0 + (i * 4));
 282                tvout_write(tvout, *(pf_matrix + i),
 283                            TVO_CSC_AUX_M0 + (i * 4));
 284        }
 285}
 286
 287/**
 288 * Start VIP block for DVO output
 289 *
 290 * @tvout: pointer on tvout structure
 291 * @main_path: true if main path has to be used in the vip configuration
 292 *        else aux path is used.
 293 */
 294static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
 295{
 296        u32 tvo_in_vid_format;
 297        int val, tmp;
 298
 299        dev_dbg(tvout->dev, "%s\n", __func__);
 300
 301        if (main_path) {
 302                DRM_DEBUG_DRIVER("main vip for DVO\n");
 303                /* Select the input sync for dvo */
 304                tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO;
 305                val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
 306                val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
 307                val |= tmp;
 308                tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
 309                tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
 310        } else {
 311                DRM_DEBUG_DRIVER("aux vip for DVO\n");
 312                /* Select the input sync for dvo */
 313                tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO;
 314                val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
 315                val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
 316                val |= tmp;
 317                tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
 318                tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
 319        }
 320
 321        /* Set color channel order */
 322        tvout_vip_set_color_order(tvout, TVO_VIP_DVO,
 323                                  TVO_VIP_REORDER_CR_R_SEL,
 324                                  TVO_VIP_REORDER_Y_G_SEL,
 325                                  TVO_VIP_REORDER_CB_B_SEL);
 326
 327        /* Set clipping mode */
 328        tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED);
 329
 330        /* Set round mode (rounded to 8-bit per component) */
 331        tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED);
 332
 333        /* Set input video format */
 334        tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 335
 336        /* Input selection */
 337        tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path,
 338                                STI_TVOUT_VIDEO_OUT_RGB);
 339}
 340
 341/**
 342 * Start VIP block for HDMI output
 343 *
 344 * @tvout: pointer on tvout structure
 345 * @main_path: true if main path has to be used in the vip configuration
 346 *        else aux path is used.
 347 */
 348static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
 349{
 350        u32 tvo_in_vid_format;
 351
 352        dev_dbg(tvout->dev, "%s\n", __func__);
 353
 354        if (main_path) {
 355                DRM_DEBUG_DRIVER("main vip for hdmi\n");
 356                /* select the input sync for hdmi */
 357                tvout_write(tvout,
 358                            TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDMI,
 359                            TVO_HDMI_SYNC_SEL);
 360                tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
 361        } else {
 362                DRM_DEBUG_DRIVER("aux vip for hdmi\n");
 363                /* select the input sync for hdmi */
 364                tvout_write(tvout,
 365                            TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDMI,
 366                            TVO_HDMI_SYNC_SEL);
 367                tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
 368        }
 369
 370        /* set color channel order */
 371        tvout_vip_set_color_order(tvout, TVO_VIP_HDMI,
 372                                  TVO_VIP_REORDER_CR_R_SEL,
 373                                  TVO_VIP_REORDER_Y_G_SEL,
 374                                  TVO_VIP_REORDER_CB_B_SEL);
 375
 376        /* set clipping mode */
 377        tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_DISABLED);
 378
 379        /* set round mode (rounded to 8-bit per component) */
 380        tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED);
 381
 382        /* set input video format */
 383        tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 384
 385        /* input selection */
 386        tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path,
 387                                STI_TVOUT_VIDEO_OUT_RGB);
 388}
 389
 390/**
 391 * Start HDF VIP and HD DAC
 392 *
 393 * @tvout: pointer on tvout structure
 394 * @main_path: true if main path has to be used in the vip configuration
 395 *        else aux path is used.
 396 */
 397static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
 398{
 399        u32 tvo_in_vid_format;
 400        int val;
 401
 402        dev_dbg(tvout->dev, "%s\n", __func__);
 403
 404        if (main_path) {
 405                DRM_DEBUG_DRIVER("main vip for HDF\n");
 406                /* Select the input sync for HD analog and HD DCS */
 407                val  = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS;
 408                val  = val << TVO_SYNC_HD_DCS_SHIFT;
 409                val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF;
 410                tvout_write(tvout, val, TVO_HD_SYNC_SEL);
 411                tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
 412        } else {
 413                DRM_DEBUG_DRIVER("aux vip for HDF\n");
 414                /* Select the input sync for HD analog and HD DCS */
 415                val  = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS;
 416                val  = val << TVO_SYNC_HD_DCS_SHIFT;
 417                val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF;
 418                tvout_write(tvout, val, TVO_HD_SYNC_SEL);
 419                tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
 420        }
 421
 422        /* set color channel order */
 423        tvout_vip_set_color_order(tvout, TVO_VIP_HDF,
 424                                  TVO_VIP_REORDER_CR_R_SEL,
 425                                  TVO_VIP_REORDER_Y_G_SEL,
 426                                  TVO_VIP_REORDER_CB_B_SEL);
 427
 428        /* set clipping mode */
 429        tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED);
 430
 431        /* set round mode (rounded to 10-bit per component) */
 432        tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
 433
 434        /* Set input video format */
 435        tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 436
 437        /* Input selection */
 438        tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path,
 439                                STI_TVOUT_VIDEO_OUT_YUV);
 440
 441        /* power up HD DAC */
 442        tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF);
 443}
 444
 445#define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \
 446                                   readl(tvout->regs + reg))
 447
 448static void tvout_dbg_vip(struct seq_file *s, int val)
 449{
 450        int r, g, b, tmp, mask;
 451        char *const reorder[] = {"Y_G", "Cb_B", "Cr_R"};
 452        char *const clipping[] = {"No", "EAV/SAV", "Limited range RGB/Y",
 453                                  "Limited range Cb/Cr", "decided by register"};
 454        char *const round[] = {"8-bit", "10-bit", "12-bit"};
 455        char *const input_sel[] = {"Main (color matrix enabled)",
 456                                   "Main (color matrix by-passed)",
 457                                   "", "", "", "", "", "",
 458                                   "Aux (color matrix enabled)",
 459                                   "Aux (color matrix by-passed)",
 460                                   "", "", "", "", "", "Force value"};
 461
 462        seq_putc(s, '\t');
 463        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
 464        r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
 465        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
 466        g = (val & mask) >> TVO_VIP_REORDER_G_SHIFT;
 467        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT;
 468        b = (val & mask) >> TVO_VIP_REORDER_B_SHIFT;
 469        seq_printf(s, "%-24s %s->%s %s->%s %s->%s\n", "Reorder:",
 470                   reorder[r], reorder[TVO_VIP_REORDER_CR_R_SEL],
 471                   reorder[g], reorder[TVO_VIP_REORDER_Y_G_SEL],
 472                   reorder[b], reorder[TVO_VIP_REORDER_CB_B_SEL]);
 473        seq_puts(s, "\t\t\t\t\t");
 474        mask = TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT;
 475        tmp = (val & mask) >> TVO_VIP_CLIP_SHIFT;
 476        seq_printf(s, "%-24s %s\n", "Clipping:", clipping[tmp]);
 477        seq_puts(s, "\t\t\t\t\t");
 478        mask = TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT;
 479        tmp = (val & mask) >> TVO_VIP_RND_SHIFT;
 480        seq_printf(s, "%-24s input data rounded to %s per component\n",
 481                   "Round:", round[tmp]);
 482        seq_puts(s, "\t\t\t\t\t");
 483        tmp = (val & TVO_VIP_SEL_INPUT_MASK);
 484        seq_printf(s, "%-24s %s", "Input selection:", input_sel[tmp]);
 485}
 486
 487static void tvout_dbg_hd_dac_cfg(struct seq_file *s, int val)
 488{
 489        seq_printf(s, "\t%-24s %s", "HD DAC:",
 490                   val & 1 ? "disabled" : "enabled");
 491}
 492
 493static int tvout_dbg_show(struct seq_file *s, void *data)
 494{
 495        struct drm_info_node *node = s->private;
 496        struct sti_tvout *tvout = (struct sti_tvout *)node->info_ent->data;
 497        struct drm_crtc *crtc;
 498
 499        seq_printf(s, "TVOUT: (vaddr = 0x%p)", tvout->regs);
 500
 501        seq_puts(s, "\n\n  HDMI encoder: ");
 502        crtc = tvout->hdmi->crtc;
 503        if (crtc) {
 504                seq_printf(s, "connected to %s path",
 505                           sti_crtc_is_main(crtc) ? "main" : "aux");
 506                DBGFS_DUMP(TVO_HDMI_SYNC_SEL);
 507                DBGFS_DUMP(TVO_VIP_HDMI);
 508                tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDMI));
 509        } else {
 510                seq_puts(s, "disabled");
 511        }
 512
 513        seq_puts(s, "\n\n  DVO encoder: ");
 514        crtc = tvout->dvo->crtc;
 515        if (crtc) {
 516                seq_printf(s, "connected to %s path",
 517                           sti_crtc_is_main(crtc) ? "main" : "aux");
 518                DBGFS_DUMP(TVO_DVO_SYNC_SEL);
 519                DBGFS_DUMP(TVO_DVO_CONFIG);
 520                DBGFS_DUMP(TVO_VIP_DVO);
 521                tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_DVO));
 522        } else {
 523                seq_puts(s, "disabled");
 524        }
 525
 526        seq_puts(s, "\n\n  HDA encoder: ");
 527        crtc = tvout->hda->crtc;
 528        if (crtc) {
 529                seq_printf(s, "connected to %s path",
 530                           sti_crtc_is_main(crtc) ? "main" : "aux");
 531                DBGFS_DUMP(TVO_HD_SYNC_SEL);
 532                DBGFS_DUMP(TVO_HD_DAC_CFG_OFF);
 533                tvout_dbg_hd_dac_cfg(s,
 534                                     readl(tvout->regs + TVO_HD_DAC_CFG_OFF));
 535                DBGFS_DUMP(TVO_VIP_HDF);
 536                tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDF));
 537        } else {
 538                seq_puts(s, "disabled");
 539        }
 540
 541        seq_puts(s, "\n\n  main path configuration");
 542        DBGFS_DUMP(TVO_CSC_MAIN_M0);
 543        DBGFS_DUMP(TVO_CSC_MAIN_M1);
 544        DBGFS_DUMP(TVO_CSC_MAIN_M2);
 545        DBGFS_DUMP(TVO_CSC_MAIN_M3);
 546        DBGFS_DUMP(TVO_CSC_MAIN_M4);
 547        DBGFS_DUMP(TVO_CSC_MAIN_M5);
 548        DBGFS_DUMP(TVO_CSC_MAIN_M6);
 549        DBGFS_DUMP(TVO_CSC_MAIN_M7);
 550        DBGFS_DUMP(TVO_MAIN_IN_VID_FORMAT);
 551
 552        seq_puts(s, "\n\n  auxiliary path configuration");
 553        DBGFS_DUMP(TVO_CSC_AUX_M0);
 554        DBGFS_DUMP(TVO_CSC_AUX_M2);
 555        DBGFS_DUMP(TVO_CSC_AUX_M3);
 556        DBGFS_DUMP(TVO_CSC_AUX_M4);
 557        DBGFS_DUMP(TVO_CSC_AUX_M5);
 558        DBGFS_DUMP(TVO_CSC_AUX_M6);
 559        DBGFS_DUMP(TVO_CSC_AUX_M7);
 560        DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
 561        seq_putc(s, '\n');
 562        return 0;
 563}
 564
 565static struct drm_info_list tvout_debugfs_files[] = {
 566        { "tvout", tvout_dbg_show, 0, NULL },
 567};
 568
 569static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor)
 570{
 571        unsigned int i;
 572
 573        for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++)
 574                tvout_debugfs_files[i].data = tvout;
 575
 576        return drm_debugfs_create_files(tvout_debugfs_files,
 577                                        ARRAY_SIZE(tvout_debugfs_files),
 578                                        minor->debugfs_root, minor);
 579}
 580
 581static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)
 582{
 583}
 584
 585static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,
 586                                       struct drm_display_mode *mode,
 587                                       struct drm_display_mode *adjusted_mode)
 588{
 589}
 590
 591static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
 592{
 593        struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);
 594
 595        drm_encoder_cleanup(encoder);
 596        kfree(sti_encoder);
 597}
 598
 599static int sti_tvout_late_register(struct drm_encoder *encoder)
 600{
 601        struct sti_tvout *tvout = to_sti_tvout(encoder);
 602        int ret;
 603
 604        if (tvout->debugfs_registered)
 605                return 0;
 606
 607        ret = tvout_debugfs_init(tvout, encoder->dev->primary);
 608        if (ret)
 609                return ret;
 610
 611        tvout->debugfs_registered = true;
 612        return 0;
 613}
 614
 615static void sti_tvout_early_unregister(struct drm_encoder *encoder)
 616{
 617        struct sti_tvout *tvout = to_sti_tvout(encoder);
 618
 619        if (!tvout->debugfs_registered)
 620                return;
 621
 622        tvout->debugfs_registered = false;
 623}
 624
 625static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
 626        .destroy = sti_tvout_encoder_destroy,
 627        .late_register = sti_tvout_late_register,
 628        .early_unregister = sti_tvout_early_unregister,
 629};
 630
 631static void sti_dvo_encoder_enable(struct drm_encoder *encoder)
 632{
 633        struct sti_tvout *tvout = to_sti_tvout(encoder);
 634
 635        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 636
 637        tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc));
 638}
 639
 640static void sti_dvo_encoder_disable(struct drm_encoder *encoder)
 641{
 642        struct sti_tvout *tvout = to_sti_tvout(encoder);
 643
 644        /* Reset VIP register */
 645        tvout_write(tvout, 0x0, TVO_VIP_DVO);
 646}
 647
 648static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = {
 649        .dpms = sti_tvout_encoder_dpms,
 650        .mode_set = sti_tvout_encoder_mode_set,
 651        .enable = sti_dvo_encoder_enable,
 652        .disable = sti_dvo_encoder_disable,
 653};
 654
 655static struct drm_encoder *
 656sti_tvout_create_dvo_encoder(struct drm_device *dev,
 657                             struct sti_tvout *tvout)
 658{
 659        struct sti_tvout_encoder *encoder;
 660        struct drm_encoder *drm_encoder;
 661
 662        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 663        if (!encoder)
 664                return NULL;
 665
 666        encoder->tvout = tvout;
 667
 668        drm_encoder = (struct drm_encoder *)encoder;
 669
 670        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 671        drm_encoder->possible_clones = 1 << 0;
 672
 673        drm_encoder_init(dev, drm_encoder,
 674                         &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS,
 675                         NULL);
 676
 677        drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs);
 678
 679        return drm_encoder;
 680}
 681
 682static void sti_hda_encoder_enable(struct drm_encoder *encoder)
 683{
 684        struct sti_tvout *tvout = to_sti_tvout(encoder);
 685
 686        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 687
 688        tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc));
 689}
 690
 691static void sti_hda_encoder_disable(struct drm_encoder *encoder)
 692{
 693        struct sti_tvout *tvout = to_sti_tvout(encoder);
 694
 695        /* reset VIP register */
 696        tvout_write(tvout, 0x0, TVO_VIP_HDF);
 697
 698        /* power down HD DAC */
 699        tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF);
 700}
 701
 702static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {
 703        .dpms = sti_tvout_encoder_dpms,
 704        .mode_set = sti_tvout_encoder_mode_set,
 705        .commit = sti_hda_encoder_enable,
 706        .disable = sti_hda_encoder_disable,
 707};
 708
 709static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
 710                struct sti_tvout *tvout)
 711{
 712        struct sti_tvout_encoder *encoder;
 713        struct drm_encoder *drm_encoder;
 714
 715        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 716        if (!encoder)
 717                return NULL;
 718
 719        encoder->tvout = tvout;
 720
 721        drm_encoder = (struct drm_encoder *) encoder;
 722
 723        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 724        drm_encoder->possible_clones = 1 << 0;
 725
 726        drm_encoder_init(dev, drm_encoder,
 727                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL);
 728
 729        drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);
 730
 731        return drm_encoder;
 732}
 733
 734static void sti_hdmi_encoder_enable(struct drm_encoder *encoder)
 735{
 736        struct sti_tvout *tvout = to_sti_tvout(encoder);
 737
 738        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 739
 740        tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc));
 741}
 742
 743static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)
 744{
 745        struct sti_tvout *tvout = to_sti_tvout(encoder);
 746
 747        /* reset VIP register */
 748        tvout_write(tvout, 0x0, TVO_VIP_HDMI);
 749}
 750
 751static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {
 752        .dpms = sti_tvout_encoder_dpms,
 753        .mode_set = sti_tvout_encoder_mode_set,
 754        .commit = sti_hdmi_encoder_enable,
 755        .disable = sti_hdmi_encoder_disable,
 756};
 757
 758static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,
 759                struct sti_tvout *tvout)
 760{
 761        struct sti_tvout_encoder *encoder;
 762        struct drm_encoder *drm_encoder;
 763
 764        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 765        if (!encoder)
 766                return NULL;
 767
 768        encoder->tvout = tvout;
 769
 770        drm_encoder = (struct drm_encoder *) encoder;
 771
 772        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 773        drm_encoder->possible_clones = 1 << 1;
 774
 775        drm_encoder_init(dev, drm_encoder,
 776                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL);
 777
 778        drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);
 779
 780        return drm_encoder;
 781}
 782
 783static void sti_tvout_create_encoders(struct drm_device *dev,
 784                struct sti_tvout *tvout)
 785{
 786        tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);
 787        tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);
 788        tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout);
 789}
 790
 791static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
 792{
 793        if (tvout->hdmi)
 794                drm_encoder_cleanup(tvout->hdmi);
 795        tvout->hdmi = NULL;
 796
 797        if (tvout->hda)
 798                drm_encoder_cleanup(tvout->hda);
 799        tvout->hda = NULL;
 800
 801        if (tvout->dvo)
 802                drm_encoder_cleanup(tvout->dvo);
 803        tvout->dvo = NULL;
 804}
 805
 806static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
 807{
 808        struct sti_tvout *tvout = dev_get_drvdata(dev);
 809        struct drm_device *drm_dev = data;
 810
 811        tvout->drm_dev = drm_dev;
 812
 813        sti_tvout_create_encoders(drm_dev, tvout);
 814
 815        return 0;
 816}
 817
 818static void sti_tvout_unbind(struct device *dev, struct device *master,
 819        void *data)
 820{
 821        struct sti_tvout *tvout = dev_get_drvdata(dev);
 822
 823        sti_tvout_destroy_encoders(tvout);
 824}
 825
 826static const struct component_ops sti_tvout_ops = {
 827        .bind   = sti_tvout_bind,
 828        .unbind = sti_tvout_unbind,
 829};
 830
 831static int sti_tvout_probe(struct platform_device *pdev)
 832{
 833        struct device *dev = &pdev->dev;
 834        struct device_node *node = dev->of_node;
 835        struct sti_tvout *tvout;
 836        struct resource *res;
 837
 838        DRM_INFO("%s\n", __func__);
 839
 840        if (!node)
 841                return -ENODEV;
 842
 843        tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
 844        if (!tvout)
 845                return -ENOMEM;
 846
 847        tvout->dev = dev;
 848
 849        /* get memory resources */
 850        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
 851        if (!res) {
 852                DRM_ERROR("Invalid glue resource\n");
 853                return -ENOMEM;
 854        }
 855        tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
 856        if (!tvout->regs)
 857                return -ENOMEM;
 858
 859        /* get reset resources */
 860        tvout->reset = devm_reset_control_get(dev, "tvout");
 861        /* take tvout out of reset */
 862        if (!IS_ERR(tvout->reset))
 863                reset_control_deassert(tvout->reset);
 864
 865        platform_set_drvdata(pdev, tvout);
 866
 867        return component_add(dev, &sti_tvout_ops);
 868}
 869
 870static int sti_tvout_remove(struct platform_device *pdev)
 871{
 872        component_del(&pdev->dev, &sti_tvout_ops);
 873        return 0;
 874}
 875
 876static const struct of_device_id tvout_of_match[] = {
 877        { .compatible = "st,stih407-tvout", },
 878        { /* end node */ }
 879};
 880MODULE_DEVICE_TABLE(of, tvout_of_match);
 881
 882struct platform_driver sti_tvout_driver = {
 883        .driver = {
 884                .name = "sti-tvout",
 885                .owner = THIS_MODULE,
 886                .of_match_table = tvout_of_match,
 887        },
 888        .probe = sti_tvout_probe,
 889        .remove = sti_tvout_remove,
 890};
 891
 892MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
 893MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
 894MODULE_LICENSE("GPL");
 895