1 /* 2 * Copyright (c) 2015 MediaTek Inc. 3 * Authors: 4 * YT Shen <yt.shen@mediatek.com> 5 * CK Hu <ck.hu@mediatek.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #include <linux/clk.h> 18 #include <linux/of.h> 19 #include <linux/of_address.h> 20 #include <linux/of_irq.h> 21 #include <linux/of_platform.h> 22 #include <linux/platform_device.h> 23 #include <drm/drmP.h> 24 #include "mtk_drm_drv.h" 25 #include "mtk_drm_plane.h" 26 #include "mtk_drm_ddp_comp.h" 27 #include "mtk_drm_crtc.h" 28 29 #define DISP_OD_EN 0x0000 30 #define DISP_OD_INTEN 0x0008 31 #define DISP_OD_INTSTA 0x000c 32 #define DISP_OD_CFG 0x0020 33 #define DISP_OD_SIZE 0x0030 34 #define DISP_DITHER_5 0x0114 35 #define DISP_DITHER_7 0x011c 36 #define DISP_DITHER_15 0x013c 37 #define DISP_DITHER_16 0x0140 38 39 #define DISP_REG_UFO_START 0x0000 40 41 #define DISP_COLOR_CFG_MAIN 0x0400 42 #define DISP_COLOR_START_MT2701 0x0f00 43 #define DISP_COLOR_START_MT8173 0x0c00 44 #define DISP_COLOR_START(comp) ((comp)->data->color_offset) 45 #define DISP_COLOR_WIDTH(comp) (DISP_COLOR_START(comp) + 0x50) 46 #define DISP_COLOR_HEIGHT(comp) (DISP_COLOR_START(comp) + 0x54) 47 48 #define DISP_AAL_EN 0x0000 49 #define DISP_AAL_SIZE 0x0030 50 51 #define DISP_GAMMA_EN 0x0000 52 #define DISP_GAMMA_CFG 0x0020 53 #define DISP_GAMMA_SIZE 0x0030 54 #define DISP_GAMMA_LUT 0x0700 55 56 #define LUT_10BIT_MASK 0x03ff 57 58 #define COLOR_BYPASS_ALL BIT(7) 59 #define COLOR_SEQ_SEL BIT(13) 60 61 #define OD_RELAYMODE BIT(0) 62 63 #define UFO_BYPASS BIT(2) 64 65 #define AAL_EN BIT(0) 66 67 #define GAMMA_EN BIT(0) 68 #define GAMMA_LUT_EN BIT(1) 69 70 #define DISP_DITHERING BIT(2) 71 #define DITHER_LSB_ERR_SHIFT_R(x) (((x) & 0x7) << 28) 72 #define DITHER_OVFLW_BIT_R(x) (((x) & 0x7) << 24) 73 #define DITHER_ADD_LSHIFT_R(x) (((x) & 0x7) << 20) 74 #define DITHER_ADD_RSHIFT_R(x) (((x) & 0x7) << 16) 75 #define DITHER_NEW_BIT_MODE BIT(0) 76 #define DITHER_LSB_ERR_SHIFT_B(x) (((x) & 0x7) << 28) 77 #define DITHER_OVFLW_BIT_B(x) (((x) & 0x7) << 24) 78 #define DITHER_ADD_LSHIFT_B(x) (((x) & 0x7) << 20) 79 #define DITHER_ADD_RSHIFT_B(x) (((x) & 0x7) << 16) 80 #define DITHER_LSB_ERR_SHIFT_G(x) (((x) & 0x7) << 12) 81 #define DITHER_OVFLW_BIT_G(x) (((x) & 0x7) << 8) 82 #define DITHER_ADD_LSHIFT_G(x) (((x) & 0x7) << 4) 83 #define DITHER_ADD_RSHIFT_G(x) (((x) & 0x7) << 0) 84 85 struct mtk_disp_color_data { 86 unsigned int color_offset; 87 }; 88 89 struct mtk_disp_color { 90 struct mtk_ddp_comp ddp_comp; 91 const struct mtk_disp_color_data *data; 92 }; 93 94 static inline struct mtk_disp_color *comp_to_color(struct mtk_ddp_comp *comp) 95 { 96 return container_of(comp, struct mtk_disp_color, ddp_comp); 97 } 98 99 void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc, 100 unsigned int CFG) 101 { 102 /* If bpc equal to 0, the dithering function didn't be enabled */ 103 if (bpc == 0) 104 return; 105 106 if (bpc >= MTK_MIN_BPC) { 107 writel(0, comp->regs + DISP_DITHER_5); 108 writel(0, comp->regs + DISP_DITHER_7); 109 writel(DITHER_LSB_ERR_SHIFT_R(MTK_MAX_BPC - bpc) | 110 DITHER_ADD_LSHIFT_R(MTK_MAX_BPC - bpc) | 111 DITHER_NEW_BIT_MODE, 112 comp->regs + DISP_DITHER_15); 113 writel(DITHER_LSB_ERR_SHIFT_B(MTK_MAX_BPC - bpc) | 114 DITHER_ADD_LSHIFT_B(MTK_MAX_BPC - bpc) | 115 DITHER_LSB_ERR_SHIFT_G(MTK_MAX_BPC - bpc) | 116 DITHER_ADD_LSHIFT_G(MTK_MAX_BPC - bpc), 117 comp->regs + DISP_DITHER_16); 118 writel(DISP_DITHERING, comp->regs + CFG); 119 } 120 } 121 122 static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w, 123 unsigned int h, unsigned int vrefresh, 124 unsigned int bpc) 125 { 126 struct mtk_disp_color *color = comp_to_color(comp); 127 128 writel(w, comp->regs + DISP_COLOR_WIDTH(color)); 129 writel(h, comp->regs + DISP_COLOR_HEIGHT(color)); 130 } 131 132 static void mtk_color_start(struct mtk_ddp_comp *comp) 133 { 134 struct mtk_disp_color *color = comp_to_color(comp); 135 136 writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL, 137 comp->regs + DISP_COLOR_CFG_MAIN); 138 writel(0x1, comp->regs + DISP_COLOR_START(color)); 139 } 140 141 static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w, 142 unsigned int h, unsigned int vrefresh, 143 unsigned int bpc) 144 { 145 writel(w << 16 | h, comp->regs + DISP_OD_SIZE); 146 writel(OD_RELAYMODE, comp->regs + DISP_OD_CFG); 147 mtk_dither_set(comp, bpc, DISP_OD_CFG); 148 } 149 150 static void mtk_od_start(struct mtk_ddp_comp *comp) 151 { 152 writel(1, comp->regs + DISP_OD_EN); 153 } 154 155 static void mtk_ufoe_start(struct mtk_ddp_comp *comp) 156 { 157 writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START); 158 } 159 160 static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w, 161 unsigned int h, unsigned int vrefresh, 162 unsigned int bpc) 163 { 164 writel(h << 16 | w, comp->regs + DISP_AAL_SIZE); 165 } 166 167 static void mtk_aal_start(struct mtk_ddp_comp *comp) 168 { 169 writel(AAL_EN, comp->regs + DISP_AAL_EN); 170 } 171 172 static void mtk_aal_stop(struct mtk_ddp_comp *comp) 173 { 174 writel_relaxed(0x0, comp->regs + DISP_AAL_EN); 175 } 176 177 static void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w, 178 unsigned int h, unsigned int vrefresh, 179 unsigned int bpc) 180 { 181 writel(h << 16 | w, comp->regs + DISP_GAMMA_SIZE); 182 mtk_dither_set(comp, bpc, DISP_GAMMA_CFG); 183 } 184 185 static void mtk_gamma_start(struct mtk_ddp_comp *comp) 186 { 187 writel(GAMMA_EN, comp->regs + DISP_GAMMA_EN); 188 } 189 190 static void mtk_gamma_stop(struct mtk_ddp_comp *comp) 191 { 192 writel_relaxed(0x0, comp->regs + DISP_GAMMA_EN); 193 } 194 195 static void mtk_gamma_set(struct mtk_ddp_comp *comp, 196 struct drm_crtc_state *state) 197 { 198 unsigned int i, reg; 199 struct drm_color_lut *lut; 200 void __iomem *lut_base; 201 u32 word; 202 203 if (state->gamma_lut) { 204 reg = readl(comp->regs + DISP_GAMMA_CFG); 205 reg = reg | GAMMA_LUT_EN; 206 writel(reg, comp->regs + DISP_GAMMA_CFG); 207 lut_base = comp->regs + DISP_GAMMA_LUT; 208 lut = (struct drm_color_lut *)state->gamma_lut->data; 209 for (i = 0; i < MTK_LUT_SIZE; i++) { 210 word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) + 211 (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) + 212 ((lut[i].blue >> 6) & LUT_10BIT_MASK); 213 writel(word, (lut_base + i * 4)); 214 } 215 } 216 } 217 218 static const struct mtk_ddp_comp_funcs ddp_aal = { 219 .gamma_set = mtk_gamma_set, 220 .config = mtk_aal_config, 221 .start = mtk_aal_start, 222 .stop = mtk_aal_stop, 223 }; 224 225 static const struct mtk_ddp_comp_funcs ddp_gamma = { 226 .gamma_set = mtk_gamma_set, 227 .config = mtk_gamma_config, 228 .start = mtk_gamma_start, 229 .stop = mtk_gamma_stop, 230 }; 231 232 static const struct mtk_ddp_comp_funcs ddp_color = { 233 .config = mtk_color_config, 234 .start = mtk_color_start, 235 }; 236 237 static const struct mtk_ddp_comp_funcs ddp_od = { 238 .config = mtk_od_config, 239 .start = mtk_od_start, 240 }; 241 242 static const struct mtk_ddp_comp_funcs ddp_ufoe = { 243 .start = mtk_ufoe_start, 244 }; 245 246 static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = { 247 [MTK_DISP_OVL] = "ovl", 248 [MTK_DISP_RDMA] = "rdma", 249 [MTK_DISP_WDMA] = "wdma", 250 [MTK_DISP_COLOR] = "color", 251 [MTK_DISP_AAL] = "aal", 252 [MTK_DISP_GAMMA] = "gamma", 253 [MTK_DISP_UFOE] = "ufoe", 254 [MTK_DSI] = "dsi", 255 [MTK_DPI] = "dpi", 256 [MTK_DISP_PWM] = "pwm", 257 [MTK_DISP_MUTEX] = "mutex", 258 [MTK_DISP_OD] = "od", 259 [MTK_DISP_BLS] = "bls", 260 }; 261 262 struct mtk_ddp_comp_match { 263 enum mtk_ddp_comp_type type; 264 int alias_id; 265 const struct mtk_ddp_comp_funcs *funcs; 266 }; 267 268 static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = { 269 [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, &ddp_aal }, 270 [DDP_COMPONENT_BLS] = { MTK_DISP_BLS, 0, NULL }, 271 [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color }, 272 [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color }, 273 [DDP_COMPONENT_DPI0] = { MTK_DPI, 0, NULL }, 274 [DDP_COMPONENT_DSI0] = { MTK_DSI, 0, NULL }, 275 [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL }, 276 [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, &ddp_gamma }, 277 [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od }, 278 [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, NULL }, 279 [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, NULL }, 280 [DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, NULL }, 281 [DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, NULL }, 282 [DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, NULL }, 283 [DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, NULL }, 284 [DDP_COMPONENT_UFOE] = { MTK_DISP_UFOE, 0, &ddp_ufoe }, 285 [DDP_COMPONENT_WDMA0] = { MTK_DISP_WDMA, 0, NULL }, 286 [DDP_COMPONENT_WDMA1] = { MTK_DISP_WDMA, 1, NULL }, 287 }; 288 289 static const struct mtk_disp_color_data mt2701_color_driver_data = { 290 .color_offset = DISP_COLOR_START_MT2701, 291 }; 292 293 static const struct mtk_disp_color_data mt8173_color_driver_data = { 294 .color_offset = DISP_COLOR_START_MT8173, 295 }; 296 297 static const struct of_device_id mtk_disp_color_driver_dt_match[] = { 298 { .compatible = "mediatek,mt2701-disp-color", 299 .data = &mt2701_color_driver_data}, 300 { .compatible = "mediatek,mt8173-disp-color", 301 .data = &mt8173_color_driver_data}, 302 {}, 303 }; 304 305 int mtk_ddp_comp_get_id(struct device_node *node, 306 enum mtk_ddp_comp_type comp_type) 307 { 308 int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]); 309 int i; 310 311 for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) { 312 if (comp_type == mtk_ddp_matches[i].type && 313 (id < 0 || id == mtk_ddp_matches[i].alias_id)) 314 return i; 315 } 316 317 return -EINVAL; 318 } 319 320 int mtk_ddp_comp_init(struct device *dev, struct device_node *node, 321 struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id, 322 const struct mtk_ddp_comp_funcs *funcs) 323 { 324 enum mtk_ddp_comp_type type; 325 struct device_node *larb_node; 326 struct platform_device *larb_pdev; 327 const struct of_device_id *match; 328 struct mtk_disp_color *color; 329 330 if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX) 331 return -EINVAL; 332 333 type = mtk_ddp_matches[comp_id].type; 334 if (type == MTK_DISP_COLOR) { 335 devm_kfree(dev, comp); 336 color = devm_kzalloc(dev, sizeof(*color), GFP_KERNEL); 337 if (!color) 338 return -ENOMEM; 339 340 match = of_match_node(mtk_disp_color_driver_dt_match, node); 341 color->data = match->data; 342 comp = &color->ddp_comp; 343 } 344 345 comp->id = comp_id; 346 comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs; 347 348 if (comp_id == DDP_COMPONENT_BLS || 349 comp_id == DDP_COMPONENT_DPI0 || 350 comp_id == DDP_COMPONENT_DSI0 || 351 comp_id == DDP_COMPONENT_PWM0) { 352 comp->regs = NULL; 353 comp->clk = NULL; 354 comp->irq = 0; 355 return 0; 356 } 357 358 comp->regs = of_iomap(node, 0); 359 comp->irq = of_irq_get(node, 0); 360 comp->clk = of_clk_get(node, 0); 361 if (IS_ERR(comp->clk)) 362 comp->clk = NULL; 363 364 /* Only DMA capable components need the LARB property */ 365 comp->larb_dev = NULL; 366 if (type != MTK_DISP_OVL && 367 type != MTK_DISP_RDMA && 368 type != MTK_DISP_WDMA) 369 return 0; 370 371 larb_node = of_parse_phandle(node, "mediatek,larb", 0); 372 if (!larb_node) { 373 dev_err(dev, 374 "Missing mediadek,larb phandle in %s node\n", 375 node->full_name); 376 return -EINVAL; 377 } 378 379 larb_pdev = of_find_device_by_node(larb_node); 380 if (!larb_pdev) { 381 dev_warn(dev, "Waiting for larb device %s\n", 382 larb_node->full_name); 383 of_node_put(larb_node); 384 return -EPROBE_DEFER; 385 } 386 of_node_put(larb_node); 387 388 comp->larb_dev = &larb_pdev->dev; 389 390 return 0; 391 } 392 393 int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp) 394 { 395 struct mtk_drm_private *private = drm->dev_private; 396 397 if (private->ddp_comp[comp->id]) 398 return -EBUSY; 399 400 private->ddp_comp[comp->id] = comp; 401 return 0; 402 } 403 404 void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp) 405 { 406 struct mtk_drm_private *private = drm->dev_private; 407 408 private->ddp_comp[comp->id] = NULL; 409 } 410