linux/drivers/gpu/drm/xlnx/xlnx_dsi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Xilinx FPGA MIPI DSI Tx Controller driver.
   4 *
   5 * Copyright (C) 2017 - 2018 Xilinx, Inc.
   6 *
   7 * Author : Saurabh Sengar <saurabhs@xilinx.com>
   8 *        : Siva Rajesh J <siva.rajesh.jarugula@xilinx.com>
   9 */
  10
  11#include <drm/drmP.h>
  12#include <drm/drm_atomic_helper.h>
  13#include <drm/drm_crtc.h>
  14#include <drm/drm_crtc_helper.h>
  15#include <drm/drm_fourcc.h>
  16#include <drm/drm_gem_cma_helper.h>
  17#include <drm/drm_mipi_dsi.h>
  18#include <drm/drm_panel.h>
  19#include <linux/component.h>
  20#include <linux/device.h>
  21#include <linux/of_device.h>
  22#include <linux/of_graph.h>
  23#include <linux/phy/phy.h>
  24#include <video/mipi_display.h>
  25#include <video/videomode.h>
  26
  27#include "xlnx_bridge.h"
  28
  29/* DSI Tx IP registers */
  30#define XDSI_CCR                        0x00
  31#define XDSI_CCR_COREENB                BIT(0)
  32#define XDSI_CCR_CRREADY                BIT(2)
  33#define XDSI_PCR                        0x04
  34#define XDSI_PCR_VIDEOMODE(x)           (((x) & 0x3) << 3)
  35#define XDSI_PCR_VIDEOMODE_MASK         (0x3 << 3)
  36#define XDSI_PCR_VIDEOMODE_SHIFT        3
  37#define XDSI_PCR_BLLPTYPE(x)            ((x) << 5)
  38#define XDSI_PCR_BLLPMODE(x)            ((x) << 6)
  39#define XDSI_PCR_EOTPENABLE(x)          ((x) << 13)
  40#define XDSI_GIER                       0x20
  41#define XDSI_ISR                        0x24
  42#define XDSI_IER                        0x28
  43#define XDSI_CMD                        0x30
  44#define XDSI_CMD_QUEUE_PACKET(x)        ((x) & GENMASK(23, 0))
  45#define XDSI_TIME1                      0x50
  46#define XDSI_TIME1_BLLP_BURST(x)        ((x) & GENMASK(15, 0))
  47#define XDSI_TIME1_HSA(x)               (((x) & GENMASK(15, 0)) << 16)
  48#define XDSI_TIME2                      0x54
  49#define XDSI_TIME2_VACT(x)              ((x) & GENMASK(15, 0))
  50#define XDSI_TIME2_HACT(x)              (((x) & GENMASK(15, 0)) << 16)
  51#define XDSI_HACT_MULTIPLIER            GENMASK(1, 0)
  52#define XDSI_TIME3                      0x58
  53#define XDSI_TIME3_HFP(x)               ((x) & GENMASK(15, 0))
  54#define XDSI_TIME3_HBP(x)               (((x) & GENMASK(15, 0)) << 16)
  55#define XDSI_TIME4                      0x5c
  56#define XDSI_TIME4_VFP(x)               ((x) & GENMASK(7, 0))
  57#define XDSI_TIME4_VBP(x)               (((x) & GENMASK(7, 0)) << 8)
  58#define XDSI_TIME4_VSA(x)               (((x) & GENMASK(7, 0)) << 16)
  59#define XDSI_LTIME                      0x60
  60#define XDSI_BLLP_TIME                  0x64
  61/*
  62 * XDSI_NUM_DATA_T represents number of data types in the
  63 * enum mipi_dsi_pixel_format in the MIPI DSI part of DRM framework.
  64 */
  65#define XDSI_NUM_DATA_T                 4
  66#define XDSI_VIDEO_MODE_SYNC_PULSE      0x0
  67#define XDSI_VIDEO_MODE_SYNC_EVENT      0x1
  68#define XDSI_VIDEO_MODE_BURST           0x2
  69
  70/**
  71 * struct xlnx_dsi - Core configuration DSI Tx subsystem device structure
  72 * @encoder: DRM encoder structure
  73 * @dsi_host: DSI host device
  74 * @connector: DRM connector structure
  75 * @panel_node: MIPI DSI device panel node
  76 * @panel:  DRM panel structure
  77 * @dev: device structure
  78 * @iomem: Base address of DSI subsystem
  79 * @lanes: number of active data lanes supported by DSI controller
  80 * @mode_flags: DSI operation mode related flags
  81 * @format: pixel format for video mode of DSI controller
  82 * @vm: videomode data structure
  83 * @mul_factor: multiplication factor for HACT timing parameter
  84 * @eotp_prop: configurable EoTP DSI parameter
  85 * @bllp_mode_prop: configurable BLLP mode DSI parameter
  86 * @bllp_type_prop: configurable BLLP type DSI parameter
  87 * @video_mode_prop: configurable Video mode DSI parameter
  88 * @bllp_burst_time_prop: Configurable BLLP time for burst mode
  89 * @cmd_queue_prop: configurable command queue
  90 * @eotp_prop_val: configurable EoTP DSI parameter value
  91 * @bllp_mode_prop_val: configurable BLLP mode DSI parameter value
  92 * @bllp_type_prop_val: configurable BLLP type DSI parameter value
  93 * @video_mode_prop_val: configurable Video mode DSI parameter value
  94 * @bllp_burst_time_prop_val: Configurable BLLP time for burst mode value
  95 * @cmd_queue_prop_val: configurable command queue value
  96 * @bridge: bridge structure
  97 * @height_out: configurable bridge output height parameter
  98 * @height_out_prop_val: configurable bridge output height parameter value
  99 * @width_out: configurable bridge output width parameter
 100 * @width_out_prop_val: configurable bridge output width parameter value
 101 * @in_fmt: configurable bridge input media format
 102 * @in_fmt_prop_val: configurable media bus format value
 103 * @out_fmt: configurable bridge output media format
 104 * @out_fmt_prop_val: configurable media bus format value
 105 */
 106struct xlnx_dsi {
 107        struct drm_encoder encoder;
 108        struct mipi_dsi_host dsi_host;
 109        struct drm_connector connector;
 110        struct device_node *panel_node;
 111        struct drm_panel *panel;
 112        struct device *dev;
 113        void __iomem *iomem;
 114        u32 lanes;
 115        u32 mode_flags;
 116        enum mipi_dsi_pixel_format format;
 117        struct videomode vm;
 118        u32 mul_factor;
 119        struct drm_property *eotp_prop;
 120        struct drm_property *bllp_mode_prop;
 121        struct drm_property *bllp_type_prop;
 122        struct drm_property *video_mode_prop;
 123        struct drm_property *bllp_burst_time_prop;
 124        struct drm_property *cmd_queue_prop;
 125        bool eotp_prop_val;
 126        bool bllp_mode_prop_val;
 127        bool bllp_type_prop_val;
 128        u32 video_mode_prop_val;
 129        u32 bllp_burst_time_prop_val;
 130        u32 cmd_queue_prop_val;
 131        struct xlnx_bridge *bridge;
 132        struct drm_property *height_out;
 133        u32 height_out_prop_val;
 134        struct drm_property *width_out;
 135        u32 width_out_prop_val;
 136        struct drm_property *in_fmt;
 137        u32 in_fmt_prop_val;
 138        struct drm_property *out_fmt;
 139        u32 out_fmt_prop_val;
 140};
 141
 142#define host_to_dsi(host) container_of(host, struct xlnx_dsi, dsi_host)
 143#define connector_to_dsi(c) container_of(c, struct xlnx_dsi, connector)
 144#define encoder_to_dsi(e) container_of(e, struct xlnx_dsi, encoder)
 145
 146static inline void xlnx_dsi_writel(void __iomem *base, int offset, u32 val)
 147{
 148        writel(val, base + offset);
 149}
 150
 151static inline u32 xlnx_dsi_readl(void __iomem *base, int offset)
 152{
 153        return readl(base + offset);
 154}
 155
 156/**
 157 * xlnx_dsi_set_config_parameters - Configure DSI Tx registers with parameters
 158 * given from user application.
 159 * @dsi: DSI structure having the updated user parameters
 160 *
 161 * This function takes the DSI structure having drm_property parameters
 162 * configured from  user application and writes them into DSI IP registers.
 163 */
 164static void xlnx_dsi_set_config_parameters(struct xlnx_dsi *dsi)
 165{
 166        u32 reg;
 167
 168        reg = XDSI_PCR_EOTPENABLE(dsi->eotp_prop_val);
 169        reg |= XDSI_PCR_VIDEOMODE(dsi->video_mode_prop_val);
 170        reg |= XDSI_PCR_BLLPTYPE(dsi->bllp_type_prop_val);
 171        reg |= XDSI_PCR_BLLPMODE(dsi->bllp_mode_prop_val);
 172
 173        xlnx_dsi_writel(dsi->iomem, XDSI_PCR, reg);
 174        /*
 175         * Configure the burst time if video mode is burst.
 176         * HSA of TIME1 register is ignored in this mode.
 177         */
 178        if (dsi->video_mode_prop_val == XDSI_VIDEO_MODE_BURST) {
 179                reg = XDSI_TIME1_BLLP_BURST(dsi->bllp_burst_time_prop_val);
 180                xlnx_dsi_writel(dsi->iomem, XDSI_TIME1, reg);
 181        }
 182
 183        reg = XDSI_CMD_QUEUE_PACKET(dsi->cmd_queue_prop_val);
 184        xlnx_dsi_writel(dsi->iomem, XDSI_CMD, reg);
 185
 186        dev_dbg(dsi->dev, "PCR register value is = %x\n",
 187                xlnx_dsi_readl(dsi->iomem, XDSI_PCR));
 188}
 189
 190/**
 191 * xlnx_dsi_set_display_mode - Configure DSI timing registers
 192 * @dsi: DSI structure having the updated user parameters
 193 *
 194 * This function writes the timing parameters of DSI IP which are
 195 * retrieved from panel timing values.
 196 */
 197static void xlnx_dsi_set_display_mode(struct xlnx_dsi *dsi)
 198{
 199        struct videomode *vm = &dsi->vm;
 200        u32 reg, video_mode;
 201
 202        reg = xlnx_dsi_readl(dsi->iomem, XDSI_PCR);
 203        video_mode = (reg & XDSI_PCR_VIDEOMODE_MASK) >>
 204                      XDSI_PCR_VIDEOMODE_SHIFT;
 205
 206        /* configure the HSA value only if non_burst_sync_pluse video mode */
 207        if (!video_mode &&
 208            (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) {
 209                reg = XDSI_TIME1_HSA(vm->hsync_len);
 210                xlnx_dsi_writel(dsi->iomem, XDSI_TIME1, reg);
 211        }
 212
 213        reg = XDSI_TIME4_VFP(vm->vfront_porch) |
 214              XDSI_TIME4_VBP(vm->vback_porch) |
 215              XDSI_TIME4_VSA(vm->vsync_len);
 216        xlnx_dsi_writel(dsi->iomem, XDSI_TIME4, reg);
 217
 218        reg = XDSI_TIME3_HFP(vm->hfront_porch) |
 219              XDSI_TIME3_HBP(vm->hback_porch);
 220        xlnx_dsi_writel(dsi->iomem, XDSI_TIME3, reg);
 221
 222        dev_dbg(dsi->dev, "mul factor for parsed datatype is = %d\n",
 223                (dsi->mul_factor) / 100);
 224        /*
 225         * The HACT parameter received from panel timing values should be
 226         * divisible by 4. The reason for this is, the word count given as
 227         * input to DSI controller is HACT * mul_factor. The mul_factor is
 228         * 3, 2.25, 2.25, 2 respectively for RGB888, RGB666_L, RGB666_P and
 229         * RGB565.
 230         * e.g. for RGB666_L color format and 1080p, the word count is
 231         * 1920*2.25 = 4320 which is divisible by 4 and it is a valid input
 232         * to DSI controller. Based on this 2.25 mul factor, we come up with
 233         * the division factor of (XDSI_HACT_MULTIPLIER) as 4 for checking
 234         */
 235        if ((vm->hactive & XDSI_HACT_MULTIPLIER) != 0)
 236                dev_warn(dsi->dev, "Incorrect HACT will be programmed\n");
 237
 238        reg = XDSI_TIME2_HACT((vm->hactive) * (dsi->mul_factor) / 100) |
 239              XDSI_TIME2_VACT(vm->vactive);
 240        xlnx_dsi_writel(dsi->iomem, XDSI_TIME2, reg);
 241
 242        dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive);
 243}
 244
 245/**
 246 * xlnx_dsi_set_display_enable - Enables the DSI Tx IP core enable
 247 * register bit
 248 * @dsi: DSI structure having the updated user parameters
 249 *
 250 * This function takes the DSI strucure and enables the core enable bit
 251 * of core configuration register.
 252 */
 253static void xlnx_dsi_set_display_enable(struct xlnx_dsi *dsi)
 254{
 255        u32 reg;
 256
 257        reg = xlnx_dsi_readl(dsi->iomem, XDSI_CCR);
 258        reg |= XDSI_CCR_COREENB;
 259
 260        xlnx_dsi_writel(dsi->iomem, XDSI_CCR, reg);
 261        dev_dbg(dsi->dev, "MIPI DSI Tx controller is enabled.\n");
 262}
 263
 264/**
 265 * xlnx_dsi_set_display_disable - Disable the DSI Tx IP core enable
 266 * register bit
 267 * @dsi: DSI structure having the updated user parameters
 268 *
 269 * This function takes the DSI strucure and disables the core enable bit
 270 * of core configuration register.
 271 */
 272static void xlnx_dsi_set_display_disable(struct xlnx_dsi *dsi)
 273{
 274        u32 reg;
 275
 276        reg = xlnx_dsi_readl(dsi->iomem, XDSI_CCR);
 277        reg &= ~XDSI_CCR_COREENB;
 278
 279        xlnx_dsi_writel(dsi->iomem, XDSI_CCR, reg);
 280        dev_dbg(dsi->dev, "DSI Tx is disabled. reset regs to default values\n");
 281}
 282
 283/**
 284 * xlnx_dsi_atomic_set_property - implementation of drm_connector_funcs
 285 * set_property invoked by IOCTL call to DRM_IOCTL_MODE_OBJ_SETPROPERTY
 286 *
 287 * @connector: pointer Xilinx DSI connector
 288 * @state: DRM connector state
 289 * @prop: pointer to the drm_property structure
 290 * @val: DSI parameter value that is configured from user application
 291 *
 292 * This function takes a drm_property name and value given from user application
 293 * and update the DSI structure property varabiles with the values.
 294 * These values are later used to configure the DSI Rx IP.
 295 *
 296 * Return: 0 on success OR -EINVAL if setting property fails
 297 */
 298static int xlnx_dsi_atomic_set_property(struct drm_connector *connector,
 299                                        struct drm_connector_state *state,
 300                                        struct drm_property *prop, u64 val)
 301{
 302        struct xlnx_dsi *dsi = connector_to_dsi(connector);
 303
 304        dev_dbg(dsi->dev, "property name = %s, value = %lld\n",
 305                prop->name, val);
 306
 307        if (prop == dsi->eotp_prop)
 308                dsi->eotp_prop_val = !!val;
 309        else if (prop == dsi->bllp_mode_prop)
 310                dsi->bllp_mode_prop_val = !!val;
 311        else if (prop == dsi->bllp_type_prop)
 312                dsi->bllp_type_prop_val = !!val;
 313        else if (prop == dsi->video_mode_prop)
 314                dsi->video_mode_prop_val = (unsigned int)val;
 315        else if (prop == dsi->bllp_burst_time_prop)
 316                dsi->bllp_burst_time_prop_val = (unsigned int)val;
 317        else if (prop == dsi->cmd_queue_prop)
 318                dsi->cmd_queue_prop_val = (unsigned int)val;
 319        else if (prop == dsi->height_out)
 320                dsi->height_out_prop_val = (u32)val;
 321        else if (prop == dsi->width_out)
 322                dsi->width_out_prop_val = (u32)val;
 323        else if (prop == dsi->in_fmt)
 324                dsi->in_fmt_prop_val = (u32)val;
 325        else if (prop == dsi->out_fmt)
 326                dsi->out_fmt_prop_val = (u32)val;
 327        else
 328                return -EINVAL;
 329
 330        xlnx_dsi_set_config_parameters(dsi);
 331
 332        return 0;
 333}
 334
 335static int
 336xlnx_dsi_atomic_get_property(struct drm_connector *connector,
 337                             const struct drm_connector_state *state,
 338                             struct drm_property *prop, uint64_t *val)
 339{
 340        struct xlnx_dsi *dsi = connector_to_dsi(connector);
 341
 342        if (prop == dsi->eotp_prop)
 343                *val = dsi->eotp_prop_val;
 344        else if (prop == dsi->bllp_mode_prop)
 345                *val = dsi->bllp_mode_prop_val;
 346        else if (prop == dsi->bllp_type_prop)
 347                *val = dsi->bllp_type_prop_val;
 348        else if (prop == dsi->video_mode_prop)
 349                *val = dsi->video_mode_prop_val;
 350        else if (prop == dsi->bllp_burst_time_prop)
 351                *val = dsi->bllp_burst_time_prop_val;
 352        else if (prop == dsi->cmd_queue_prop)
 353                *val = dsi->cmd_queue_prop_val;
 354        else if (prop == dsi->height_out)
 355                *val = dsi->height_out_prop_val;
 356        else if (prop == dsi->width_out)
 357                *val = dsi->width_out_prop_val;
 358        else if (prop == dsi->in_fmt)
 359                *val = dsi->in_fmt_prop_val;
 360        else if (prop == dsi->out_fmt)
 361                *val = dsi->out_fmt_prop_val;
 362        else
 363                return -EINVAL;
 364
 365        return 0;
 366}
 367
 368static int xlnx_dsi_host_attach(struct mipi_dsi_host *host,
 369                                struct mipi_dsi_device *device)
 370{
 371        u32 panel_lanes;
 372        struct xlnx_dsi *dsi = host_to_dsi(host);
 373
 374        panel_lanes = device->lanes;
 375        dsi->mode_flags = device->mode_flags;
 376        dsi->panel_node = device->dev.of_node;
 377
 378        if (panel_lanes != dsi->lanes) {
 379                dev_err(dsi->dev, "Mismatch of lanes. panel = %d, DSI = %d\n",
 380                        panel_lanes, dsi->lanes);
 381                return -EINVAL;
 382        }
 383
 384        if (dsi->lanes > 4 || dsi->lanes < 1) {
 385                dev_err(dsi->dev, "%d lanes : invalid xlnx,dsi-num-lanes\n",
 386                        dsi->lanes);
 387                return -EINVAL;
 388        }
 389
 390        if (device->format != dsi->format) {
 391                dev_err(dsi->dev, "Mismatch of format. panel = %d, DSI = %d\n",
 392                        device->format, dsi->format);
 393                return -EINVAL;
 394        }
 395
 396        if (dsi->connector.dev)
 397                drm_helper_hpd_irq_event(dsi->connector.dev);
 398
 399        return 0;
 400}
 401
 402static int xlnx_dsi_host_detach(struct mipi_dsi_host *host,
 403                                struct mipi_dsi_device *device)
 404{
 405        struct xlnx_dsi *dsi = host_to_dsi(host);
 406
 407        dsi->panel = NULL;
 408
 409        if (dsi->connector.dev)
 410                drm_helper_hpd_irq_event(dsi->connector.dev);
 411
 412        return 0;
 413}
 414
 415static const struct mipi_dsi_host_ops xlnx_dsi_ops = {
 416        .attach = xlnx_dsi_host_attach,
 417        .detach = xlnx_dsi_host_detach,
 418};
 419
 420static int xlnx_dsi_connector_dpms(struct drm_connector *connector, int mode)
 421{
 422        struct xlnx_dsi *dsi = connector_to_dsi(connector);
 423        int ret;
 424
 425        dev_dbg(dsi->dev, "connector dpms state: %d\n", mode);
 426
 427        switch (mode) {
 428        case DRM_MODE_DPMS_ON:
 429                ret = drm_panel_prepare(dsi->panel);
 430                if (ret < 0) {
 431                        dev_err(dsi->dev, "DRM panel not found\n");
 432                        return ret;
 433                }
 434
 435                ret = drm_panel_enable(dsi->panel);
 436                if (ret < 0) {
 437                        drm_panel_unprepare(dsi->panel);
 438                        dev_err(dsi->dev, "DRM panel not enabled\n");
 439                        return ret;
 440                }
 441                break;
 442        default:
 443                drm_panel_disable(dsi->panel);
 444                drm_panel_unprepare(dsi->panel);
 445                break;
 446        }
 447
 448        return drm_helper_connector_dpms(connector, mode);
 449}
 450
 451static enum drm_connector_status
 452xlnx_dsi_detect(struct drm_connector *connector, bool force)
 453{
 454        struct xlnx_dsi *dsi = connector_to_dsi(connector);
 455
 456        if (!dsi->panel) {
 457                dsi->panel = of_drm_find_panel(dsi->panel_node);
 458                if (dsi->panel)
 459                        drm_panel_attach(dsi->panel, &dsi->connector);
 460        } else if (!dsi->panel_node) {
 461                xlnx_dsi_connector_dpms(connector, DRM_MODE_DPMS_OFF);
 462                drm_panel_detach(dsi->panel);
 463                dsi->panel = NULL;
 464        }
 465
 466        if (dsi->panel)
 467                return connector_status_connected;
 468
 469        return connector_status_disconnected;
 470}
 471
 472static void xlnx_dsi_connector_destroy(struct drm_connector *connector)
 473{
 474        drm_connector_unregister(connector);
 475        drm_connector_cleanup(connector);
 476        connector->dev = NULL;
 477}
 478
 479static const struct drm_connector_funcs xlnx_dsi_connector_funcs = {
 480        .dpms = xlnx_dsi_connector_dpms,
 481        .detect = xlnx_dsi_detect,
 482        .fill_modes = drm_helper_probe_single_connector_modes,
 483        .destroy = xlnx_dsi_connector_destroy,
 484        .atomic_set_property = xlnx_dsi_atomic_set_property,
 485        .atomic_get_property = xlnx_dsi_atomic_get_property,
 486        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 487        .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
 488        .reset                  = drm_atomic_helper_connector_reset,
 489};
 490
 491static int xlnx_dsi_get_modes(struct drm_connector *connector)
 492{
 493        struct xlnx_dsi *dsi = connector_to_dsi(connector);
 494
 495        if (dsi->panel)
 496                return dsi->panel->funcs->get_modes(dsi->panel);
 497
 498        return 0;
 499}
 500
 501static struct drm_encoder *
 502xlnx_dsi_best_encoder(struct drm_connector *connector)
 503{
 504        return &(connector_to_dsi(connector)->encoder);
 505}
 506
 507static struct drm_connector_helper_funcs xlnx_dsi_connector_helper_funcs = {
 508        .get_modes = xlnx_dsi_get_modes,
 509        .best_encoder = xlnx_dsi_best_encoder,
 510};
 511
 512/**
 513 * xlnx_dsi_connector_create_property -  create DSI connector properties
 514 *
 515 * @connector: pointer to Xilinx DSI connector
 516 *
 517 * This function takes the xilinx DSI connector component and defines
 518 * the drm_property variables with their default values.
 519 */
 520static void xlnx_dsi_connector_create_property(struct drm_connector *connector)
 521{
 522        struct drm_device *dev = connector->dev;
 523        struct xlnx_dsi *dsi  = connector_to_dsi(connector);
 524
 525        dsi->eotp_prop = drm_property_create_bool(dev, 1, "eotp");
 526        dsi->video_mode_prop = drm_property_create_range(dev, 0, "video_mode",
 527                                                         0, 2);
 528        dsi->bllp_mode_prop = drm_property_create_bool(dev, 0, "bllp_mode");
 529        dsi->bllp_type_prop = drm_property_create_bool(dev, 0, "bllp_type");
 530        dsi->bllp_burst_time_prop =
 531                drm_property_create_range(dev, 0, "bllp_burst_time", 0, 0xFFFF);
 532        dsi->cmd_queue_prop = drm_property_create_range(dev, 0, "cmd_queue", 0,
 533                                                        0xffffff);
 534        dsi->height_out = drm_property_create_range(dev, 0, "height_out",
 535                                                    2, 4096);
 536        dsi->width_out = drm_property_create_range(dev, 0, "width_out",
 537                                                   2, 4096);
 538        dsi->in_fmt = drm_property_create_range(dev, 0, "in_fmt", 0, 16384);
 539        dsi->out_fmt = drm_property_create_range(dev, 0, "out_fmt", 0, 16384);
 540}
 541
 542/**
 543 * xlnx_dsi_connector_attach_property -  attach DSI connector
 544 * properties
 545 *
 546 * @connector: pointer to Xilinx DSI connector
 547 */
 548static void xlnx_dsi_connector_attach_property(struct drm_connector *connector)
 549{
 550        struct xlnx_dsi *dsi = connector_to_dsi(connector);
 551        struct drm_mode_object *obj = &connector->base;
 552
 553        if (dsi->eotp_prop)
 554                drm_object_attach_property(obj, dsi->eotp_prop, 1);
 555
 556        if (dsi->video_mode_prop)
 557                drm_object_attach_property(obj, dsi->video_mode_prop, 0);
 558
 559        if (dsi->bllp_burst_time_prop)
 560                drm_object_attach_property(&connector->base,
 561                                           dsi->bllp_burst_time_prop, 0);
 562
 563        if (dsi->bllp_mode_prop)
 564                drm_object_attach_property(&connector->base,
 565                                           dsi->bllp_mode_prop, 0);
 566
 567        if (dsi->bllp_type_prop)
 568                drm_object_attach_property(&connector->base,
 569                                           dsi->bllp_type_prop, 0);
 570
 571        if (dsi->cmd_queue_prop)
 572                drm_object_attach_property(&connector->base,
 573                                           dsi->cmd_queue_prop, 0);
 574
 575        if (dsi->height_out)
 576                drm_object_attach_property(obj, dsi->height_out, 0);
 577
 578        if (dsi->width_out)
 579                drm_object_attach_property(obj, dsi->width_out, 0);
 580
 581        if (dsi->in_fmt)
 582                drm_object_attach_property(obj, dsi->in_fmt, 0);
 583
 584        if (dsi->out_fmt)
 585                drm_object_attach_property(obj, dsi->out_fmt, 0);
 586}
 587
 588static int xlnx_dsi_create_connector(struct drm_encoder *encoder)
 589{
 590        struct xlnx_dsi *dsi = encoder_to_dsi(encoder);
 591        struct drm_connector *connector = &dsi->connector;
 592        int ret;
 593
 594        connector->polled = DRM_CONNECTOR_POLL_HPD;
 595
 596        ret = drm_connector_init(encoder->dev, connector,
 597                                 &xlnx_dsi_connector_funcs,
 598                                 DRM_MODE_CONNECTOR_DSI);
 599        if (ret) {
 600                dev_err(dsi->dev, "Failed to initialize connector with drm\n");
 601                return ret;
 602        }
 603
 604        drm_connector_helper_add(connector, &xlnx_dsi_connector_helper_funcs);
 605        drm_connector_register(connector);
 606        drm_mode_connector_attach_encoder(connector, encoder);
 607        xlnx_dsi_connector_create_property(connector);
 608        xlnx_dsi_connector_attach_property(connector);
 609
 610        return 0;
 611}
 612
 613/**
 614 * xlnx_dsi_atomic_mode_set -  derive the DSI timing parameters
 615 *
 616 * @encoder: pointer to Xilinx DRM encoder
 617 * @crtc_state: Pointer to drm core crtc state
 618 * @connector_state: DSI connector drm state
 619 *
 620 * This function derives the DSI IP timing parameters from the timing
 621 * values given in the attached panel driver.
 622 */
 623static void
 624xlnx_dsi_atomic_mode_set(struct drm_encoder *encoder,
 625                         struct drm_crtc_state *crtc_state,
 626                                 struct drm_connector_state *connector_state)
 627{
 628        struct xlnx_dsi *dsi = encoder_to_dsi(encoder);
 629        struct videomode *vm = &dsi->vm;
 630        struct drm_display_mode *m = &crtc_state->adjusted_mode;
 631
 632        /* Set bridge input and output parameters */
 633        xlnx_bridge_set_input(dsi->bridge, m->hdisplay, m->vdisplay,
 634                              dsi->in_fmt_prop_val);
 635        xlnx_bridge_set_output(dsi->bridge, dsi->width_out_prop_val,
 636                               dsi->height_out_prop_val,
 637                               dsi->out_fmt_prop_val);
 638        xlnx_bridge_enable(dsi->bridge);
 639
 640        vm->hactive = m->hdisplay;
 641        vm->vactive = m->vdisplay;
 642        vm->vfront_porch = m->vsync_start - m->vdisplay;
 643        vm->vback_porch = m->vtotal - m->vsync_end;
 644        vm->vsync_len = m->vsync_end - m->vsync_start;
 645        vm->hfront_porch = m->hsync_start - m->hdisplay;
 646        vm->hback_porch = m->htotal - m->hsync_end;
 647        vm->hsync_len = m->hsync_end - m->hsync_start;
 648        xlnx_dsi_set_display_mode(dsi);
 649}
 650
 651static void xlnx_dsi_disable(struct drm_encoder *encoder)
 652{
 653        struct xlnx_dsi *dsi = encoder_to_dsi(encoder);
 654
 655        if (dsi->bridge)
 656                xlnx_bridge_disable(dsi->bridge);
 657
 658        xlnx_dsi_set_display_disable(dsi);
 659}
 660
 661static void xlnx_dsi_enable(struct drm_encoder *encoder)
 662{
 663        struct xlnx_dsi *dsi = encoder_to_dsi(encoder);
 664
 665        xlnx_dsi_set_display_enable(dsi);
 666}
 667
 668static const struct drm_encoder_helper_funcs xlnx_dsi_encoder_helper_funcs = {
 669        .atomic_mode_set = xlnx_dsi_atomic_mode_set,
 670        .enable = xlnx_dsi_enable,
 671        .disable = xlnx_dsi_disable,
 672};
 673
 674static const struct drm_encoder_funcs xlnx_dsi_encoder_funcs = {
 675        .destroy = drm_encoder_cleanup,
 676};
 677
 678static int xlnx_dsi_parse_dt(struct xlnx_dsi *dsi)
 679{
 680        struct device *dev = dsi->dev;
 681        struct device_node *node = dev->of_node;
 682        int ret;
 683        u32 datatype;
 684        static const int xdsi_mul_fact[XDSI_NUM_DATA_T] = {300, 225, 225, 200};
 685        /*
 686         * Used as a multiplication factor for HACT based on used
 687         * DSI data type.
 688         *
 689         * e.g. for RGB666_L datatype and 1920x1080 resolution,
 690         * the Hact (WC) would be as follows -
 691         * 1920 pixels * 18 bits per pixel / 8 bits per byte
 692         * = 1920 pixels * 2.25 bytes per pixel = 4320 bytes.
 693         *
 694         * Data Type - Multiplication factor
 695         * RGB888    - 3
 696         * RGB666_L  - 2.25
 697-        * RGB666_P  - 2.25
 698         * RGB565    - 2
 699         *
 700         * Since the multiplication factor maybe a floating number,
 701         * a 100x multiplication factor is used.
 702         */
 703        ret = of_property_read_u32(node, "xlnx,dsi-num-lanes", &dsi->lanes);
 704        if (ret < 0) {
 705                dev_err(dsi->dev, "missing xlnx,dsi-num-lanes property\n");
 706                return ret;
 707        }
 708        if (dsi->lanes > 4 || dsi->lanes < 1) {
 709                dev_err(dsi->dev, "%d lanes : invalid lanes\n", dsi->lanes);
 710                return -EINVAL;
 711        }
 712        ret = of_property_read_u32(node, "xlnx,dsi-data-type", &datatype);
 713        if (ret < 0) {
 714                dev_err(dsi->dev, "missing xlnx,dsi-data-type property\n");
 715                return ret;
 716        }
 717        dsi->format = datatype;
 718        if (datatype > MIPI_DSI_FMT_RGB565) {
 719                dev_err(dsi->dev, "Invalid xlnx,dsi-data-type string\n");
 720                return -EINVAL;
 721        }
 722        dsi->mul_factor = xdsi_mul_fact[datatype];
 723        dev_dbg(dsi->dev, "DSI controller num lanes = %d", dsi->lanes);
 724        dev_dbg(dsi->dev, "DSI controller datatype = %d\n", datatype);
 725
 726        return 0;
 727}
 728
 729static int xlnx_dsi_bind(struct device *dev, struct device *master,
 730                         void *data)
 731{
 732        struct xlnx_dsi *dsi = dev_get_drvdata(dev);
 733        struct drm_encoder *encoder = &dsi->encoder;
 734        struct drm_device *drm_dev = data;
 735        int ret;
 736
 737        /*
 738         * TODO: The possible CRTCs are 1 now as per current implementation of
 739         * DSI tx drivers. DRM framework can support more than one CRTCs and
 740         * DSI driver can be enhanced for that.
 741         */
 742        encoder->possible_crtcs = 1;
 743        drm_encoder_init(drm_dev, encoder, &xlnx_dsi_encoder_funcs,
 744                         DRM_MODE_ENCODER_DSI, NULL);
 745        drm_encoder_helper_add(encoder, &xlnx_dsi_encoder_helper_funcs);
 746        ret = xlnx_dsi_create_connector(encoder);
 747        if (ret) {
 748                dev_err(dsi->dev, "fail creating connector, ret = %d\n", ret);
 749                drm_encoder_cleanup(encoder);
 750                return ret;
 751        }
 752        ret = mipi_dsi_host_register(&dsi->dsi_host);
 753        if (ret) {
 754                xlnx_dsi_connector_destroy(&dsi->connector);
 755                drm_encoder_cleanup(encoder);
 756                return ret;
 757        }
 758        return 0;
 759}
 760
 761static void xlnx_dsi_unbind(struct device *dev, struct device *master,
 762                            void *data)
 763{
 764        struct xlnx_dsi *dsi = dev_get_drvdata(dev);
 765
 766        xlnx_dsi_disable(&dsi->encoder);
 767        mipi_dsi_host_unregister(&dsi->dsi_host);
 768        xlnx_bridge_disable(dsi->bridge);
 769}
 770
 771static const struct component_ops xlnx_dsi_component_ops = {
 772        .bind   = xlnx_dsi_bind,
 773        .unbind = xlnx_dsi_unbind,
 774};
 775
 776static int xlnx_dsi_probe(struct platform_device *pdev)
 777{
 778        struct device *dev = &pdev->dev;
 779        struct resource *res;
 780        struct xlnx_dsi *dsi;
 781        struct device_node *vpss_node;
 782        int ret;
 783
 784        dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
 785        if (!dsi)
 786                return -ENOMEM;
 787
 788        dsi->dsi_host.ops = &xlnx_dsi_ops;
 789        dsi->dsi_host.dev = dev;
 790        dsi->dev = dev;
 791
 792        ret = xlnx_dsi_parse_dt(dsi);
 793        if (ret)
 794                return ret;
 795
 796        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 797        dsi->iomem = devm_ioremap_resource(dev, res);
 798        if (IS_ERR(dsi->iomem))
 799                return PTR_ERR(dsi->iomem);
 800
 801        platform_set_drvdata(pdev, dsi);
 802
 803        /* Bridge support */
 804        vpss_node = of_parse_phandle(dsi->dev->of_node, "xlnx,vpss", 0);
 805        if (vpss_node) {
 806                dsi->bridge = of_xlnx_bridge_get(vpss_node);
 807                if (!dsi->bridge) {
 808                        dev_info(dsi->dev, "Didn't get bridge instance\n");
 809                        return -EPROBE_DEFER;
 810                }
 811        }
 812
 813        return component_add(dev, &xlnx_dsi_component_ops);
 814}
 815
 816static int xlnx_dsi_remove(struct platform_device *pdev)
 817{
 818        component_del(&pdev->dev, &xlnx_dsi_component_ops);
 819
 820        return 0;
 821}
 822
 823static const struct of_device_id xlnx_dsi_of_match[] = {
 824        { .compatible = "xlnx,dsi"},
 825        { }
 826};
 827MODULE_DEVICE_TABLE(of, xlnx_dsi_of_match);
 828
 829static struct platform_driver dsi_driver = {
 830        .probe = xlnx_dsi_probe,
 831        .remove = xlnx_dsi_remove,
 832        .driver = {
 833                .name = "xlnx-dsi",
 834                .of_match_table = xlnx_dsi_of_match,
 835        },
 836};
 837
 838module_platform_driver(dsi_driver);
 839
 840MODULE_AUTHOR("Siva Rajesh <sivaraj@xilinx.com>");
 841MODULE_DESCRIPTION("Xilinx FPGA MIPI DSI Tx Driver");
 842MODULE_LICENSE("GPL v2");
 843