1258 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (c) 2016 Allwinnertech Co., Ltd.4 * Copyright (C) 2017-2018 Bootlin5 *6 * Maxime Ripard <maxime.ripard@bootlin.com>7 */8 9#include <linux/clk.h>10#include <linux/component.h>11#include <linux/crc-ccitt.h>12#include <linux/module.h>13#include <linux/of_address.h>14#include <linux/phy/phy-mipi-dphy.h>15#include <linux/phy/phy.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18#include <linux/regulator/consumer.h>19#include <linux/reset.h>20#include <linux/slab.h>21 22#include <drm/drm_atomic_helper.h>23#include <drm/drm_mipi_dsi.h>24#include <drm/drm_panel.h>25#include <drm/drm_print.h>26#include <drm/drm_probe_helper.h>27#include <drm/drm_simple_kms_helper.h>28 29#include "sun4i_crtc.h"30#include "sun4i_tcon.h"31#include "sun6i_mipi_dsi.h"32 33#include <video/mipi_display.h>34 35#define SUN6I_DSI_CTL_REG 0x00036#define SUN6I_DSI_CTL_EN BIT(0)37 38#define SUN6I_DSI_BASIC_CTL_REG 0x00c39#define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n) (((n) & 0xf) << 4)40#define SUN6I_DSI_BASIC_CTL_TRAIL_FILL BIT(3)41#define SUN6I_DSI_BASIC_CTL_HBP_DIS BIT(2)42#define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS BIT(1)43#define SUN6I_DSI_BASIC_CTL_VIDEO_BURST BIT(0)44 45#define SUN6I_DSI_BASIC_CTL0_REG 0x01046#define SUN6I_DSI_BASIC_CTL0_HS_EOTP_EN BIT(18)47#define SUN6I_DSI_BASIC_CTL0_CRC_EN BIT(17)48#define SUN6I_DSI_BASIC_CTL0_ECC_EN BIT(16)49#define SUN6I_DSI_BASIC_CTL0_INST_ST BIT(0)50 51#define SUN6I_DSI_BASIC_CTL1_REG 0x01452#define SUN6I_DSI_BASIC_CTL1_VIDEO_ST_DELAY(n) (((n) & 0x1fff) << 4)53#define SUN6I_DSI_BASIC_CTL1_VIDEO_FILL BIT(2)54#define SUN6I_DSI_BASIC_CTL1_VIDEO_PRECISION BIT(1)55#define SUN6I_DSI_BASIC_CTL1_VIDEO_MODE BIT(0)56 57#define SUN6I_DSI_BASIC_SIZE0_REG 0x01858#define SUN6I_DSI_BASIC_SIZE0_VBP(n) (((n) & 0xfff) << 16)59#define SUN6I_DSI_BASIC_SIZE0_VSA(n) ((n) & 0xfff)60 61#define SUN6I_DSI_BASIC_SIZE1_REG 0x01c62#define SUN6I_DSI_BASIC_SIZE1_VT(n) (((n) & 0xfff) << 16)63#define SUN6I_DSI_BASIC_SIZE1_VACT(n) ((n) & 0xfff)64 65#define SUN6I_DSI_INST_FUNC_REG(n) (0x020 + (n) * 0x04)66#define SUN6I_DSI_INST_FUNC_INST_MODE(n) (((n) & 0xf) << 28)67#define SUN6I_DSI_INST_FUNC_ESCAPE_ENTRY(n) (((n) & 0xf) << 24)68#define SUN6I_DSI_INST_FUNC_TRANS_PACKET(n) (((n) & 0xf) << 20)69#define SUN6I_DSI_INST_FUNC_LANE_CEN BIT(4)70#define SUN6I_DSI_INST_FUNC_LANE_DEN(n) ((n) & 0xf)71 72#define SUN6I_DSI_INST_LOOP_SEL_REG 0x04073 74#define SUN6I_DSI_INST_LOOP_NUM_REG(n) (0x044 + (n) * 0x10)75#define SUN6I_DSI_INST_LOOP_NUM_N1(n) (((n) & 0xfff) << 16)76#define SUN6I_DSI_INST_LOOP_NUM_N0(n) ((n) & 0xfff)77 78#define SUN6I_DSI_INST_JUMP_SEL_REG 0x04879 80#define SUN6I_DSI_INST_JUMP_CFG_REG(n) (0x04c + (n) * 0x04)81#define SUN6I_DSI_INST_JUMP_CFG_TO(n) (((n) & 0xf) << 20)82#define SUN6I_DSI_INST_JUMP_CFG_POINT(n) (((n) & 0xf) << 16)83#define SUN6I_DSI_INST_JUMP_CFG_NUM(n) ((n) & 0xffff)84 85#define SUN6I_DSI_TRANS_START_REG 0x06086 87#define SUN6I_DSI_TRANS_ZERO_REG 0x07888 89#define SUN6I_DSI_TCON_DRQ_REG 0x07c90#define SUN6I_DSI_TCON_DRQ_ENABLE_MODE BIT(28)91#define SUN6I_DSI_TCON_DRQ_SET(n) ((n) & 0x3ff)92 93#define SUN6I_DSI_PIXEL_CTL0_REG 0x08094#define SUN6I_DSI_PIXEL_CTL0_PD_PLUG_DISABLE BIT(16)95#define SUN6I_DSI_PIXEL_CTL0_FORMAT(n) ((n) & 0xf)96 97#define SUN6I_DSI_PIXEL_CTL1_REG 0x08498 99#define SUN6I_DSI_PIXEL_PH_REG 0x090100#define SUN6I_DSI_PIXEL_PH_ECC(n) (((n) & 0xff) << 24)101#define SUN6I_DSI_PIXEL_PH_WC(n) (((n) & 0xffff) << 8)102#define SUN6I_DSI_PIXEL_PH_VC(n) (((n) & 3) << 6)103#define SUN6I_DSI_PIXEL_PH_DT(n) ((n) & 0x3f)104 105#define SUN6I_DSI_PIXEL_PF0_REG 0x098106#define SUN6I_DSI_PIXEL_PF0_CRC_FORCE(n) ((n) & 0xffff)107 108#define SUN6I_DSI_PIXEL_PF1_REG 0x09c109#define SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINEN(n) (((n) & 0xffff) << 16)110#define SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINE0(n) ((n) & 0xffff)111 112#define SUN6I_DSI_SYNC_HSS_REG 0x0b0113 114#define SUN6I_DSI_SYNC_HSE_REG 0x0b4115 116#define SUN6I_DSI_SYNC_VSS_REG 0x0b8117 118#define SUN6I_DSI_SYNC_VSE_REG 0x0bc119 120#define SUN6I_DSI_BLK_HSA0_REG 0x0c0121 122#define SUN6I_DSI_BLK_HSA1_REG 0x0c4123#define SUN6I_DSI_BLK_PF(n) (((n) & 0xffff) << 16)124#define SUN6I_DSI_BLK_PD(n) ((n) & 0xff)125 126#define SUN6I_DSI_BLK_HBP0_REG 0x0c8127 128#define SUN6I_DSI_BLK_HBP1_REG 0x0cc129 130#define SUN6I_DSI_BLK_HFP0_REG 0x0d0131 132#define SUN6I_DSI_BLK_HFP1_REG 0x0d4133 134#define SUN6I_DSI_BLK_HBLK0_REG 0x0e0135 136#define SUN6I_DSI_BLK_HBLK1_REG 0x0e4137 138#define SUN6I_DSI_BLK_VBLK0_REG 0x0e8139 140#define SUN6I_DSI_BLK_VBLK1_REG 0x0ec141 142#define SUN6I_DSI_BURST_LINE_REG 0x0f0143#define SUN6I_DSI_BURST_LINE_SYNC_POINT(n) (((n) & 0xffff) << 16)144#define SUN6I_DSI_BURST_LINE_NUM(n) ((n) & 0xffff)145 146#define SUN6I_DSI_BURST_DRQ_REG 0x0f4147#define SUN6I_DSI_BURST_DRQ_EDGE1(n) (((n) & 0xffff) << 16)148#define SUN6I_DSI_BURST_DRQ_EDGE0(n) ((n) & 0xffff)149 150#define SUN6I_DSI_CMD_CTL_REG 0x200151#define SUN6I_DSI_CMD_CTL_RX_OVERFLOW BIT(26)152#define SUN6I_DSI_CMD_CTL_RX_FLAG BIT(25)153#define SUN6I_DSI_CMD_CTL_TX_FLAG BIT(9)154 155#define SUN6I_DSI_CMD_RX_REG(n) (0x240 + (n) * 0x04)156 157#define SUN6I_DSI_DEBUG_DATA_REG 0x2f8158 159#define SUN6I_DSI_CMD_TX_REG(n) (0x300 + (n) * 0x04)160 161#define SUN6I_DSI_SYNC_POINT 40162 163enum sun6i_dsi_start_inst {164 DSI_START_LPRX,165 DSI_START_LPTX,166 DSI_START_HSC,167 DSI_START_HSD,168};169 170enum sun6i_dsi_inst_id {171 DSI_INST_ID_LP11 = 0,172 DSI_INST_ID_TBA,173 DSI_INST_ID_HSC,174 DSI_INST_ID_HSD,175 DSI_INST_ID_LPDT,176 DSI_INST_ID_HSCEXIT,177 DSI_INST_ID_NOP,178 DSI_INST_ID_DLY,179 DSI_INST_ID_END = 15,180};181 182enum sun6i_dsi_inst_mode {183 DSI_INST_MODE_STOP = 0,184 DSI_INST_MODE_TBA,185 DSI_INST_MODE_HS,186 DSI_INST_MODE_ESCAPE,187 DSI_INST_MODE_HSCEXIT,188 DSI_INST_MODE_NOP,189};190 191enum sun6i_dsi_inst_escape {192 DSI_INST_ESCA_LPDT = 0,193 DSI_INST_ESCA_ULPS,194 DSI_INST_ESCA_UN1,195 DSI_INST_ESCA_UN2,196 DSI_INST_ESCA_RESET,197 DSI_INST_ESCA_UN3,198 DSI_INST_ESCA_UN4,199 DSI_INST_ESCA_UN5,200};201 202enum sun6i_dsi_inst_packet {203 DSI_INST_PACK_PIXEL = 0,204 DSI_INST_PACK_COMMAND,205};206 207static const u32 sun6i_dsi_ecc_array[] = {208 [0] = (BIT(0) | BIT(1) | BIT(2) | BIT(4) | BIT(5) | BIT(7) | BIT(10) |209 BIT(11) | BIT(13) | BIT(16) | BIT(20) | BIT(21) | BIT(22) |210 BIT(23)),211 [1] = (BIT(0) | BIT(1) | BIT(3) | BIT(4) | BIT(6) | BIT(8) | BIT(10) |212 BIT(12) | BIT(14) | BIT(17) | BIT(20) | BIT(21) | BIT(22) |213 BIT(23)),214 [2] = (BIT(0) | BIT(2) | BIT(3) | BIT(5) | BIT(6) | BIT(9) | BIT(11) |215 BIT(12) | BIT(15) | BIT(18) | BIT(20) | BIT(21) | BIT(22)),216 [3] = (BIT(1) | BIT(2) | BIT(3) | BIT(7) | BIT(8) | BIT(9) | BIT(13) |217 BIT(14) | BIT(15) | BIT(19) | BIT(20) | BIT(21) | BIT(23)),218 [4] = (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(16) |219 BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(22) | BIT(23)),220 [5] = (BIT(10) | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15) |221 BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(21) | BIT(22) |222 BIT(23)),223};224 225static u32 sun6i_dsi_ecc_compute(unsigned int data)226{227 int i;228 u8 ecc = 0;229 230 for (i = 0; i < ARRAY_SIZE(sun6i_dsi_ecc_array); i++) {231 u32 field = sun6i_dsi_ecc_array[i];232 bool init = false;233 u8 val = 0;234 int j;235 236 for (j = 0; j < 24; j++) {237 if (!(BIT(j) & field))238 continue;239 240 if (!init) {241 val = (BIT(j) & data) ? 1 : 0;242 init = true;243 } else {244 val ^= (BIT(j) & data) ? 1 : 0;245 }246 }247 248 ecc |= val << i;249 }250 251 return ecc;252}253 254static u16 sun6i_dsi_crc_compute(u8 const *buffer, size_t len)255{256 return crc_ccitt(0xffff, buffer, len);257}258 259static u16 sun6i_dsi_crc_repeat(u8 pd, u8 *buffer, size_t len)260{261 memset(buffer, pd, len);262 263 return sun6i_dsi_crc_compute(buffer, len);264}265 266static u32 sun6i_dsi_build_sync_pkt(u8 dt, u8 vc, u8 d0, u8 d1)267{268 u32 val = dt & 0x3f;269 270 val |= (vc & 3) << 6;271 val |= (d0 & 0xff) << 8;272 val |= (d1 & 0xff) << 16;273 val |= sun6i_dsi_ecc_compute(val) << 24;274 275 return val;276}277 278static u32 sun6i_dsi_build_blk0_pkt(u8 vc, u16 wc)279{280 return sun6i_dsi_build_sync_pkt(MIPI_DSI_BLANKING_PACKET, vc,281 wc & 0xff, wc >> 8);282}283 284static u32 sun6i_dsi_build_blk1_pkt(u16 pd, u8 *buffer, size_t len)285{286 u32 val = SUN6I_DSI_BLK_PD(pd);287 288 return val | SUN6I_DSI_BLK_PF(sun6i_dsi_crc_repeat(pd, buffer, len));289}290 291static void sun6i_dsi_inst_abort(struct sun6i_dsi *dsi)292{293 regmap_update_bits(dsi->regs, SUN6I_DSI_BASIC_CTL0_REG,294 SUN6I_DSI_BASIC_CTL0_INST_ST, 0);295}296 297static void sun6i_dsi_inst_commit(struct sun6i_dsi *dsi)298{299 regmap_update_bits(dsi->regs, SUN6I_DSI_BASIC_CTL0_REG,300 SUN6I_DSI_BASIC_CTL0_INST_ST,301 SUN6I_DSI_BASIC_CTL0_INST_ST);302}303 304static int sun6i_dsi_inst_wait_for_completion(struct sun6i_dsi *dsi)305{306 u32 val;307 308 return regmap_read_poll_timeout(dsi->regs, SUN6I_DSI_BASIC_CTL0_REG,309 val,310 !(val & SUN6I_DSI_BASIC_CTL0_INST_ST),311 100, 5000);312}313 314static void sun6i_dsi_inst_setup(struct sun6i_dsi *dsi,315 enum sun6i_dsi_inst_id id,316 enum sun6i_dsi_inst_mode mode,317 bool clock, u8 data,318 enum sun6i_dsi_inst_packet packet,319 enum sun6i_dsi_inst_escape escape)320{321 regmap_write(dsi->regs, SUN6I_DSI_INST_FUNC_REG(id),322 SUN6I_DSI_INST_FUNC_INST_MODE(mode) |323 SUN6I_DSI_INST_FUNC_ESCAPE_ENTRY(escape) |324 SUN6I_DSI_INST_FUNC_TRANS_PACKET(packet) |325 (clock ? SUN6I_DSI_INST_FUNC_LANE_CEN : 0) |326 SUN6I_DSI_INST_FUNC_LANE_DEN(data));327}328 329static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,330 struct mipi_dsi_device *device)331{332 u8 lanes_mask = GENMASK(device->lanes - 1, 0);333 334 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_LP11, DSI_INST_MODE_STOP,335 true, lanes_mask, 0, 0);336 337 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_TBA, DSI_INST_MODE_TBA,338 false, 1, 0, 0);339 340 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_HSC, DSI_INST_MODE_HS,341 true, 0, DSI_INST_PACK_PIXEL, 0);342 343 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_HSD, DSI_INST_MODE_HS,344 false, lanes_mask, DSI_INST_PACK_PIXEL, 0);345 346 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_LPDT, DSI_INST_MODE_ESCAPE,347 false, 1, DSI_INST_PACK_COMMAND,348 DSI_INST_ESCA_LPDT);349 350 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_HSCEXIT, DSI_INST_MODE_HSCEXIT,351 true, 0, 0, 0);352 353 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_NOP, DSI_INST_MODE_STOP,354 false, lanes_mask, 0, 0);355 356 sun6i_dsi_inst_setup(dsi, DSI_INST_ID_DLY, DSI_INST_MODE_NOP,357 true, lanes_mask, 0, 0);358 359 regmap_write(dsi->regs, SUN6I_DSI_INST_JUMP_CFG_REG(0),360 SUN6I_DSI_INST_JUMP_CFG_POINT(DSI_INST_ID_NOP) |361 SUN6I_DSI_INST_JUMP_CFG_TO(DSI_INST_ID_HSCEXIT) |362 SUN6I_DSI_INST_JUMP_CFG_NUM(1));363};364 365static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,366 struct drm_display_mode *mode)367{368 u16 delay = mode->vtotal - (mode->vsync_start - mode->vdisplay) + 1;369 370 if (delay > mode->vtotal)371 delay = delay % mode->vtotal;372 373 return max_t(u16, delay, 1);374}375 376static u16 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi,377 struct drm_display_mode *mode)378{379 struct mipi_dsi_device *device = dsi->device;380 unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;381 382 return mode->htotal * Bpp / device->lanes;383}384 385static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi,386 struct drm_display_mode *mode,387 u16 line_num, u16 edge1)388{389 u16 edge0 = edge1;390 391 edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8;392 393 if (edge0 > line_num)394 return edge0 - line_num;395 396 return 1;397}398 399static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi,400 struct drm_display_mode *mode,401 u16 line_num)402{403 struct mipi_dsi_device *device = dsi->device;404 unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;405 unsigned int hbp = mode->htotal - mode->hsync_end;406 u16 edge1;407 408 edge1 = SUN6I_DSI_SYNC_POINT;409 edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes;410 411 if (edge1 > line_num)412 return line_num;413 414 return edge1;415}416 417static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,418 struct drm_display_mode *mode)419{420 struct mipi_dsi_device *device = dsi->device;421 u32 val = 0;422 423 if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {424 u16 line_num = sun6i_dsi_get_line_num(dsi, mode);425 u16 edge0, edge1;426 427 edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num);428 edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1);429 430 regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG,431 SUN6I_DSI_BURST_DRQ_EDGE0(edge0) |432 SUN6I_DSI_BURST_DRQ_EDGE1(edge1));433 434 regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG,435 SUN6I_DSI_BURST_LINE_NUM(line_num) |436 SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT));437 438 val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE;439 } else if ((mode->hsync_start - mode->hdisplay) > 20) {440 /* Maaaaaagic */441 u16 drq = (mode->hsync_start - mode->hdisplay) - 20;442 443 drq *= mipi_dsi_pixel_format_to_bpp(device->format);444 drq /= 32;445 446 val = (SUN6I_DSI_TCON_DRQ_ENABLE_MODE |447 SUN6I_DSI_TCON_DRQ_SET(drq));448 }449 450 regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG, val);451}452 453static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,454 struct drm_display_mode *mode)455{456 struct mipi_dsi_device *device = dsi->device;457 u16 delay = 50 - 1;458 459 if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {460 u32 hsync_porch = (mode->htotal - mode->hdisplay) * 150;461 462 delay = (hsync_porch / ((mode->clock / 1000) * 8));463 delay -= 50;464 }465 466 regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG,467 2 << (4 * DSI_INST_ID_LP11) |468 3 << (4 * DSI_INST_ID_DLY));469 470 regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0),471 SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) |472 SUN6I_DSI_INST_LOOP_NUM_N1(delay));473 regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(1),474 SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) |475 SUN6I_DSI_INST_LOOP_NUM_N1(delay));476}477 478static void sun6i_dsi_setup_format(struct sun6i_dsi *dsi,479 struct drm_display_mode *mode)480{481 struct mipi_dsi_device *device = dsi->device;482 u32 val = SUN6I_DSI_PIXEL_PH_VC(device->channel);483 u8 dt, fmt;484 u16 wc;485 486 /*487 * TODO: The format defines are only valid in video mode and488 * change in command mode.489 */490 switch (device->format) {491 case MIPI_DSI_FMT_RGB888:492 dt = MIPI_DSI_PACKED_PIXEL_STREAM_24;493 fmt = 8;494 break;495 case MIPI_DSI_FMT_RGB666:496 dt = MIPI_DSI_PIXEL_STREAM_3BYTE_18;497 fmt = 9;498 break;499 case MIPI_DSI_FMT_RGB666_PACKED:500 dt = MIPI_DSI_PACKED_PIXEL_STREAM_18;501 fmt = 10;502 break;503 case MIPI_DSI_FMT_RGB565:504 dt = MIPI_DSI_PACKED_PIXEL_STREAM_16;505 fmt = 11;506 break;507 default:508 return;509 }510 val |= SUN6I_DSI_PIXEL_PH_DT(dt);511 512 wc = mode->hdisplay * mipi_dsi_pixel_format_to_bpp(device->format) / 8;513 val |= SUN6I_DSI_PIXEL_PH_WC(wc);514 val |= SUN6I_DSI_PIXEL_PH_ECC(sun6i_dsi_ecc_compute(val));515 516 regmap_write(dsi->regs, SUN6I_DSI_PIXEL_PH_REG, val);517 518 regmap_write(dsi->regs, SUN6I_DSI_PIXEL_PF0_REG,519 SUN6I_DSI_PIXEL_PF0_CRC_FORCE(0xffff));520 521 regmap_write(dsi->regs, SUN6I_DSI_PIXEL_PF1_REG,522 SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINE0(0xffff) |523 SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINEN(0xffff));524 525 regmap_write(dsi->regs, SUN6I_DSI_PIXEL_CTL0_REG,526 SUN6I_DSI_PIXEL_CTL0_PD_PLUG_DISABLE |527 SUN6I_DSI_PIXEL_CTL0_FORMAT(fmt));528}529 530static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,531 struct drm_display_mode *mode)532{533 struct mipi_dsi_device *device = dsi->device;534 int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;535 u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0;536 u32 basic_ctl = 0;537 size_t bytes;538 u8 *buffer;539 540 /* Do all timing calculations up front to allocate buffer space */541 542 if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {543 hblk = mode->hdisplay * Bpp;544 basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST |545 SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS |546 SUN6I_DSI_BASIC_CTL_HBP_DIS;547 548 if (device->lanes == 4)549 basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL |550 SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc);551 } else {552 /*553 * A sync period is composed of a blanking packet (4554 * bytes + payload + 2 bytes) and a sync event packet555 * (4 bytes). Its minimal size is therefore 10 bytes556 */557#define HSA_PACKET_OVERHEAD 10558 hsa = max(HSA_PACKET_OVERHEAD,559 (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);560 561 /*562 * The backporch is set using a blanking packet (4563 * bytes + payload + 2 bytes). Its minimal size is564 * therefore 6 bytes565 */566#define HBP_PACKET_OVERHEAD 6567 hbp = max(HBP_PACKET_OVERHEAD,568 (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD);569 570 /*571 * The frontporch is set using a sync event (4 bytes)572 * and two blanking packets (each one is 4 bytes +573 * payload + 2 bytes). Its minimal size is therefore574 * 16 bytes575 */576#define HFP_PACKET_OVERHEAD 16577 hfp = max(HFP_PACKET_OVERHEAD,578 (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD);579 580 /*581 * The blanking is set using a sync event (4 bytes)582 * and a blanking packet (4 bytes + payload + 2583 * bytes). Its minimal size is therefore 10 bytes.584 */585#define HBLK_PACKET_OVERHEAD 10586 hblk = max(HBLK_PACKET_OVERHEAD,587 (mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp -588 HBLK_PACKET_OVERHEAD);589 590 /*591 * And I'm not entirely sure what vblk is about. The driver in592 * Allwinner BSP is using a rather convoluted calculation593 * there only for 4 lanes. However, using 0 (the !4 lanes594 * case) even with a 4 lanes screen seems to work...595 */596 vblk = 0;597 }598 599 /* How many bytes do we need to send all payloads? */600 bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk);601 buffer = kmalloc(bytes, GFP_KERNEL);602 if (WARN_ON(!buffer))603 return;604 605 regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, basic_ctl);606 607 regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG,608 sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START,609 device->channel,610 0, 0));611 612 regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSE_REG,613 sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_END,614 device->channel,615 0, 0));616 617 regmap_write(dsi->regs, SUN6I_DSI_SYNC_VSS_REG,618 sun6i_dsi_build_sync_pkt(MIPI_DSI_V_SYNC_START,619 device->channel,620 0, 0));621 622 regmap_write(dsi->regs, SUN6I_DSI_SYNC_VSE_REG,623 sun6i_dsi_build_sync_pkt(MIPI_DSI_V_SYNC_END,624 device->channel,625 0, 0));626 627 regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE0_REG,628 SUN6I_DSI_BASIC_SIZE0_VSA(mode->vsync_end -629 mode->vsync_start) |630 SUN6I_DSI_BASIC_SIZE0_VBP(mode->vtotal -631 mode->vsync_end));632 633 regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE1_REG,634 SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) |635 SUN6I_DSI_BASIC_SIZE1_VT(mode->vtotal));636 637 /* sync */638 regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA0_REG,639 sun6i_dsi_build_blk0_pkt(device->channel, hsa));640 regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA1_REG,641 sun6i_dsi_build_blk1_pkt(0, buffer, hsa));642 643 /* backporch */644 regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP0_REG,645 sun6i_dsi_build_blk0_pkt(device->channel, hbp));646 regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP1_REG,647 sun6i_dsi_build_blk1_pkt(0, buffer, hbp));648 649 /* frontporch */650 regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP0_REG,651 sun6i_dsi_build_blk0_pkt(device->channel, hfp));652 regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP1_REG,653 sun6i_dsi_build_blk1_pkt(0, buffer, hfp));654 655 /* hblk */656 regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK0_REG,657 sun6i_dsi_build_blk0_pkt(device->channel, hblk));658 regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK1_REG,659 sun6i_dsi_build_blk1_pkt(0, buffer, hblk));660 661 /* vblk */662 regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK0_REG,663 sun6i_dsi_build_blk0_pkt(device->channel, vblk));664 regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK1_REG,665 sun6i_dsi_build_blk1_pkt(0, buffer, vblk));666 667 kfree(buffer);668}669 670static int sun6i_dsi_start(struct sun6i_dsi *dsi,671 enum sun6i_dsi_start_inst func)672{673 switch (func) {674 case DSI_START_LPTX:675 regmap_write(dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG,676 DSI_INST_ID_LPDT << (4 * DSI_INST_ID_LP11) |677 DSI_INST_ID_END << (4 * DSI_INST_ID_LPDT));678 break;679 case DSI_START_LPRX:680 regmap_write(dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG,681 DSI_INST_ID_LPDT << (4 * DSI_INST_ID_LP11) |682 DSI_INST_ID_DLY << (4 * DSI_INST_ID_LPDT) |683 DSI_INST_ID_TBA << (4 * DSI_INST_ID_DLY) |684 DSI_INST_ID_END << (4 * DSI_INST_ID_TBA));685 break;686 case DSI_START_HSC:687 regmap_write(dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG,688 DSI_INST_ID_HSC << (4 * DSI_INST_ID_LP11) |689 DSI_INST_ID_END << (4 * DSI_INST_ID_HSC));690 break;691 case DSI_START_HSD:692 regmap_write(dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG,693 DSI_INST_ID_NOP << (4 * DSI_INST_ID_LP11) |694 DSI_INST_ID_HSD << (4 * DSI_INST_ID_NOP) |695 DSI_INST_ID_DLY << (4 * DSI_INST_ID_HSD) |696 DSI_INST_ID_NOP << (4 * DSI_INST_ID_DLY) |697 DSI_INST_ID_END << (4 * DSI_INST_ID_HSCEXIT));698 break;699 default:700 regmap_write(dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG,701 DSI_INST_ID_END << (4 * DSI_INST_ID_LP11));702 break;703 }704 705 sun6i_dsi_inst_abort(dsi);706 sun6i_dsi_inst_commit(dsi);707 708 if (func == DSI_START_HSC)709 regmap_write_bits(dsi->regs,710 SUN6I_DSI_INST_FUNC_REG(DSI_INST_ID_LP11),711 SUN6I_DSI_INST_FUNC_LANE_CEN, 0);712 713 return 0;714}715 716static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)717{718 struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;719 struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder);720 struct mipi_dsi_device *device = dsi->device;721 union phy_configure_opts opts = { };722 struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;723 u16 delay;724 int err;725 726 DRM_DEBUG_DRIVER("Enabling DSI output\n");727 728 err = regulator_enable(dsi->regulator);729 if (err)730 dev_warn(dsi->dev, "failed to enable VCC-DSI supply: %d\n", err);731 732 reset_control_deassert(dsi->reset);733 clk_prepare_enable(dsi->mod_clk);734 735 /*736 * Enable the DSI block.737 */738 regmap_write(dsi->regs, SUN6I_DSI_CTL_REG, SUN6I_DSI_CTL_EN);739 740 regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL0_REG,741 SUN6I_DSI_BASIC_CTL0_ECC_EN | SUN6I_DSI_BASIC_CTL0_CRC_EN);742 743 regmap_write(dsi->regs, SUN6I_DSI_TRANS_START_REG, 10);744 regmap_write(dsi->regs, SUN6I_DSI_TRANS_ZERO_REG, 0);745 746 sun6i_dsi_inst_init(dsi, dsi->device);747 748 regmap_write(dsi->regs, SUN6I_DSI_DEBUG_DATA_REG, 0xff);749 750 delay = sun6i_dsi_get_video_start_delay(dsi, mode);751 regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL1_REG,752 SUN6I_DSI_BASIC_CTL1_VIDEO_ST_DELAY(delay) |753 SUN6I_DSI_BASIC_CTL1_VIDEO_FILL |754 SUN6I_DSI_BASIC_CTL1_VIDEO_PRECISION |755 SUN6I_DSI_BASIC_CTL1_VIDEO_MODE);756 757 sun6i_dsi_setup_burst(dsi, mode);758 sun6i_dsi_setup_inst_loop(dsi, mode);759 sun6i_dsi_setup_format(dsi, mode);760 sun6i_dsi_setup_timings(dsi, mode);761 762 phy_init(dsi->dphy);763 764 phy_mipi_dphy_get_default_config(mode->clock * 1000,765 mipi_dsi_pixel_format_to_bpp(device->format),766 device->lanes, cfg);767 768 phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);769 phy_configure(dsi->dphy, &opts);770 phy_power_on(dsi->dphy);771 772 if (dsi->panel)773 drm_panel_prepare(dsi->panel);774 775 /*776 * FIXME: This should be moved after the switch to HS mode.777 *778 * Unfortunately, once in HS mode, it seems like we're not779 * able to send DCS commands anymore, which would prevent any780 * panel to send any DCS command as part as their enable781 * method, which is quite common.782 *783 * I haven't seen any artifact due to that sub-optimal784 * ordering on the panels I've tested it with, so I guess this785 * will do for now, until that IP is better understood.786 */787 if (dsi->panel)788 drm_panel_enable(dsi->panel);789 790 sun6i_dsi_start(dsi, DSI_START_HSC);791 792 udelay(1000);793 794 sun6i_dsi_start(dsi, DSI_START_HSD);795}796 797static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)798{799 struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder);800 801 DRM_DEBUG_DRIVER("Disabling DSI output\n");802 803 if (dsi->panel) {804 drm_panel_disable(dsi->panel);805 drm_panel_unprepare(dsi->panel);806 }807 808 phy_power_off(dsi->dphy);809 phy_exit(dsi->dphy);810 811 clk_disable_unprepare(dsi->mod_clk);812 reset_control_assert(dsi->reset);813 regulator_disable(dsi->regulator);814}815 816static int sun6i_dsi_get_modes(struct drm_connector *connector)817{818 struct sun6i_dsi *dsi = connector_to_sun6i_dsi(connector);819 820 return drm_panel_get_modes(dsi->panel, connector);821}822 823static const struct drm_connector_helper_funcs sun6i_dsi_connector_helper_funcs = {824 .get_modes = sun6i_dsi_get_modes,825};826 827static enum drm_connector_status828sun6i_dsi_connector_detect(struct drm_connector *connector, bool force)829{830 struct sun6i_dsi *dsi = connector_to_sun6i_dsi(connector);831 832 return dsi->panel ? connector_status_connected :833 connector_status_disconnected;834}835 836static const struct drm_connector_funcs sun6i_dsi_connector_funcs = {837 .detect = sun6i_dsi_connector_detect,838 .fill_modes = drm_helper_probe_single_connector_modes,839 .destroy = drm_connector_cleanup,840 .reset = drm_atomic_helper_connector_reset,841 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,842 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,843};844 845static const struct drm_encoder_helper_funcs sun6i_dsi_enc_helper_funcs = {846 .disable = sun6i_dsi_encoder_disable,847 .enable = sun6i_dsi_encoder_enable,848};849 850static u32 sun6i_dsi_dcs_build_pkt_hdr(struct sun6i_dsi *dsi,851 const struct mipi_dsi_msg *msg)852{853 u32 pkt = msg->type;854 855 if (msg->type == MIPI_DSI_DCS_LONG_WRITE) {856 pkt |= ((msg->tx_len) & 0xffff) << 8;857 pkt |= (((msg->tx_len) >> 8) & 0xffff) << 16;858 } else {859 pkt |= (((u8 *)msg->tx_buf)[0] << 8);860 if (msg->tx_len > 1)861 pkt |= (((u8 *)msg->tx_buf)[1] << 16);862 }863 864 pkt |= sun6i_dsi_ecc_compute(pkt) << 24;865 866 return pkt;867}868 869static int sun6i_dsi_dcs_write_short(struct sun6i_dsi *dsi,870 const struct mipi_dsi_msg *msg)871{872 regmap_write(dsi->regs, SUN6I_DSI_CMD_TX_REG(0),873 sun6i_dsi_dcs_build_pkt_hdr(dsi, msg));874 regmap_write_bits(dsi->regs, SUN6I_DSI_CMD_CTL_REG,875 0xff, (4 - 1));876 877 sun6i_dsi_start(dsi, DSI_START_LPTX);878 879 return msg->tx_len;880}881 882static int sun6i_dsi_dcs_write_long(struct sun6i_dsi *dsi,883 const struct mipi_dsi_msg *msg)884{885 int ret, len = 0;886 u8 *bounce;887 u16 crc;888 889 regmap_write(dsi->regs, SUN6I_DSI_CMD_TX_REG(0),890 sun6i_dsi_dcs_build_pkt_hdr(dsi, msg));891 892 bounce = kzalloc(ALIGN(msg->tx_len + sizeof(crc), 4), GFP_KERNEL);893 if (!bounce)894 return -ENOMEM;895 896 memcpy(bounce, msg->tx_buf, msg->tx_len);897 len += msg->tx_len;898 899 crc = sun6i_dsi_crc_compute(bounce, msg->tx_len);900 memcpy((u8 *)bounce + msg->tx_len, &crc, sizeof(crc));901 len += sizeof(crc);902 903 regmap_bulk_write(dsi->regs, SUN6I_DSI_CMD_TX_REG(1), bounce, DIV_ROUND_UP(len, 4));904 regmap_write(dsi->regs, SUN6I_DSI_CMD_CTL_REG, len + 4 - 1);905 kfree(bounce);906 907 sun6i_dsi_start(dsi, DSI_START_LPTX);908 909 ret = sun6i_dsi_inst_wait_for_completion(dsi);910 if (ret < 0) {911 sun6i_dsi_inst_abort(dsi);912 return ret;913 }914 915 /*916 * TODO: There's some bits (reg 0x200, bits 8/9) that917 * apparently can be used to check whether the data have been918 * sent, but I couldn't get it to work reliably.919 */920 return msg->tx_len;921}922 923static int sun6i_dsi_dcs_read(struct sun6i_dsi *dsi,924 const struct mipi_dsi_msg *msg)925{926 u32 val;927 int ret;928 u8 byte0;929 930 regmap_write(dsi->regs, SUN6I_DSI_CMD_TX_REG(0),931 sun6i_dsi_dcs_build_pkt_hdr(dsi, msg));932 regmap_write(dsi->regs, SUN6I_DSI_CMD_CTL_REG,933 (4 - 1));934 935 sun6i_dsi_start(dsi, DSI_START_LPRX);936 937 ret = sun6i_dsi_inst_wait_for_completion(dsi);938 if (ret < 0) {939 sun6i_dsi_inst_abort(dsi);940 return ret;941 }942 943 /*944 * TODO: There's some bits (reg 0x200, bits 24/25) that945 * apparently can be used to check whether the data have been946 * received, but I couldn't get it to work reliably.947 */948 regmap_read(dsi->regs, SUN6I_DSI_CMD_CTL_REG, &val);949 if (val & SUN6I_DSI_CMD_CTL_RX_OVERFLOW)950 return -EIO;951 952 regmap_read(dsi->regs, SUN6I_DSI_CMD_RX_REG(0), &val);953 byte0 = val & 0xff;954 if (byte0 == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT)955 return -EIO;956 957 ((u8 *)msg->rx_buf)[0] = (val >> 8);958 959 return 1;960}961 962static int sun6i_dsi_attach(struct mipi_dsi_host *host,963 struct mipi_dsi_device *device)964{965 struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);966 struct drm_panel *panel = of_drm_find_panel(device->dev.of_node);967 968 if (IS_ERR(panel))969 return PTR_ERR(panel);970 if (!dsi->drm || !dsi->drm->registered)971 return -EPROBE_DEFER;972 973 dsi->panel = panel;974 dsi->device = device;975 976 drm_kms_helper_hotplug_event(dsi->drm);977 978 dev_info(host->dev, "Attached device %s\n", device->name);979 980 return 0;981}982 983static int sun6i_dsi_detach(struct mipi_dsi_host *host,984 struct mipi_dsi_device *device)985{986 struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);987 988 dsi->panel = NULL;989 dsi->device = NULL;990 991 drm_kms_helper_hotplug_event(dsi->drm);992 993 return 0;994}995 996static ssize_t sun6i_dsi_transfer(struct mipi_dsi_host *host,997 const struct mipi_dsi_msg *msg)998{999 struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);1000 int ret;1001 1002 ret = sun6i_dsi_inst_wait_for_completion(dsi);1003 if (ret < 0)1004 sun6i_dsi_inst_abort(dsi);1005 1006 regmap_write(dsi->regs, SUN6I_DSI_CMD_CTL_REG,1007 SUN6I_DSI_CMD_CTL_RX_OVERFLOW |1008 SUN6I_DSI_CMD_CTL_RX_FLAG |1009 SUN6I_DSI_CMD_CTL_TX_FLAG);1010 1011 switch (msg->type) {1012 case MIPI_DSI_DCS_SHORT_WRITE:1013 case MIPI_DSI_DCS_SHORT_WRITE_PARAM:1014 case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:1015 ret = sun6i_dsi_dcs_write_short(dsi, msg);1016 break;1017 1018 case MIPI_DSI_DCS_LONG_WRITE:1019 ret = sun6i_dsi_dcs_write_long(dsi, msg);1020 break;1021 1022 case MIPI_DSI_DCS_READ:1023 if (msg->rx_len == 1) {1024 ret = sun6i_dsi_dcs_read(dsi, msg);1025 break;1026 }1027 fallthrough;1028 1029 default:1030 ret = -EINVAL;1031 }1032 1033 return ret;1034}1035 1036static const struct mipi_dsi_host_ops sun6i_dsi_host_ops = {1037 .attach = sun6i_dsi_attach,1038 .detach = sun6i_dsi_detach,1039 .transfer = sun6i_dsi_transfer,1040};1041 1042static const struct regmap_config sun6i_dsi_regmap_config = {1043 .reg_bits = 32,1044 .val_bits = 32,1045 .reg_stride = 4,1046 .max_register = SUN6I_DSI_CMD_TX_REG(255),1047 .name = "mipi-dsi",1048};1049 1050static int sun6i_dsi_bind(struct device *dev, struct device *master,1051 void *data)1052{1053 struct drm_device *drm = data;1054 struct sun6i_dsi *dsi = dev_get_drvdata(dev);1055 int ret;1056 1057 drm_encoder_helper_add(&dsi->encoder,1058 &sun6i_dsi_enc_helper_funcs);1059 ret = drm_simple_encoder_init(drm, &dsi->encoder,1060 DRM_MODE_ENCODER_DSI);1061 if (ret) {1062 dev_err(dsi->dev, "Couldn't initialise the DSI encoder\n");1063 return ret;1064 }1065 dsi->encoder.possible_crtcs = BIT(0);1066 1067 drm_connector_helper_add(&dsi->connector,1068 &sun6i_dsi_connector_helper_funcs);1069 ret = drm_connector_init(drm, &dsi->connector,1070 &sun6i_dsi_connector_funcs,1071 DRM_MODE_CONNECTOR_DSI);1072 if (ret) {1073 dev_err(dsi->dev,1074 "Couldn't initialise the DSI connector\n");1075 goto err_cleanup_connector;1076 }1077 1078 drm_connector_attach_encoder(&dsi->connector, &dsi->encoder);1079 1080 dsi->drm = drm;1081 1082 return 0;1083 1084err_cleanup_connector:1085 drm_encoder_cleanup(&dsi->encoder);1086 return ret;1087}1088 1089static void sun6i_dsi_unbind(struct device *dev, struct device *master,1090 void *data)1091{1092 struct sun6i_dsi *dsi = dev_get_drvdata(dev);1093 1094 dsi->drm = NULL;1095}1096 1097static const struct component_ops sun6i_dsi_ops = {1098 .bind = sun6i_dsi_bind,1099 .unbind = sun6i_dsi_unbind,1100};1101 1102static int sun6i_dsi_probe(struct platform_device *pdev)1103{1104 const struct sun6i_dsi_variant *variant;1105 struct device *dev = &pdev->dev;1106 struct sun6i_dsi *dsi;1107 void __iomem *base;1108 int ret;1109 1110 variant = device_get_match_data(dev);1111 if (!variant)1112 return -EINVAL;1113 1114 dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);1115 if (!dsi)1116 return -ENOMEM;1117 dev_set_drvdata(dev, dsi);1118 dsi->dev = dev;1119 dsi->host.ops = &sun6i_dsi_host_ops;1120 dsi->host.dev = dev;1121 dsi->variant = variant;1122 1123 base = devm_platform_ioremap_resource(pdev, 0);1124 if (IS_ERR(base)) {1125 dev_err(dev, "Couldn't map the DSI encoder registers\n");1126 return PTR_ERR(base);1127 }1128 1129 dsi->regulator = devm_regulator_get(dev, "vcc-dsi");1130 if (IS_ERR(dsi->regulator))1131 return dev_err_probe(dev, PTR_ERR(dsi->regulator),1132 "Couldn't get VCC-DSI supply\n");1133 1134 dsi->reset = devm_reset_control_get_shared(dev, NULL);1135 if (IS_ERR(dsi->reset)) {1136 dev_err(dev, "Couldn't get our reset line\n");1137 return PTR_ERR(dsi->reset);1138 }1139 1140 dsi->regs = devm_regmap_init_mmio(dev, base, &sun6i_dsi_regmap_config);1141 if (IS_ERR(dsi->regs)) {1142 dev_err(dev, "Couldn't init regmap\n");1143 return PTR_ERR(dsi->regs);1144 }1145 1146 dsi->bus_clk = devm_clk_get(dev, variant->has_mod_clk ? "bus" : NULL);1147 if (IS_ERR(dsi->bus_clk))1148 return dev_err_probe(dev, PTR_ERR(dsi->bus_clk),1149 "Couldn't get the DSI bus clock\n");1150 1151 ret = regmap_mmio_attach_clk(dsi->regs, dsi->bus_clk);1152 if (ret)1153 return ret;1154 1155 if (variant->has_mod_clk) {1156 dsi->mod_clk = devm_clk_get(dev, "mod");1157 if (IS_ERR(dsi->mod_clk)) {1158 dev_err(dev, "Couldn't get the DSI mod clock\n");1159 ret = PTR_ERR(dsi->mod_clk);1160 goto err_attach_clk;1161 }1162 1163 /*1164 * In order to operate properly, the module clock on the1165 * A31 variant always seems to be set to 297MHz.1166 */1167 if (variant->set_mod_clk)1168 clk_set_rate_exclusive(dsi->mod_clk, 297000000);1169 }1170 1171 dsi->dphy = devm_phy_get(dev, "dphy");1172 if (IS_ERR(dsi->dphy)) {1173 dev_err(dev, "Couldn't get the MIPI D-PHY\n");1174 ret = PTR_ERR(dsi->dphy);1175 goto err_unprotect_clk;1176 }1177 1178 ret = mipi_dsi_host_register(&dsi->host);1179 if (ret) {1180 dev_err(dev, "Couldn't register MIPI-DSI host\n");1181 goto err_unprotect_clk;1182 }1183 1184 ret = component_add(&pdev->dev, &sun6i_dsi_ops);1185 if (ret) {1186 dev_err(dev, "Couldn't register our component\n");1187 goto err_remove_dsi_host;1188 }1189 1190 return 0;1191 1192err_remove_dsi_host:1193 mipi_dsi_host_unregister(&dsi->host);1194err_unprotect_clk:1195 if (dsi->variant->has_mod_clk && dsi->variant->set_mod_clk)1196 clk_rate_exclusive_put(dsi->mod_clk);1197err_attach_clk:1198 regmap_mmio_detach_clk(dsi->regs);1199 1200 return ret;1201}1202 1203static void sun6i_dsi_remove(struct platform_device *pdev)1204{1205 struct device *dev = &pdev->dev;1206 struct sun6i_dsi *dsi = dev_get_drvdata(dev);1207 1208 component_del(&pdev->dev, &sun6i_dsi_ops);1209 mipi_dsi_host_unregister(&dsi->host);1210 if (dsi->variant->has_mod_clk && dsi->variant->set_mod_clk)1211 clk_rate_exclusive_put(dsi->mod_clk);1212 1213 regmap_mmio_detach_clk(dsi->regs);1214}1215 1216static const struct sun6i_dsi_variant sun6i_a31_mipi_dsi_variant = {1217 .has_mod_clk = true,1218 .set_mod_clk = true,1219};1220 1221static const struct sun6i_dsi_variant sun50i_a64_mipi_dsi_variant = {1222};1223 1224static const struct sun6i_dsi_variant sun50i_a100_mipi_dsi_variant = {1225 .has_mod_clk = true,1226};1227 1228static const struct of_device_id sun6i_dsi_of_table[] = {1229 {1230 .compatible = "allwinner,sun6i-a31-mipi-dsi",1231 .data = &sun6i_a31_mipi_dsi_variant,1232 },1233 {1234 .compatible = "allwinner,sun50i-a64-mipi-dsi",1235 .data = &sun50i_a64_mipi_dsi_variant,1236 },1237 {1238 .compatible = "allwinner,sun50i-a100-mipi-dsi",1239 .data = &sun50i_a100_mipi_dsi_variant,1240 },1241 { }1242};1243MODULE_DEVICE_TABLE(of, sun6i_dsi_of_table);1244 1245static struct platform_driver sun6i_dsi_platform_driver = {1246 .probe = sun6i_dsi_probe,1247 .remove_new = sun6i_dsi_remove,1248 .driver = {1249 .name = "sun6i-mipi-dsi",1250 .of_match_table = sun6i_dsi_of_table,1251 },1252};1253module_platform_driver(sun6i_dsi_platform_driver);1254 1255MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");1256MODULE_DESCRIPTION("Allwinner A31 DSI Driver");1257MODULE_LICENSE("GPL");1258