Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/exynos: mic: add MIC driver

MIC(Mobile image compressor) is newly added IP in Exynos5433. MIC
resides between decon and mipi dsim, and compresses frame data by 50%.
With dsi, not display port, to send frame data to the panel, the
bandwidth is not enough. That is why this compressor is introduced.

Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>

authored by

Hyungwon Hwang and committed by
Inki Dae
77bbd891 8ccd0d0c

+552 -3
+51
Documentation/devicetree/bindings/video/exynos-mic.txt
··· 1 + Device-Tree bindings for Samsung Exynos SoC mobile image compressor (MIC) 2 + 3 + MIC (mobile image compressor) resides between decon and mipi dsi. Mipi dsi is 4 + not capable to transfer high resoltuion frame data as decon can send. MIC 5 + solves this problem by compressing the frame data by 1/2 before it is 6 + transferred through mipi dsi. The compressed frame data must be uncompressed in 7 + the panel PCB. 8 + 9 + Required properties: 10 + - compatible: value should be "samsung,exynos5433-mic". 11 + - reg: physical base address and length of the MIC registers set and system 12 + register of mic. 13 + - clocks: must include clock specifiers corresponding to entries in the 14 + clock-names property. 15 + - clock-names: list of clock names sorted in the same order as the clocks 16 + property. Must contain "pclk_mic0", "sclk_rgb_vclk_to_mic0". 17 + - samsung,disp-syscon: the reference node for syscon for DISP block. 18 + - ports: contains a port which is connected to decon node and dsi node. 19 + address-cells and size-cells must 1 and 0, respectively. 20 + - port: contains an endpoint node which is connected to the endpoint in the 21 + decon node or dsi node. The reg value must be 0 and 1 respectively. 22 + 23 + Example: 24 + SoC specific DT entry: 25 + mic: mic@13930000 { 26 + compatible = "samsung,exynos5433-mic"; 27 + reg = <0x13930000 0x48>; 28 + clocks = <&cmu_disp CLK_PCLK_MIC0>, 29 + <&cmu_disp CLK_SCLK_RGB_VCLK_TO_MIC0>; 30 + clock-names = "pclk_mic0", "sclk_rgb_vclk_to_mic0"; 31 + samsung,disp-syscon = <&syscon_disp>; 32 + 33 + ports { 34 + #address-cells = <1>; 35 + #size-cells = <0>; 36 + 37 + port@0 { 38 + reg = <0>; 39 + mic_to_decon: endpoint { 40 + remote-endpoint = <&decon_to_mic>; 41 + }; 42 + }; 43 + 44 + port@1 { 45 + reg = <1>; 46 + mic_to_dsi: endpoint { 47 + remote-endpoint = <&dsi_to_mic>; 48 + }; 49 + }; 50 + }; 51 + };
+6
drivers/gpu/drm/exynos/Kconfig
··· 103 103 depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM 104 104 help 105 105 Choose this option if you want to use Exynos GSC for DRM. 106 + 107 + config DRM_EXYNOS_MIC 108 + bool "Exynos DRM MIC" 109 + depends on (DRM_EXYNOS && DRM_EXYNOS5433_DECON) 110 + help 111 + Choose this option if you want to use Exynos MIC for DRM.
+1
drivers/gpu/drm/exynos/Makefile
··· 22 22 exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o 23 23 exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o 24 24 exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o 25 + exynosdrm-$(CONFIG_DRM_EXYNOS_MIC) += exynos_drm_mic.o 25 26 26 27 obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
+3 -3
drivers/gpu/drm/exynos/exynos_drm_drv.c
··· 357 357 #ifdef CONFIG_DRM_EXYNOS7_DECON 358 358 &decon_driver, 359 359 #endif 360 + #ifdef CONFIG_DRM_EXYNOS_MIC 361 + &mic_driver, 362 + #endif 360 363 #ifdef CONFIG_DRM_EXYNOS_DP 361 364 &dp_driver, 362 365 #endif ··· 373 370 }; 374 371 375 372 static struct platform_driver *const exynos_drm_non_kms_drivers[] = { 376 - #ifdef CONFIG_DRM_EXYNOS_MIC 377 - &mic_driver, 378 - #endif 379 373 #ifdef CONFIG_DRM_EXYNOS_G2D 380 374 &g2d_driver, 381 375 #endif
+1
drivers/gpu/drm/exynos/exynos_drm_drv.h
··· 314 314 extern struct platform_driver rotator_driver; 315 315 extern struct platform_driver gsc_driver; 316 316 extern struct platform_driver ipp_driver; 317 + extern struct platform_driver mic_driver; 317 318 #endif
+490
drivers/gpu/drm/exynos/exynos_drm_mic.c
··· 1 + /* 2 + * Copyright (C) 2015 Samsung Electronics Co.Ltd 3 + * Authors: 4 + * Hyungwon Hwang <human.hwang@samsung.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundationr 9 + */ 10 + 11 + #include <linux/platform_device.h> 12 + #include <video/of_videomode.h> 13 + #include <linux/of_address.h> 14 + #include <video/videomode.h> 15 + #include <linux/module.h> 16 + #include <linux/delay.h> 17 + #include <linux/mutex.h> 18 + #include <linux/of.h> 19 + #include <linux/of_graph.h> 20 + #include <linux/clk.h> 21 + #include <drm/drmP.h> 22 + #include <linux/mfd/syscon.h> 23 + #include <linux/regmap.h> 24 + 25 + /* Sysreg registers for MIC */ 26 + #define DSD_CFG_MUX 0x1004 27 + #define MIC0_RGB_MUX (1 << 0) 28 + #define MIC0_I80_MUX (1 << 1) 29 + #define MIC0_ON_MUX (1 << 5) 30 + 31 + /* MIC registers */ 32 + #define MIC_OP 0x0 33 + #define MIC_IP_VER 0x0004 34 + #define MIC_V_TIMING_0 0x0008 35 + #define MIC_V_TIMING_1 0x000C 36 + #define MIC_IMG_SIZE 0x0010 37 + #define MIC_INPUT_TIMING_0 0x0014 38 + #define MIC_INPUT_TIMING_1 0x0018 39 + #define MIC_2D_OUTPUT_TIMING_0 0x001C 40 + #define MIC_2D_OUTPUT_TIMING_1 0x0020 41 + #define MIC_2D_OUTPUT_TIMING_2 0x0024 42 + #define MIC_3D_OUTPUT_TIMING_0 0x0028 43 + #define MIC_3D_OUTPUT_TIMING_1 0x002C 44 + #define MIC_3D_OUTPUT_TIMING_2 0x0030 45 + #define MIC_CORE_PARA_0 0x0034 46 + #define MIC_CORE_PARA_1 0x0038 47 + #define MIC_CTC_CTRL 0x0040 48 + #define MIC_RD_DATA 0x0044 49 + 50 + #define MIC_UPD_REG (1 << 31) 51 + #define MIC_ON_REG (1 << 30) 52 + #define MIC_TD_ON_REG (1 << 29) 53 + #define MIC_BS_CHG_OUT (1 << 16) 54 + #define MIC_VIDEO_TYPE(x) (((x) & 0xf) << 12) 55 + #define MIC_PSR_EN (1 << 5) 56 + #define MIC_SW_RST (1 << 4) 57 + #define MIC_ALL_RST (1 << 3) 58 + #define MIC_CORE_VER_CONTROL (1 << 2) 59 + #define MIC_MODE_SEL_COMMAND_MODE (1 << 1) 60 + #define MIC_MODE_SEL_MASK (1 << 1) 61 + #define MIC_CORE_EN (1 << 0) 62 + 63 + #define MIC_V_PULSE_WIDTH(x) (((x) & 0x3fff) << 16) 64 + #define MIC_V_PERIOD_LINE(x) ((x) & 0x3fff) 65 + 66 + #define MIC_VBP_SIZE(x) (((x) & 0x3fff) << 16) 67 + #define MIC_VFP_SIZE(x) ((x) & 0x3fff) 68 + 69 + #define MIC_IMG_V_SIZE(x) (((x) & 0x3fff) << 16) 70 + #define MIC_IMG_H_SIZE(x) ((x) & 0x3fff) 71 + 72 + #define MIC_H_PULSE_WIDTH_IN(x) (((x) & 0x3fff) << 16) 73 + #define MIC_H_PERIOD_PIXEL_IN(x) ((x) & 0x3fff) 74 + 75 + #define MIC_HBP_SIZE_IN(x) (((x) & 0x3fff) << 16) 76 + #define MIC_HFP_SIZE_IN(x) ((x) & 0x3fff) 77 + 78 + #define MIC_H_PULSE_WIDTH_2D(x) (((x) & 0x3fff) << 16) 79 + #define MIC_H_PERIOD_PIXEL_2D(x) ((x) & 0x3fff) 80 + 81 + #define MIC_HBP_SIZE_2D(x) (((x) & 0x3fff) << 16) 82 + #define MIC_HFP_SIZE_2D(x) ((x) & 0x3fff) 83 + 84 + #define MIC_BS_SIZE_2D(x) ((x) & 0x3fff) 85 + 86 + enum { 87 + ENDPOINT_DECON_NODE, 88 + ENDPOINT_DSI_NODE, 89 + NUM_ENDPOINTS 90 + }; 91 + 92 + static char *clk_names[] = { "pclk_mic0", "sclk_rgb_vclk_to_mic0" }; 93 + #define NUM_CLKS ARRAY_SIZE(clk_names) 94 + static DEFINE_MUTEX(mic_mutex); 95 + 96 + struct exynos_mic { 97 + struct device *dev; 98 + void __iomem *reg; 99 + struct regmap *sysreg; 100 + struct clk *clks[NUM_CLKS]; 101 + 102 + bool i80_mode; 103 + struct videomode vm; 104 + struct drm_encoder *encoder; 105 + struct drm_bridge bridge; 106 + 107 + bool enabled; 108 + }; 109 + 110 + static void mic_set_path(struct exynos_mic *mic, bool enable) 111 + { 112 + int ret; 113 + unsigned int val; 114 + 115 + ret = regmap_read(mic->sysreg, DSD_CFG_MUX, &val); 116 + if (ret) { 117 + DRM_ERROR("mic: Failed to read system register\n"); 118 + return; 119 + } 120 + 121 + if (enable) { 122 + if (mic->i80_mode) 123 + val |= MIC0_I80_MUX; 124 + else 125 + val |= MIC0_RGB_MUX; 126 + 127 + val |= MIC0_ON_MUX; 128 + } else 129 + val &= ~(MIC0_RGB_MUX | MIC0_I80_MUX | MIC0_ON_MUX); 130 + 131 + regmap_write(mic->sysreg, DSD_CFG_MUX, val); 132 + if (ret) 133 + DRM_ERROR("mic: Failed to read system register\n"); 134 + } 135 + 136 + static int mic_sw_reset(struct exynos_mic *mic) 137 + { 138 + unsigned int retry = 100; 139 + int ret; 140 + 141 + writel(MIC_SW_RST, mic->reg + MIC_OP); 142 + 143 + while (retry-- > 0) { 144 + ret = readl(mic->reg + MIC_OP); 145 + if (!(ret & MIC_SW_RST)) 146 + return 0; 147 + 148 + udelay(10); 149 + } 150 + 151 + return -ETIMEDOUT; 152 + } 153 + 154 + static void mic_set_porch_timing(struct exynos_mic *mic) 155 + { 156 + struct videomode vm = mic->vm; 157 + u32 reg; 158 + 159 + reg = MIC_V_PULSE_WIDTH(vm.vsync_len) + 160 + MIC_V_PERIOD_LINE(vm.vsync_len + vm.vactive + 161 + vm.vback_porch + vm.vfront_porch); 162 + writel(reg, mic->reg + MIC_V_TIMING_0); 163 + 164 + reg = MIC_VBP_SIZE(vm.vback_porch) + 165 + MIC_VFP_SIZE(vm.vfront_porch); 166 + writel(reg, mic->reg + MIC_V_TIMING_1); 167 + 168 + reg = MIC_V_PULSE_WIDTH(vm.hsync_len) + 169 + MIC_V_PERIOD_LINE(vm.hsync_len + vm.hactive + 170 + vm.hback_porch + vm.hfront_porch); 171 + writel(reg, mic->reg + MIC_INPUT_TIMING_0); 172 + 173 + reg = MIC_VBP_SIZE(vm.hback_porch) + 174 + MIC_VFP_SIZE(vm.hfront_porch); 175 + writel(reg, mic->reg + MIC_INPUT_TIMING_1); 176 + } 177 + 178 + static void mic_set_img_size(struct exynos_mic *mic) 179 + { 180 + struct videomode *vm = &mic->vm; 181 + u32 reg; 182 + 183 + reg = MIC_IMG_H_SIZE(vm->hactive) + 184 + MIC_IMG_V_SIZE(vm->vactive); 185 + 186 + writel(reg, mic->reg + MIC_IMG_SIZE); 187 + } 188 + 189 + static void mic_set_output_timing(struct exynos_mic *mic) 190 + { 191 + struct videomode vm = mic->vm; 192 + u32 reg, bs_size_2d; 193 + 194 + DRM_DEBUG("w: %u, h: %u\n", vm.hactive, vm.vactive); 195 + bs_size_2d = ((vm.hactive >> 2) << 1) + (vm.vactive % 4); 196 + reg = MIC_BS_SIZE_2D(bs_size_2d); 197 + writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_2); 198 + 199 + if (!mic->i80_mode) { 200 + reg = MIC_H_PULSE_WIDTH_2D(vm.hsync_len) + 201 + MIC_H_PERIOD_PIXEL_2D(vm.hsync_len + bs_size_2d + 202 + vm.hback_porch + vm.hfront_porch); 203 + writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_0); 204 + 205 + reg = MIC_HBP_SIZE_2D(vm.hback_porch) + 206 + MIC_H_PERIOD_PIXEL_2D(vm.hfront_porch); 207 + writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_1); 208 + } 209 + } 210 + 211 + static void mic_set_reg_on(struct exynos_mic *mic, bool enable) 212 + { 213 + u32 reg = readl(mic->reg + MIC_OP); 214 + 215 + if (enable) { 216 + reg &= ~(MIC_MODE_SEL_MASK | MIC_CORE_VER_CONTROL | MIC_PSR_EN); 217 + reg |= (MIC_CORE_EN | MIC_BS_CHG_OUT | MIC_ON_REG); 218 + 219 + reg &= ~MIC_MODE_SEL_COMMAND_MODE; 220 + if (mic->i80_mode) 221 + reg |= MIC_MODE_SEL_COMMAND_MODE; 222 + } else { 223 + reg &= ~MIC_CORE_EN; 224 + } 225 + 226 + reg |= MIC_UPD_REG; 227 + writel(reg, mic->reg + MIC_OP); 228 + } 229 + 230 + static struct device_node *get_remote_node(struct device_node *from, int reg) 231 + { 232 + struct device_node *endpoint = NULL, *remote_node = NULL; 233 + 234 + endpoint = of_graph_get_endpoint_by_regs(from, reg, -1); 235 + if (!endpoint) { 236 + DRM_ERROR("mic: Failed to find remote port from %s", 237 + from->full_name); 238 + goto exit; 239 + } 240 + 241 + remote_node = of_graph_get_remote_port_parent(endpoint); 242 + if (!remote_node) { 243 + DRM_ERROR("mic: Failed to find remote port parent from %s", 244 + from->full_name); 245 + goto exit; 246 + } 247 + 248 + exit: 249 + of_node_put(endpoint); 250 + return remote_node; 251 + } 252 + 253 + static int parse_dt(struct exynos_mic *mic) 254 + { 255 + int ret = 0, i, j; 256 + struct device_node *remote_node; 257 + struct device_node *nodes[3]; 258 + 259 + /* 260 + * The order of endpoints does matter. 261 + * The first node must be for decon and the second one must be for dsi. 262 + */ 263 + for (i = 0, j = 0; i < NUM_ENDPOINTS; i++) { 264 + remote_node = get_remote_node(mic->dev->of_node, i); 265 + if (!remote_node) { 266 + ret = -EPIPE; 267 + goto exit; 268 + } 269 + nodes[j++] = remote_node; 270 + 271 + switch (i) { 272 + case ENDPOINT_DECON_NODE: 273 + /* decon node */ 274 + if (of_get_child_by_name(remote_node, 275 + "i80-if-timings")) 276 + mic->i80_mode = 1; 277 + 278 + break; 279 + case ENDPOINT_DSI_NODE: 280 + /* panel node */ 281 + remote_node = get_remote_node(remote_node, 1); 282 + if (!remote_node) { 283 + ret = -EPIPE; 284 + goto exit; 285 + } 286 + nodes[j++] = remote_node; 287 + 288 + ret = of_get_videomode(remote_node, 289 + &mic->vm, 0); 290 + if (ret) { 291 + DRM_ERROR("mic: failed to get videomode"); 292 + goto exit; 293 + } 294 + 295 + break; 296 + default: 297 + DRM_ERROR("mic: Unknown endpoint from MIC"); 298 + break; 299 + } 300 + } 301 + 302 + exit: 303 + while (--j > -1) 304 + of_node_put(nodes[j]); 305 + 306 + return ret; 307 + } 308 + 309 + void mic_disable(struct drm_bridge *bridge) { } 310 + 311 + void mic_post_disable(struct drm_bridge *bridge) 312 + { 313 + struct exynos_mic *mic = bridge->driver_private; 314 + int i; 315 + 316 + mutex_lock(&mic_mutex); 317 + if (!mic->enabled) 318 + goto already_disabled; 319 + 320 + mic_set_path(mic, 0); 321 + 322 + for (i = NUM_CLKS - 1; i > -1; i--) 323 + clk_disable_unprepare(mic->clks[i]); 324 + 325 + mic->enabled = 0; 326 + 327 + already_disabled: 328 + mutex_unlock(&mic_mutex); 329 + } 330 + 331 + void mic_pre_enable(struct drm_bridge *bridge) 332 + { 333 + struct exynos_mic *mic = bridge->driver_private; 334 + int ret, i; 335 + 336 + mutex_lock(&mic_mutex); 337 + if (mic->enabled) 338 + goto already_enabled; 339 + 340 + for (i = 0; i < NUM_CLKS; i++) { 341 + ret = clk_prepare_enable(mic->clks[i]); 342 + if (ret < 0) { 343 + DRM_ERROR("Failed to enable clock (%s)\n", 344 + clk_names[i]); 345 + goto turn_off_clks; 346 + } 347 + } 348 + 349 + mic_set_path(mic, 1); 350 + 351 + ret = mic_sw_reset(mic); 352 + if (ret) { 353 + DRM_ERROR("Failed to reset\n"); 354 + goto turn_off_clks; 355 + } 356 + 357 + if (!mic->i80_mode) 358 + mic_set_porch_timing(mic); 359 + mic_set_img_size(mic); 360 + mic_set_output_timing(mic); 361 + mic_set_reg_on(mic, 1); 362 + mic->enabled = 1; 363 + mutex_unlock(&mic_mutex); 364 + 365 + return; 366 + 367 + turn_off_clks: 368 + while (--i > -1) 369 + clk_disable_unprepare(mic->clks[i]); 370 + already_enabled: 371 + mutex_unlock(&mic_mutex); 372 + } 373 + 374 + void mic_enable(struct drm_bridge *bridge) { } 375 + 376 + void mic_destroy(struct drm_bridge *bridge) 377 + { 378 + struct exynos_mic *mic = bridge->driver_private; 379 + int i; 380 + 381 + mutex_lock(&mic_mutex); 382 + if (!mic->enabled) 383 + goto already_disabled; 384 + 385 + for (i = NUM_CLKS - 1; i > -1; i--) 386 + clk_disable_unprepare(mic->clks[i]); 387 + 388 + already_disabled: 389 + mutex_unlock(&mic_mutex); 390 + } 391 + 392 + struct drm_bridge_funcs mic_bridge_funcs = { 393 + .disable = mic_disable, 394 + .post_disable = mic_post_disable, 395 + .pre_enable = mic_pre_enable, 396 + .enable = mic_enable, 397 + }; 398 + 399 + int exynos_mic_probe(struct platform_device *pdev) 400 + { 401 + struct device *dev = &pdev->dev; 402 + struct exynos_mic *mic; 403 + struct resource res; 404 + int ret, i; 405 + 406 + mic = devm_kzalloc(dev, sizeof(*mic), GFP_KERNEL); 407 + if (!mic) { 408 + DRM_ERROR("mic: Failed to allocate memory for MIC object\n"); 409 + ret = -ENOMEM; 410 + goto err; 411 + } 412 + 413 + mic->dev = dev; 414 + 415 + ret = parse_dt(mic); 416 + if (ret) 417 + goto err; 418 + 419 + ret = of_address_to_resource(dev->of_node, 0, &res); 420 + if (ret) { 421 + DRM_ERROR("mic: Failed to get mem region for MIC\n"); 422 + goto err; 423 + } 424 + mic->reg = devm_ioremap(dev, res.start, resource_size(&res)); 425 + if (!mic->reg) { 426 + DRM_ERROR("mic: Failed to remap for MIC\n"); 427 + ret = -ENOMEM; 428 + goto err; 429 + } 430 + 431 + mic->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, 432 + "samsung,disp-syscon"); 433 + if (IS_ERR(mic->sysreg)) { 434 + DRM_ERROR("mic: Failed to get system register.\n"); 435 + goto err; 436 + } 437 + 438 + mic->bridge.funcs = &mic_bridge_funcs; 439 + mic->bridge.of_node = dev->of_node; 440 + mic->bridge.driver_private = mic; 441 + ret = drm_bridge_add(&mic->bridge); 442 + if (ret) { 443 + DRM_ERROR("mic: Failed to add MIC to the global bridge list\n"); 444 + goto err; 445 + } 446 + 447 + for (i = 0; i < NUM_CLKS; i++) { 448 + mic->clks[i] = of_clk_get_by_name(dev->of_node, clk_names[i]); 449 + if (IS_ERR(mic->clks[i])) { 450 + DRM_ERROR("mic: Failed to get clock (%s)\n", 451 + clk_names[i]); 452 + ret = PTR_ERR(mic->clks[i]); 453 + goto err; 454 + } 455 + } 456 + 457 + DRM_DEBUG_KMS("MIC has been probed\n"); 458 + 459 + err: 460 + return ret; 461 + } 462 + 463 + static int exynos_mic_remove(struct platform_device *pdev) 464 + { 465 + struct exynos_mic *mic = platform_get_drvdata(pdev); 466 + int i; 467 + 468 + drm_bridge_remove(&mic->bridge); 469 + 470 + for (i = NUM_CLKS - 1; i > -1; i--) 471 + clk_put(mic->clks[i]); 472 + 473 + return 0; 474 + } 475 + 476 + static const struct of_device_id exynos_mic_of_match[] = { 477 + { .compatible = "samsung,exynos5433-mic" }, 478 + { } 479 + }; 480 + MODULE_DEVICE_TABLE(of, exynos_mic_of_match); 481 + 482 + struct platform_driver mic_driver = { 483 + .probe = exynos_mic_probe, 484 + .remove = exynos_mic_remove, 485 + .driver = { 486 + .name = "exynos-mic", 487 + .owner = THIS_MODULE, 488 + .of_match_table = exynos_mic_of_match, 489 + }, 490 + };