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_puts(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_puts(s, "\n");
 562
 563        return 0;
 564}
 565
 566static struct drm_info_list tvout_debugfs_files[] = {
 567        { "tvout", tvout_dbg_show, 0, NULL },
 568};
 569
 570static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor)
 571{
 572        unsigned int i;
 573
 574        for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++)
 575                tvout_debugfs_files[i].data = tvout;
 576
 577        return drm_debugfs_create_files(tvout_debugfs_files,
 578                                        ARRAY_SIZE(tvout_debugfs_files),
 579                                        minor->debugfs_root, minor);
 580}
 581
 582static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)
 583{
 584}
 585
 586static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,
 587                                       struct drm_display_mode *mode,
 588                                       struct drm_display_mode *adjusted_mode)
 589{
 590}
 591
 592static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
 593{
 594        struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);
 595
 596        drm_encoder_cleanup(encoder);
 597        kfree(sti_encoder);
 598}
 599
 600static int sti_tvout_late_register(struct drm_encoder *encoder)
 601{
 602        struct sti_tvout *tvout = to_sti_tvout(encoder);
 603        int ret;
 604
 605        if (tvout->debugfs_registered)
 606                return 0;
 607
 608        ret = tvout_debugfs_init(tvout, encoder->dev->primary);
 609        if (ret)
 610                return ret;
 611
 612        tvout->debugfs_registered = true;
 613        return 0;
 614}
 615
 616static void sti_tvout_early_unregister(struct drm_encoder *encoder)
 617{
 618        struct sti_tvout *tvout = to_sti_tvout(encoder);
 619
 620        if (!tvout->debugfs_registered)
 621                return;
 622
 623        tvout->debugfs_registered = false;
 624}
 625
 626static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
 627        .destroy = sti_tvout_encoder_destroy,
 628        .late_register = sti_tvout_late_register,
 629        .early_unregister = sti_tvout_early_unregister,
 630};
 631
 632static void sti_dvo_encoder_enable(struct drm_encoder *encoder)
 633{
 634        struct sti_tvout *tvout = to_sti_tvout(encoder);
 635
 636        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 637
 638        tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc));
 639}
 640
 641static void sti_dvo_encoder_disable(struct drm_encoder *encoder)
 642{
 643        struct sti_tvout *tvout = to_sti_tvout(encoder);
 644
 645        /* Reset VIP register */
 646        tvout_write(tvout, 0x0, TVO_VIP_DVO);
 647}
 648
 649static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = {
 650        .dpms = sti_tvout_encoder_dpms,
 651        .mode_set = sti_tvout_encoder_mode_set,
 652        .enable = sti_dvo_encoder_enable,
 653        .disable = sti_dvo_encoder_disable,
 654};
 655
 656static struct drm_encoder *
 657sti_tvout_create_dvo_encoder(struct drm_device *dev,
 658                             struct sti_tvout *tvout)
 659{
 660        struct sti_tvout_encoder *encoder;
 661        struct drm_encoder *drm_encoder;
 662
 663        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 664        if (!encoder)
 665                return NULL;
 666
 667        encoder->tvout = tvout;
 668
 669        drm_encoder = (struct drm_encoder *)encoder;
 670
 671        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 672        drm_encoder->possible_clones = 1 << 0;
 673
 674        drm_encoder_init(dev, drm_encoder,
 675                         &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS,
 676                         NULL);
 677
 678        drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs);
 679
 680        return drm_encoder;
 681}
 682
 683static void sti_hda_encoder_enable(struct drm_encoder *encoder)
 684{
 685        struct sti_tvout *tvout = to_sti_tvout(encoder);
 686
 687        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 688
 689        tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc));
 690}
 691
 692static void sti_hda_encoder_disable(struct drm_encoder *encoder)
 693{
 694        struct sti_tvout *tvout = to_sti_tvout(encoder);
 695
 696        /* reset VIP register */
 697        tvout_write(tvout, 0x0, TVO_VIP_HDF);
 698
 699        /* power down HD DAC */
 700        tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF);
 701}
 702
 703static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {
 704        .dpms = sti_tvout_encoder_dpms,
 705        .mode_set = sti_tvout_encoder_mode_set,
 706        .commit = sti_hda_encoder_enable,
 707        .disable = sti_hda_encoder_disable,
 708};
 709
 710static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
 711                struct sti_tvout *tvout)
 712{
 713        struct sti_tvout_encoder *encoder;
 714        struct drm_encoder *drm_encoder;
 715
 716        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 717        if (!encoder)
 718                return NULL;
 719
 720        encoder->tvout = tvout;
 721
 722        drm_encoder = (struct drm_encoder *) encoder;
 723
 724        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 725        drm_encoder->possible_clones = 1 << 0;
 726
 727        drm_encoder_init(dev, drm_encoder,
 728                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL);
 729
 730        drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);
 731
 732        return drm_encoder;
 733}
 734
 735static void sti_hdmi_encoder_enable(struct drm_encoder *encoder)
 736{
 737        struct sti_tvout *tvout = to_sti_tvout(encoder);
 738
 739        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 740
 741        tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc));
 742}
 743
 744static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)
 745{
 746        struct sti_tvout *tvout = to_sti_tvout(encoder);
 747
 748        /* reset VIP register */
 749        tvout_write(tvout, 0x0, TVO_VIP_HDMI);
 750}
 751
 752static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {
 753        .dpms = sti_tvout_encoder_dpms,
 754        .mode_set = sti_tvout_encoder_mode_set,
 755        .commit = sti_hdmi_encoder_enable,
 756        .disable = sti_hdmi_encoder_disable,
 757};
 758
 759static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,
 760                struct sti_tvout *tvout)
 761{
 762        struct sti_tvout_encoder *encoder;
 763        struct drm_encoder *drm_encoder;
 764
 765        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 766        if (!encoder)
 767                return NULL;
 768
 769        encoder->tvout = tvout;
 770
 771        drm_encoder = (struct drm_encoder *) encoder;
 772
 773        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 774        drm_encoder->possible_clones = 1 << 1;
 775
 776        drm_encoder_init(dev, drm_encoder,
 777                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL);
 778
 779        drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);
 780
 781        return drm_encoder;
 782}
 783
 784static void sti_tvout_create_encoders(struct drm_device *dev,
 785                struct sti_tvout *tvout)
 786{
 787        tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);
 788        tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);
 789        tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout);
 790}
 791
 792static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
 793{
 794        if (tvout->hdmi)
 795                drm_encoder_cleanup(tvout->hdmi);
 796        tvout->hdmi = NULL;
 797
 798        if (tvout->hda)
 799                drm_encoder_cleanup(tvout->hda);
 800        tvout->hda = NULL;
 801
 802        if (tvout->dvo)
 803                drm_encoder_cleanup(tvout->dvo);
 804        tvout->dvo = NULL;
 805}
 806
 807static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
 808{
 809        struct sti_tvout *tvout = dev_get_drvdata(dev);
 810        struct drm_device *drm_dev = data;
 811
 812        tvout->drm_dev = drm_dev;
 813
 814        sti_tvout_create_encoders(drm_dev, tvout);
 815
 816        return 0;
 817}
 818
 819static void sti_tvout_unbind(struct device *dev, struct device *master,
 820        void *data)
 821{
 822        struct sti_tvout *tvout = dev_get_drvdata(dev);
 823
 824        sti_tvout_destroy_encoders(tvout);
 825}
 826
 827static const struct component_ops sti_tvout_ops = {
 828        .bind   = sti_tvout_bind,
 829        .unbind = sti_tvout_unbind,
 830};
 831
 832static int sti_tvout_probe(struct platform_device *pdev)
 833{
 834        struct device *dev = &pdev->dev;
 835        struct device_node *node = dev->of_node;
 836        struct sti_tvout *tvout;
 837        struct resource *res;
 838
 839        DRM_INFO("%s\n", __func__);
 840
 841        if (!node)
 842                return -ENODEV;
 843
 844        tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
 845        if (!tvout)
 846                return -ENOMEM;
 847
 848        tvout->dev = dev;
 849
 850        /* get Memory ressources */
 851        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
 852        if (!res) {
 853                DRM_ERROR("Invalid glue resource\n");
 854                return -ENOMEM;
 855        }
 856        tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
 857        if (!tvout->regs)
 858                return -ENOMEM;
 859
 860        /* get reset resources */
 861        tvout->reset = devm_reset_control_get(dev, "tvout");
 862        /* take tvout out of reset */
 863        if (!IS_ERR(tvout->reset))
 864                reset_control_deassert(tvout->reset);
 865
 866        platform_set_drvdata(pdev, tvout);
 867
 868        return component_add(dev, &sti_tvout_ops);
 869}
 870
 871static int sti_tvout_remove(struct platform_device *pdev)
 872{
 873        component_del(&pdev->dev, &sti_tvout_ops);
 874        return 0;
 875}
 876
 877static const struct of_device_id tvout_of_match[] = {
 878        { .compatible = "st,stih407-tvout", },
 879        { /* end node */ }
 880};
 881MODULE_DEVICE_TABLE(of, tvout_of_match);
 882
 883struct platform_driver sti_tvout_driver = {
 884        .driver = {
 885                .name = "sti-tvout",
 886                .owner = THIS_MODULE,
 887                .of_match_table = tvout_of_match,
 888        },
 889        .probe = sti_tvout_probe,
 890        .remove = sti_tvout_remove,
 891};
 892
 893MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
 894MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
 895MODULE_LICENSE("GPL");
 896