linux/drivers/gpu/drm/mcde/mcde_dsi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2#include <linux/clk.h>
   3#include <linux/component.h>
   4#include <linux/delay.h>
   5#include <linux/io.h>
   6#include <linux/mfd/syscon.h>
   7#include <linux/module.h>
   8#include <linux/of.h>
   9#include <linux/platform_device.h>
  10#include <linux/regmap.h>
  11#include <linux/regulator/consumer.h>
  12#include <video/mipi_display.h>
  13
  14#include <drm/drm_atomic_helper.h>
  15#include <drm/drm_bridge.h>
  16#include <drm/drm_device.h>
  17#include <drm/drm_drv.h>
  18#include <drm/drm_encoder.h>
  19#include <drm/drm_mipi_dsi.h>
  20#include <drm/drm_modeset_helper_vtables.h>
  21#include <drm/drm_of.h>
  22#include <drm/drm_panel.h>
  23#include <drm/drm_print.h>
  24#include <drm/drm_probe_helper.h>
  25
  26#include "mcde_drm.h"
  27#include "mcde_dsi_regs.h"
  28
  29#define DSI_DEFAULT_LP_FREQ_HZ  19200000
  30#define DSI_DEFAULT_HS_FREQ_HZ  420160000
  31
  32/* PRCMU DSI reset registers */
  33#define PRCM_DSI_SW_RESET 0x324
  34#define PRCM_DSI_SW_RESET_DSI0_SW_RESETN BIT(0)
  35#define PRCM_DSI_SW_RESET_DSI1_SW_RESETN BIT(1)
  36#define PRCM_DSI_SW_RESET_DSI2_SW_RESETN BIT(2)
  37
  38struct mcde_dsi {
  39        struct device *dev;
  40        struct mcde *mcde;
  41        struct drm_bridge bridge;
  42        struct drm_connector connector;
  43        struct drm_panel *panel;
  44        struct drm_bridge *bridge_out;
  45        struct mipi_dsi_host dsi_host;
  46        struct mipi_dsi_device *mdsi;
  47        struct clk *hs_clk;
  48        struct clk *lp_clk;
  49        unsigned long hs_freq;
  50        unsigned long lp_freq;
  51        bool unused;
  52
  53        void __iomem *regs;
  54        struct regmap *prcmu;
  55};
  56
  57static inline struct mcde_dsi *bridge_to_mcde_dsi(struct drm_bridge *bridge)
  58{
  59        return container_of(bridge, struct mcde_dsi, bridge);
  60}
  61
  62static inline struct mcde_dsi *host_to_mcde_dsi(struct mipi_dsi_host *h)
  63{
  64        return container_of(h, struct mcde_dsi, dsi_host);
  65}
  66
  67static inline struct mcde_dsi *connector_to_mcde_dsi(struct drm_connector *c)
  68{
  69        return container_of(c, struct mcde_dsi, connector);
  70}
  71
  72bool mcde_dsi_irq(struct mipi_dsi_device *mdsi)
  73{
  74        struct mcde_dsi *d;
  75        u32 val;
  76        bool te_received = false;
  77
  78        d = host_to_mcde_dsi(mdsi->host);
  79
  80        dev_dbg(d->dev, "%s called\n", __func__);
  81
  82        val = readl(d->regs + DSI_DIRECT_CMD_STS_FLAG);
  83        if (val)
  84                dev_dbg(d->dev, "DSI_DIRECT_CMD_STS_FLAG = %08x\n", val);
  85        if (val & DSI_DIRECT_CMD_STS_WRITE_COMPLETED)
  86                dev_dbg(d->dev, "direct command write completed\n");
  87        if (val & DSI_DIRECT_CMD_STS_TE_RECEIVED) {
  88                te_received = true;
  89                dev_dbg(d->dev, "direct command TE received\n");
  90        }
  91        if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED)
  92                dev_err(d->dev, "direct command ACK ERR received\n");
  93        if (val & DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR)
  94                dev_err(d->dev, "direct command read ERR received\n");
  95        /* Mask off the ACK value and clear status */
  96        writel(val, d->regs + DSI_DIRECT_CMD_STS_CLR);
  97
  98        val = readl(d->regs + DSI_CMD_MODE_STS_FLAG);
  99        if (val)
 100                dev_dbg(d->dev, "DSI_CMD_MODE_STS_FLAG = %08x\n", val);
 101        if (val & DSI_CMD_MODE_STS_ERR_NO_TE)
 102                /* This happens all the time (safe to ignore) */
 103                dev_dbg(d->dev, "CMD mode no TE\n");
 104        if (val & DSI_CMD_MODE_STS_ERR_TE_MISS)
 105                /* This happens all the time (safe to ignore) */
 106                dev_dbg(d->dev, "CMD mode TE miss\n");
 107        if (val & DSI_CMD_MODE_STS_ERR_SDI1_UNDERRUN)
 108                dev_err(d->dev, "CMD mode SD1 underrun\n");
 109        if (val & DSI_CMD_MODE_STS_ERR_SDI2_UNDERRUN)
 110                dev_err(d->dev, "CMD mode SD2 underrun\n");
 111        if (val & DSI_CMD_MODE_STS_ERR_UNWANTED_RD)
 112                dev_err(d->dev, "CMD mode unwanted RD\n");
 113        writel(val, d->regs + DSI_CMD_MODE_STS_CLR);
 114
 115        val = readl(d->regs + DSI_DIRECT_CMD_RD_STS_FLAG);
 116        if (val)
 117                dev_dbg(d->dev, "DSI_DIRECT_CMD_RD_STS_FLAG = %08x\n", val);
 118        writel(val, d->regs + DSI_DIRECT_CMD_RD_STS_CLR);
 119
 120        val = readl(d->regs + DSI_TG_STS_FLAG);
 121        if (val)
 122                dev_dbg(d->dev, "DSI_TG_STS_FLAG = %08x\n", val);
 123        writel(val, d->regs + DSI_TG_STS_CLR);
 124
 125        val = readl(d->regs + DSI_VID_MODE_STS_FLAG);
 126        if (val)
 127                dev_err(d->dev, "some video mode error status\n");
 128        writel(val, d->regs + DSI_VID_MODE_STS_CLR);
 129
 130        return te_received;
 131}
 132
 133static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
 134                                struct mipi_dsi_device *mdsi)
 135{
 136        struct mcde_dsi *d = host_to_mcde_dsi(host);
 137
 138        if (mdsi->lanes < 1 || mdsi->lanes > 2) {
 139                DRM_ERROR("dsi device params invalid, 1 or 2 lanes supported\n");
 140                return -EINVAL;
 141        }
 142
 143        dev_info(d->dev, "attached DSI device with %d lanes\n", mdsi->lanes);
 144        /* MIPI_DSI_FMT_RGB88 etc */
 145        dev_info(d->dev, "format %08x, %dbpp\n", mdsi->format,
 146                 mipi_dsi_pixel_format_to_bpp(mdsi->format));
 147        dev_info(d->dev, "mode flags: %08lx\n", mdsi->mode_flags);
 148
 149        d->mdsi = mdsi;
 150        if (d->mcde)
 151                d->mcde->mdsi = mdsi;
 152
 153        return 0;
 154}
 155
 156static int mcde_dsi_host_detach(struct mipi_dsi_host *host,
 157                                struct mipi_dsi_device *mdsi)
 158{
 159        struct mcde_dsi *d = host_to_mcde_dsi(host);
 160
 161        d->mdsi = NULL;
 162        if (d->mcde)
 163                d->mcde->mdsi = NULL;
 164
 165        return 0;
 166}
 167
 168#define MCDE_DSI_HOST_IS_READ(type)                         \
 169        ((type == MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM) || \
 170         (type == MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM) || \
 171         (type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM) || \
 172         (type == MIPI_DSI_DCS_READ))
 173
 174static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
 175                                      const struct mipi_dsi_msg *msg)
 176{
 177        struct mcde_dsi *d = host_to_mcde_dsi(host);
 178        const u32 loop_delay_us = 10; /* us */
 179        const u8 *tx = msg->tx_buf;
 180        u32 loop_counter;
 181        size_t txlen;
 182        u32 val;
 183        int ret;
 184        int i;
 185
 186        txlen = msg->tx_len;
 187        if (txlen > 12) {
 188                dev_err(d->dev,
 189                        "dunno how to write more than 12 bytes yet\n");
 190                return -EIO;
 191        }
 192
 193        dev_dbg(d->dev,
 194                "message to channel %d, %zd bytes",
 195                msg->channel,
 196                txlen);
 197
 198        /* Command "nature" */
 199        if (MCDE_DSI_HOST_IS_READ(msg->type))
 200                /* MCTL_MAIN_DATA_CTL already set up */
 201                val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ;
 202        else
 203                val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE;
 204        /*
 205         * More than 2 bytes will not fit in a single packet, so it's
 206         * time to set the "long not short" bit. One byte is used by
 207         * the MIPI DCS command leaving just one byte for the payload
 208         * in a short package.
 209         */
 210        if (mipi_dsi_packet_format_is_long(msg->type))
 211                val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT;
 212        val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT;
 213        /* Add one to the length for the MIPI DCS command */
 214        val |= txlen
 215                << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT;
 216        val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN;
 217        val |= msg->type << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT;
 218        writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS);
 219
 220        /* MIPI DCS command is part of the data */
 221        if (txlen > 0) {
 222                val = 0;
 223                for (i = 0; i < 4 && i < txlen; i++)
 224                        val |= tx[i] << (i & 3) * 8;
 225        }
 226        writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0);
 227        if (txlen > 4) {
 228                val = 0;
 229                for (i = 0; i < 4 && (i + 4) < txlen; i++)
 230                        val |= tx[i + 4] << (i & 3) * 8;
 231                writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1);
 232        }
 233        if (txlen > 8) {
 234                val = 0;
 235                for (i = 0; i < 4 && (i + 8) < txlen; i++)
 236                        val |= tx[i + 8] << (i & 3) * 8;
 237                writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2);
 238        }
 239        if (txlen > 12) {
 240                val = 0;
 241                for (i = 0; i < 4 && (i + 12) < txlen; i++)
 242                        val |= tx[i + 12] << (i & 3) * 8;
 243                writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3);
 244        }
 245
 246        writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
 247        writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
 248        /* Send command */
 249        writel(1, d->regs + DSI_DIRECT_CMD_SEND);
 250
 251        loop_counter = 1000 * 1000 / loop_delay_us;
 252        while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
 253                 DSI_DIRECT_CMD_STS_WRITE_COMPLETED)
 254               && --loop_counter)
 255                usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
 256
 257        if (!loop_counter) {
 258                dev_err(d->dev, "DSI write timeout!\n");
 259                return -ETIME;
 260        }
 261
 262        val = readl(d->regs + DSI_DIRECT_CMD_STS);
 263        if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) {
 264                val >>= DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT;
 265                dev_err(d->dev, "error during transmission: %04x\n",
 266                        val);
 267                return -EIO;
 268        }
 269
 270        if (!MCDE_DSI_HOST_IS_READ(msg->type)) {
 271                /* Return number of bytes written */
 272                if (mipi_dsi_packet_format_is_long(msg->type))
 273                        ret = 4 + txlen;
 274                else
 275                        ret = 4;
 276        } else {
 277                /* OK this is a read command, get the response */
 278                u32 rdsz;
 279                u32 rddat;
 280                u8 *rx = msg->rx_buf;
 281
 282                rdsz = readl(d->regs + DSI_DIRECT_CMD_RD_PROPERTY);
 283                rdsz &= DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK;
 284                rddat = readl(d->regs + DSI_DIRECT_CMD_RDDAT);
 285                for (i = 0; i < 4 && i < rdsz; i++)
 286                        rx[i] = (rddat >> (i * 8)) & 0xff;
 287                ret = rdsz;
 288        }
 289
 290        writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
 291        writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
 292
 293        return ret;
 294}
 295
 296static const struct mipi_dsi_host_ops mcde_dsi_host_ops = {
 297        .attach = mcde_dsi_host_attach,
 298        .detach = mcde_dsi_host_detach,
 299        .transfer = mcde_dsi_host_transfer,
 300};
 301
 302/* This sends a direct (short) command to request TE */
 303void mcde_dsi_te_request(struct mipi_dsi_device *mdsi)
 304{
 305        struct mcde_dsi *d;
 306        u32 val;
 307
 308        d = host_to_mcde_dsi(mdsi->host);
 309
 310        /* Command "nature" TE request */
 311        val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_TE_REQ;
 312        val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT;
 313        val |= 2 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT;
 314        val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN;
 315        val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_1 <<
 316                DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT;
 317        writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS);
 318
 319        /* Clear TE reveived and error status bits and enables them */
 320        writel(DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR |
 321               DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR,
 322               d->regs + DSI_DIRECT_CMD_STS_CLR);
 323        val = readl(d->regs + DSI_DIRECT_CMD_STS_CTL);
 324        val |= DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EN;
 325        val |= DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EN;
 326        writel(val, d->regs + DSI_DIRECT_CMD_STS_CTL);
 327
 328        /* Clear and enable no TE or TE missing status */
 329        writel(DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR |
 330               DSI_CMD_MODE_STS_CLR_ERR_TE_MISS_CLR,
 331               d->regs + DSI_CMD_MODE_STS_CLR);
 332        val = readl(d->regs + DSI_CMD_MODE_STS_CTL);
 333        val |= DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EN;
 334        val |= DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EN;
 335        writel(val, d->regs + DSI_CMD_MODE_STS_CTL);
 336
 337        /* Send this TE request command */
 338        writel(1, d->regs + DSI_DIRECT_CMD_SEND);
 339}
 340
 341static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
 342                                      const struct drm_display_mode *mode)
 343{
 344        u8 bpp = mipi_dsi_pixel_format_to_bpp(d->mdsi->format);
 345        u64 bpl;
 346        u32 hfp;
 347        u32 hbp;
 348        u32 hsa;
 349        u32 blkline_pck, line_duration;
 350        u32 blkeol_pck, blkeol_duration;
 351        u32 val;
 352
 353        val = 0;
 354        if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
 355                val |= DSI_VID_MAIN_CTL_BURST_MODE;
 356        if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
 357                val |= DSI_VID_MAIN_CTL_SYNC_PULSE_ACTIVE;
 358                val |= DSI_VID_MAIN_CTL_SYNC_PULSE_HORIZONTAL;
 359        }
 360        /* RGB header and pixel mode */
 361        switch (d->mdsi->format) {
 362        case MIPI_DSI_FMT_RGB565:
 363                val |= MIPI_DSI_PACKED_PIXEL_STREAM_16 <<
 364                        DSI_VID_MAIN_CTL_HEADER_SHIFT;
 365                val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_16BITS;
 366                break;
 367        case MIPI_DSI_FMT_RGB666_PACKED:
 368                val |= MIPI_DSI_PACKED_PIXEL_STREAM_18 <<
 369                        DSI_VID_MAIN_CTL_HEADER_SHIFT;
 370                val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS;
 371                break;
 372        case MIPI_DSI_FMT_RGB666:
 373                val |= MIPI_DSI_PIXEL_STREAM_3BYTE_18
 374                        << DSI_VID_MAIN_CTL_HEADER_SHIFT;
 375                val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS_LOOSE;
 376                break;
 377        case MIPI_DSI_FMT_RGB888:
 378                val |= MIPI_DSI_PACKED_PIXEL_STREAM_24 <<
 379                        DSI_VID_MAIN_CTL_HEADER_SHIFT;
 380                val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_24BITS;
 381                break;
 382        default:
 383                dev_err(d->dev, "unknown pixel mode\n");
 384                return;
 385        }
 386
 387        /* TODO: TVG could be enabled here */
 388
 389        /* Send blanking packet */
 390        val |= DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_0;
 391        /* Send EOL packet */
 392        val |= DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0;
 393        /* Recovery mode 1 */
 394        val |= 1 << DSI_VID_MAIN_CTL_RECOVERY_MODE_SHIFT;
 395        /* All other fields zero */
 396        writel(val, d->regs + DSI_VID_MAIN_CTL);
 397
 398        /* Vertical frame parameters are pretty straight-forward */
 399        val = mode->vdisplay << DSI_VID_VSIZE_VSA_LENGTH_SHIFT;
 400        /* vertical front porch */
 401        val |= (mode->vsync_start - mode->vdisplay)
 402                << DSI_VID_VSIZE_VFP_LENGTH_SHIFT;
 403        /* vertical sync active */
 404        val |= (mode->vsync_end - mode->vsync_start)
 405                << DSI_VID_VSIZE_VACT_LENGTH_SHIFT;
 406        /* vertical back porch */
 407        val |= (mode->vtotal - mode->vsync_end)
 408                << DSI_VID_VSIZE_VBP_LENGTH_SHIFT;
 409        writel(val, d->regs + DSI_VID_VSIZE);
 410
 411        /*
 412         * Horizontal frame parameters:
 413         * horizontal resolution is given in pixels and must be re-calculated
 414         * into bytes since this is what the hardware expects.
 415         *
 416         * 6 + 2 is HFP header + checksum
 417         */
 418        hfp = (mode->hsync_start - mode->hdisplay) * bpp - 6 - 2;
 419        if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
 420                /*
 421                 * 6 is HBP header + checksum
 422                 * 4 is RGB header + checksum
 423                 */
 424                hbp = (mode->htotal - mode->hsync_end) * bpp - 4 - 6;
 425                /*
 426                 * 6 is HBP header + checksum
 427                 * 4 is HSW packet bytes
 428                 * 4 is RGB header + checksum
 429                 */
 430                hsa = (mode->hsync_end - mode->hsync_start) * bpp - 4 - 4 - 6;
 431        } else {
 432                /*
 433                 * HBP includes both back porch and sync
 434                 * 6 is HBP header + checksum
 435                 * 4 is HSW packet bytes
 436                 * 4 is RGB header + checksum
 437                 */
 438                hbp = (mode->htotal - mode->hsync_start) * bpp - 4 - 4 - 6;
 439                /* HSA is not considered in this mode and set to 0 */
 440                hsa = 0;
 441        }
 442        dev_dbg(d->dev, "hfp: %u, hbp: %u, hsa: %u\n",
 443                hfp, hbp, hsa);
 444
 445        /* Frame parameters: horizontal sync active */
 446        val = hsa << DSI_VID_HSIZE1_HSA_LENGTH_SHIFT;
 447        /* horizontal back porch */
 448        val |= hbp << DSI_VID_HSIZE1_HBP_LENGTH_SHIFT;
 449        /* horizontal front porch */
 450        val |= hfp << DSI_VID_HSIZE1_HFP_LENGTH_SHIFT;
 451        writel(val, d->regs + DSI_VID_HSIZE1);
 452
 453        /* RGB data length (bytes on one scanline) */
 454        val = mode->hdisplay * (bpp / 8);
 455        writel(val, d->regs + DSI_VID_HSIZE2);
 456
 457        /* TODO: further adjustments for TVG mode here */
 458
 459        /*
 460         * EOL packet length from bits per line calculations: pixel clock
 461         * is given in kHz, calculate the time between two pixels in
 462         * picoseconds.
 463         */
 464        bpl = mode->clock * mode->htotal;
 465        bpl *= (d->hs_freq / 8);
 466        do_div(bpl, 1000000); /* microseconds */
 467        do_div(bpl, 1000000); /* seconds */
 468        bpl *= d->mdsi->lanes;
 469        dev_dbg(d->dev, "calculated bytes per line: %llu\n", bpl);
 470        /*
 471         * 6 is header + checksum, header = 4 bytes, checksum = 2 bytes
 472         * 4 is short packet for vsync/hsync
 473         */
 474        if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
 475                /* Fixme: isn't the hsync width in pixels? */
 476                blkline_pck = bpl - (mode->hsync_end - mode->hsync_start) - 6;
 477                val = blkline_pck << DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_SHIFT;
 478                writel(val, d->regs + DSI_VID_BLKSIZE2);
 479        } else {
 480                blkline_pck = bpl - 4 - 6;
 481                val = blkline_pck << DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_SHIFT;
 482                writel(val, d->regs + DSI_VID_BLKSIZE1);
 483        }
 484
 485        line_duration = (blkline_pck + 6) / d->mdsi->lanes;
 486        dev_dbg(d->dev, "line duration %u\n", line_duration);
 487        val = line_duration << DSI_VID_DPHY_TIME_REG_LINE_DURATION_SHIFT;
 488        /*
 489         * This is the time to perform LP->HS on D-PHY
 490         * FIXME: nowhere to get this from: DT property on the DSI?
 491         */
 492        val |= 0 << DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_SHIFT;
 493        writel(val, d->regs + DSI_VID_DPHY_TIME);
 494
 495        /* Calculate block end of line */
 496        blkeol_pck = bpl - mode->hdisplay * bpp - 6;
 497        blkeol_duration = (blkeol_pck + 6) / d->mdsi->lanes;
 498        dev_dbg(d->dev, "blkeol pck: %u, duration: %u\n",
 499                 blkeol_pck, blkeol_duration);
 500
 501        if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
 502                /* Set up EOL clock for burst mode */
 503                val = readl(d->regs + DSI_VID_BLKSIZE1);
 504                val |= blkeol_pck << DSI_VID_BLKSIZE1_BLKEOL_PCK_SHIFT;
 505                writel(val, d->regs + DSI_VID_BLKSIZE1);
 506                writel(blkeol_pck, d->regs + DSI_VID_VCA_SETTING2);
 507
 508                writel(blkeol_duration, d->regs + DSI_VID_PCK_TIME);
 509                writel(blkeol_duration - 6, d->regs + DSI_VID_VCA_SETTING1);
 510        }
 511
 512        /* Maximum line limit */
 513        val = readl(d->regs + DSI_VID_VCA_SETTING2);
 514        val |= blkline_pck <<
 515                DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT;
 516        writel(val, d->regs + DSI_VID_VCA_SETTING2);
 517
 518        /* Put IF1 into video mode */
 519        val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
 520        val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
 521        writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
 522
 523        /* Disable command mode on IF1 */
 524        val = readl(d->regs + DSI_CMD_MODE_CTL);
 525        val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN;
 526        writel(val, d->regs + DSI_CMD_MODE_CTL);
 527
 528        /* Enable some error interrupts */
 529        val = readl(d->regs + DSI_VID_MODE_STS_CTL);
 530        val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
 531        val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
 532        writel(val, d->regs + DSI_VID_MODE_STS_CTL);
 533
 534        /* Enable video mode */
 535        val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
 536        val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
 537        writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
 538}
 539
 540static void mcde_dsi_start(struct mcde_dsi *d)
 541{
 542        unsigned long hs_freq;
 543        u32 val;
 544        int i;
 545
 546        /* No integration mode */
 547        writel(0, d->regs + DSI_MCTL_INTEGRATION_MODE);
 548
 549        /* Enable the DSI port, from drivers/video/mcde/dsilink_v2.c */
 550        val = DSI_MCTL_MAIN_DATA_CTL_LINK_EN |
 551                DSI_MCTL_MAIN_DATA_CTL_BTA_EN |
 552                DSI_MCTL_MAIN_DATA_CTL_READ_EN |
 553                DSI_MCTL_MAIN_DATA_CTL_REG_TE_EN;
 554        if (d->mdsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)
 555                val |= DSI_MCTL_MAIN_DATA_CTL_HOST_EOT_GEN;
 556        writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
 557
 558        /* Set a high command timeout, clear other fields */
 559        val = 0x3ff << DSI_CMD_MODE_CTL_TE_TIMEOUT_SHIFT;
 560        writel(val, d->regs + DSI_CMD_MODE_CTL);
 561
 562        /*
 563         * UI_X4 is described as "unit interval times four"
 564         * I guess since DSI packets are 4 bytes wide, one unit
 565         * is one byte.
 566         */
 567        hs_freq = clk_get_rate(d->hs_clk);
 568        hs_freq /= 1000000; /* MHz */
 569        val = 4000 / hs_freq;
 570        dev_dbg(d->dev, "UI value: %d\n", val);
 571        val <<= DSI_MCTL_DPHY_STATIC_UI_X4_SHIFT;
 572        val &= DSI_MCTL_DPHY_STATIC_UI_X4_MASK;
 573        writel(val, d->regs + DSI_MCTL_DPHY_STATIC);
 574
 575        /*
 576         * Enable clocking: 0x0f (something?) between each burst,
 577         * enable the second lane if needed, enable continuous clock if
 578         * needed, enable switch into ULPM (ultra-low power mode) on
 579         * all the lines.
 580         */
 581        val = 0x0f << DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME_SHIFT;
 582        if (d->mdsi->lanes == 2)
 583                val |= DSI_MCTL_MAIN_PHY_CTL_LANE2_EN;
 584        if (!(d->mdsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
 585                val |= DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS;
 586        val |= DSI_MCTL_MAIN_PHY_CTL_CLK_ULPM_EN |
 587                DSI_MCTL_MAIN_PHY_CTL_DAT1_ULPM_EN |
 588                DSI_MCTL_MAIN_PHY_CTL_DAT2_ULPM_EN;
 589        writel(val, d->regs + DSI_MCTL_MAIN_PHY_CTL);
 590
 591        val = (1 << DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME_SHIFT) |
 592                (1 << DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME_SHIFT);
 593        writel(val, d->regs + DSI_MCTL_ULPOUT_TIME);
 594
 595        writel(DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_0_90,
 596               d->regs + DSI_DPHY_LANES_TRIM);
 597
 598        /* High PHY timeout */
 599        val = (0x0f << DSI_MCTL_DPHY_TIMEOUT_CLK_DIV_SHIFT) |
 600                (0x3fff << DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL_SHIFT) |
 601                (0x3fff << DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL_SHIFT);
 602        writel(val, d->regs + DSI_MCTL_DPHY_TIMEOUT);
 603
 604        val = DSI_MCTL_MAIN_EN_PLL_START |
 605                DSI_MCTL_MAIN_EN_CKLANE_EN |
 606                DSI_MCTL_MAIN_EN_DAT1_EN |
 607                DSI_MCTL_MAIN_EN_IF1_EN;
 608        if (d->mdsi->lanes == 2)
 609                val |= DSI_MCTL_MAIN_EN_DAT2_EN;
 610        writel(val, d->regs + DSI_MCTL_MAIN_EN);
 611
 612        /* Wait for the PLL to lock and the clock and data lines to come up */
 613        i = 0;
 614        val = DSI_MCTL_MAIN_STS_PLL_LOCK |
 615                DSI_MCTL_MAIN_STS_CLKLANE_READY |
 616                DSI_MCTL_MAIN_STS_DAT1_READY;
 617        if (d->mdsi->lanes == 2)
 618                val |= DSI_MCTL_MAIN_STS_DAT2_READY;
 619        while ((readl(d->regs + DSI_MCTL_MAIN_STS) & val) != val) {
 620                /* Sleep for a millisecond */
 621                usleep_range(1000, 1500);
 622                if (i++ == 100) {
 623                        dev_warn(d->dev, "DSI lanes did not start up\n");
 624                        return;
 625                }
 626        }
 627
 628        /* TODO needed? */
 629
 630        /* Command mode, clear IF1 ID */
 631        val = readl(d->regs + DSI_CMD_MODE_CTL);
 632        /*
 633         * If we enable low-power mode here, with
 634         * val |= DSI_CMD_MODE_CTL_IF1_LP_EN
 635         * then display updates become really slow.
 636         */
 637        val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
 638        writel(val, d->regs + DSI_CMD_MODE_CTL);
 639
 640        /* Wait for DSI PHY to initialize */
 641        usleep_range(100, 200);
 642        dev_info(d->dev, "DSI link enabled\n");
 643}
 644
 645
 646static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
 647{
 648        struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 649
 650        dev_info(d->dev, "enable DSI master\n");
 651};
 652
 653static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
 654                                     const struct drm_display_mode *mode,
 655                                     const struct drm_display_mode *adj)
 656{
 657        struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 658        unsigned long pixel_clock_hz = mode->clock * 1000;
 659        unsigned long hs_freq, lp_freq;
 660        u32 val;
 661        int ret;
 662
 663        if (!d->mdsi) {
 664                dev_err(d->dev, "no DSI device attached to encoder!\n");
 665                return;
 666        }
 667
 668        dev_info(d->dev, "set DSI master to %dx%d %lu Hz %s mode\n",
 669                 mode->hdisplay, mode->vdisplay, pixel_clock_hz,
 670                 (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD"
 671                );
 672
 673        /* Copy maximum clock frequencies */
 674        if (d->mdsi->lp_rate)
 675                lp_freq = d->mdsi->lp_rate;
 676        else
 677                lp_freq = DSI_DEFAULT_LP_FREQ_HZ;
 678        if (d->mdsi->hs_rate)
 679                hs_freq = d->mdsi->hs_rate;
 680        else
 681                hs_freq = DSI_DEFAULT_HS_FREQ_HZ;
 682
 683        /* Enable LP (Low Power, Energy Save, ES) and HS (High Speed) clocks */
 684        d->lp_freq = clk_round_rate(d->lp_clk, lp_freq);
 685        ret = clk_set_rate(d->lp_clk, d->lp_freq);
 686        if (ret)
 687                dev_err(d->dev, "failed to set LP clock rate %lu Hz\n",
 688                        d->lp_freq);
 689
 690        d->hs_freq = clk_round_rate(d->hs_clk, hs_freq);
 691        ret = clk_set_rate(d->hs_clk, d->hs_freq);
 692        if (ret)
 693                dev_err(d->dev, "failed to set HS clock rate %lu Hz\n",
 694                        d->hs_freq);
 695
 696        /* Start clocks */
 697        ret = clk_prepare_enable(d->lp_clk);
 698        if (ret)
 699                dev_err(d->dev, "failed to enable LP clock\n");
 700        else
 701                dev_info(d->dev, "DSI LP clock rate %lu Hz\n",
 702                         d->lp_freq);
 703        ret = clk_prepare_enable(d->hs_clk);
 704        if (ret)
 705                dev_err(d->dev, "failed to enable HS clock\n");
 706        else
 707                dev_info(d->dev, "DSI HS clock rate %lu Hz\n",
 708                         d->hs_freq);
 709
 710        if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
 711                mcde_dsi_setup_video_mode(d, mode);
 712        } else {
 713                /* Command mode, clear IF1 ID */
 714                val = readl(d->regs + DSI_CMD_MODE_CTL);
 715                /*
 716                 * If we enable low-power mode here with
 717                 * val |= DSI_CMD_MODE_CTL_IF1_LP_EN
 718                 * the display updates become really slow.
 719                 */
 720                val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
 721                writel(val, d->regs + DSI_CMD_MODE_CTL);
 722        }
 723}
 724
 725static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d)
 726{
 727        u32 val;
 728        int i;
 729
 730        /*
 731         * Wait until we get out of command mode
 732         * CSM = Command State Machine
 733         */
 734        i = 0;
 735        val = DSI_CMD_MODE_STS_CSM_RUNNING;
 736        while ((readl(d->regs + DSI_CMD_MODE_STS) & val) == val) {
 737                /* Sleep for a millisecond */
 738                usleep_range(1000, 2000);
 739                if (i++ == 100) {
 740                        dev_warn(d->dev,
 741                                 "could not get out of command mode\n");
 742                        return;
 743                }
 744        }
 745}
 746
 747static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d)
 748{
 749        u32 val;
 750        int i;
 751
 752        /* Wait until we get out og video mode */
 753        i = 0;
 754        val = DSI_VID_MODE_STS_VSG_RUNNING;
 755        while ((readl(d->regs + DSI_VID_MODE_STS) & val) == val) {
 756                /* Sleep for a millisecond */
 757                usleep_range(1000, 2000);
 758                if (i++ == 100) {
 759                        dev_warn(d->dev,
 760                                 "could not get out of video mode\n");
 761                        return;
 762                }
 763        }
 764}
 765
 766static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
 767{
 768        struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 769        u32 val;
 770
 771        /* Disable all error interrupts */
 772        writel(0, d->regs + DSI_VID_MODE_STS_CTL);
 773
 774        if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
 775                /* Stop video mode */
 776                val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
 777                val &= ~DSI_MCTL_MAIN_DATA_CTL_VID_EN;
 778                writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
 779                mcde_dsi_wait_for_video_mode_stop(d);
 780        } else {
 781                /* Stop command mode */
 782                mcde_dsi_wait_for_command_mode_stop(d);
 783        }
 784
 785        /* Stop clocks */
 786        clk_disable_unprepare(d->hs_clk);
 787        clk_disable_unprepare(d->lp_clk);
 788}
 789
 790/*
 791 * This connector needs no special handling, just use the default
 792 * helpers for everything. It's pretty dummy.
 793 */
 794static const struct drm_connector_funcs mcde_dsi_connector_funcs = {
 795        .reset = drm_atomic_helper_connector_reset,
 796        .fill_modes = drm_helper_probe_single_connector_modes,
 797        .destroy = drm_connector_cleanup,
 798        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 799        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 800};
 801
 802static int mcde_dsi_get_modes(struct drm_connector *connector)
 803{
 804        struct mcde_dsi *d = connector_to_mcde_dsi(connector);
 805
 806        /* Just pass the question to the panel */
 807        if (d->panel)
 808                return drm_panel_get_modes(d->panel);
 809
 810        /* TODO: deal with bridges */
 811
 812        return 0;
 813}
 814
 815static const struct drm_connector_helper_funcs
 816mcde_dsi_connector_helper_funcs = {
 817        .get_modes = mcde_dsi_get_modes,
 818};
 819
 820static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
 821{
 822        struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 823        struct drm_device *drm = bridge->dev;
 824        int ret;
 825
 826        drm_connector_helper_add(&d->connector,
 827                                 &mcde_dsi_connector_helper_funcs);
 828
 829        if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
 830                dev_err(d->dev, "we need atomic updates\n");
 831                return -ENOTSUPP;
 832        }
 833
 834        ret = drm_connector_init(drm, &d->connector,
 835                                 &mcde_dsi_connector_funcs,
 836                                 DRM_MODE_CONNECTOR_DSI);
 837        if (ret) {
 838                dev_err(d->dev, "failed to initialize DSI bridge connector\n");
 839                return ret;
 840        }
 841        d->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
 842        /* The encoder in the bridge attached to the DSI bridge */
 843        drm_connector_attach_encoder(&d->connector, bridge->encoder);
 844        /* Then we attach the DSI bridge to the output (panel etc) bridge */
 845        ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge);
 846        if (ret) {
 847                dev_err(d->dev, "failed to attach the DSI bridge\n");
 848                return ret;
 849        }
 850        d->connector.status = connector_status_connected;
 851
 852        return 0;
 853}
 854
 855static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
 856        .attach = mcde_dsi_bridge_attach,
 857        .mode_set = mcde_dsi_bridge_mode_set,
 858        .disable = mcde_dsi_bridge_disable,
 859        .enable = mcde_dsi_bridge_enable,
 860};
 861
 862static int mcde_dsi_bind(struct device *dev, struct device *master,
 863                         void *data)
 864{
 865        struct drm_device *drm = data;
 866        struct mcde *mcde = drm->dev_private;
 867        struct mcde_dsi *d = dev_get_drvdata(dev);
 868        struct device_node *child;
 869        struct drm_panel *panel = NULL;
 870        struct drm_bridge *bridge = NULL;
 871
 872        if (!of_get_available_child_count(dev->of_node)) {
 873                dev_info(dev, "unused DSI interface\n");
 874                d->unused = true;
 875                return 0;
 876        }
 877        d->mcde = mcde;
 878        /* If the display attached before binding, set this up */
 879        if (d->mdsi)
 880                d->mcde->mdsi = d->mdsi;
 881
 882        /* Obtain the clocks */
 883        d->hs_clk = devm_clk_get(dev, "hs");
 884        if (IS_ERR(d->hs_clk)) {
 885                dev_err(dev, "unable to get HS clock\n");
 886                return PTR_ERR(d->hs_clk);
 887        }
 888
 889        d->lp_clk = devm_clk_get(dev, "lp");
 890        if (IS_ERR(d->lp_clk)) {
 891                dev_err(dev, "unable to get LP clock\n");
 892                return PTR_ERR(d->lp_clk);
 893        }
 894
 895        /* Assert RESET through the PRCMU, active low */
 896        /* FIXME: which DSI block? */
 897        regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
 898                           PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
 899
 900        usleep_range(100, 200);
 901
 902        /* De-assert RESET again */
 903        regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
 904                           PRCM_DSI_SW_RESET_DSI0_SW_RESETN,
 905                           PRCM_DSI_SW_RESET_DSI0_SW_RESETN);
 906
 907        /* Start up the hardware */
 908        mcde_dsi_start(d);
 909
 910        /* Look for a panel as a child to this node */
 911        for_each_available_child_of_node(dev->of_node, child) {
 912                panel = of_drm_find_panel(child);
 913                if (IS_ERR(panel)) {
 914                        dev_err(dev, "failed to find panel try bridge (%lu)\n",
 915                                PTR_ERR(panel));
 916                        bridge = of_drm_find_bridge(child);
 917                        if (IS_ERR(bridge)) {
 918                                dev_err(dev, "failed to find bridge (%lu)\n",
 919                                        PTR_ERR(bridge));
 920                                return PTR_ERR(bridge);
 921                        }
 922                }
 923        }
 924        if (panel) {
 925                bridge = drm_panel_bridge_add(panel,
 926                                              DRM_MODE_CONNECTOR_DSI);
 927                if (IS_ERR(bridge)) {
 928                        dev_err(dev, "error adding panel bridge\n");
 929                        return PTR_ERR(bridge);
 930                }
 931                dev_info(dev, "connected to panel\n");
 932                d->panel = panel;
 933        } else if (bridge) {
 934                /* TODO: AV8100 HDMI encoder goes here for example */
 935                dev_info(dev, "connected to non-panel bridge (unsupported)\n");
 936                return -ENODEV;
 937        } else {
 938                dev_err(dev, "no panel or bridge\n");
 939                return -ENODEV;
 940        }
 941
 942        d->bridge_out = bridge;
 943
 944        /* Create a bridge for this DSI channel */
 945        d->bridge.funcs = &mcde_dsi_bridge_funcs;
 946        d->bridge.of_node = dev->of_node;
 947        drm_bridge_add(&d->bridge);
 948
 949        /* TODO: first come first serve, use a list */
 950        mcde->bridge = &d->bridge;
 951
 952        dev_info(dev, "initialized MCDE DSI bridge\n");
 953
 954        return 0;
 955}
 956
 957static void mcde_dsi_unbind(struct device *dev, struct device *master,
 958                            void *data)
 959{
 960        struct mcde_dsi *d = dev_get_drvdata(dev);
 961
 962        if (d->panel)
 963                drm_panel_bridge_remove(d->bridge_out);
 964        regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
 965                           PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
 966}
 967
 968static const struct component_ops mcde_dsi_component_ops = {
 969        .bind   = mcde_dsi_bind,
 970        .unbind = mcde_dsi_unbind,
 971};
 972
 973static int mcde_dsi_probe(struct platform_device *pdev)
 974{
 975        struct device *dev = &pdev->dev;
 976        struct mcde_dsi *d;
 977        struct mipi_dsi_host *host;
 978        struct resource *res;
 979        u32 dsi_id;
 980        int ret;
 981
 982        d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
 983        if (!d)
 984                return -ENOMEM;
 985        d->dev = dev;
 986        platform_set_drvdata(pdev, d);
 987
 988        /* Get a handle on the PRCMU so we can do reset */
 989        d->prcmu =
 990                syscon_regmap_lookup_by_compatible("stericsson,db8500-prcmu");
 991        if (IS_ERR(d->prcmu)) {
 992                dev_err(dev, "no PRCMU regmap\n");
 993                return PTR_ERR(d->prcmu);
 994        }
 995
 996        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 997        d->regs = devm_ioremap_resource(dev, res);
 998        if (IS_ERR(d->regs)) {
 999                dev_err(dev, "no DSI regs\n");
1000                return PTR_ERR(d->regs);
1001        }
1002
1003        dsi_id = readl(d->regs + DSI_ID_REG);
1004        dev_info(dev, "HW revision 0x%08x\n", dsi_id);
1005
1006        host = &d->dsi_host;
1007        host->dev = dev;
1008        host->ops = &mcde_dsi_host_ops;
1009        ret = mipi_dsi_host_register(host);
1010        if (ret < 0) {
1011                dev_err(dev, "failed to register DSI host: %d\n", ret);
1012                return ret;
1013        }
1014        dev_info(dev, "registered DSI host\n");
1015
1016        platform_set_drvdata(pdev, d);
1017        return component_add(dev, &mcde_dsi_component_ops);
1018}
1019
1020static int mcde_dsi_remove(struct platform_device *pdev)
1021{
1022        struct mcde_dsi *d = platform_get_drvdata(pdev);
1023
1024        component_del(&pdev->dev, &mcde_dsi_component_ops);
1025        mipi_dsi_host_unregister(&d->dsi_host);
1026
1027        return 0;
1028}
1029
1030static const struct of_device_id mcde_dsi_of_match[] = {
1031        {
1032                .compatible = "ste,mcde-dsi",
1033        },
1034        {},
1035};
1036
1037struct platform_driver mcde_dsi_driver = {
1038        .driver = {
1039                .name           = "mcde-dsi",
1040                .of_match_table = of_match_ptr(mcde_dsi_of_match),
1041        },
1042        .probe = mcde_dsi_probe,
1043        .remove = mcde_dsi_remove,
1044};
1045