18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Samsung Electronics Co.Ltd 48c2ecf20Sopenharmony_ci * Authors: 58c2ecf20Sopenharmony_ci * Hyungwon Hwang <human.hwang@samsung.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/component.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <video/of_videomode.h> 228c2ecf20Sopenharmony_ci#include <video/videomode.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_encoder.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Sysreg registers for MIC */ 318c2ecf20Sopenharmony_ci#define DSD_CFG_MUX 0x1004 328c2ecf20Sopenharmony_ci#define MIC0_RGB_MUX (1 << 0) 338c2ecf20Sopenharmony_ci#define MIC0_I80_MUX (1 << 1) 348c2ecf20Sopenharmony_ci#define MIC0_ON_MUX (1 << 5) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* MIC registers */ 378c2ecf20Sopenharmony_ci#define MIC_OP 0x0 388c2ecf20Sopenharmony_ci#define MIC_IP_VER 0x0004 398c2ecf20Sopenharmony_ci#define MIC_V_TIMING_0 0x0008 408c2ecf20Sopenharmony_ci#define MIC_V_TIMING_1 0x000C 418c2ecf20Sopenharmony_ci#define MIC_IMG_SIZE 0x0010 428c2ecf20Sopenharmony_ci#define MIC_INPUT_TIMING_0 0x0014 438c2ecf20Sopenharmony_ci#define MIC_INPUT_TIMING_1 0x0018 448c2ecf20Sopenharmony_ci#define MIC_2D_OUTPUT_TIMING_0 0x001C 458c2ecf20Sopenharmony_ci#define MIC_2D_OUTPUT_TIMING_1 0x0020 468c2ecf20Sopenharmony_ci#define MIC_2D_OUTPUT_TIMING_2 0x0024 478c2ecf20Sopenharmony_ci#define MIC_3D_OUTPUT_TIMING_0 0x0028 488c2ecf20Sopenharmony_ci#define MIC_3D_OUTPUT_TIMING_1 0x002C 498c2ecf20Sopenharmony_ci#define MIC_3D_OUTPUT_TIMING_2 0x0030 508c2ecf20Sopenharmony_ci#define MIC_CORE_PARA_0 0x0034 518c2ecf20Sopenharmony_ci#define MIC_CORE_PARA_1 0x0038 528c2ecf20Sopenharmony_ci#define MIC_CTC_CTRL 0x0040 538c2ecf20Sopenharmony_ci#define MIC_RD_DATA 0x0044 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define MIC_UPD_REG (1 << 31) 568c2ecf20Sopenharmony_ci#define MIC_ON_REG (1 << 30) 578c2ecf20Sopenharmony_ci#define MIC_TD_ON_REG (1 << 29) 588c2ecf20Sopenharmony_ci#define MIC_BS_CHG_OUT (1 << 16) 598c2ecf20Sopenharmony_ci#define MIC_VIDEO_TYPE(x) (((x) & 0xf) << 12) 608c2ecf20Sopenharmony_ci#define MIC_PSR_EN (1 << 5) 618c2ecf20Sopenharmony_ci#define MIC_SW_RST (1 << 4) 628c2ecf20Sopenharmony_ci#define MIC_ALL_RST (1 << 3) 638c2ecf20Sopenharmony_ci#define MIC_CORE_VER_CONTROL (1 << 2) 648c2ecf20Sopenharmony_ci#define MIC_MODE_SEL_COMMAND_MODE (1 << 1) 658c2ecf20Sopenharmony_ci#define MIC_MODE_SEL_MASK (1 << 1) 668c2ecf20Sopenharmony_ci#define MIC_CORE_EN (1 << 0) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define MIC_V_PULSE_WIDTH(x) (((x) & 0x3fff) << 16) 698c2ecf20Sopenharmony_ci#define MIC_V_PERIOD_LINE(x) ((x) & 0x3fff) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define MIC_VBP_SIZE(x) (((x) & 0x3fff) << 16) 728c2ecf20Sopenharmony_ci#define MIC_VFP_SIZE(x) ((x) & 0x3fff) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define MIC_IMG_V_SIZE(x) (((x) & 0x3fff) << 16) 758c2ecf20Sopenharmony_ci#define MIC_IMG_H_SIZE(x) ((x) & 0x3fff) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define MIC_H_PULSE_WIDTH_IN(x) (((x) & 0x3fff) << 16) 788c2ecf20Sopenharmony_ci#define MIC_H_PERIOD_PIXEL_IN(x) ((x) & 0x3fff) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define MIC_HBP_SIZE_IN(x) (((x) & 0x3fff) << 16) 818c2ecf20Sopenharmony_ci#define MIC_HFP_SIZE_IN(x) ((x) & 0x3fff) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define MIC_H_PULSE_WIDTH_2D(x) (((x) & 0x3fff) << 16) 848c2ecf20Sopenharmony_ci#define MIC_H_PERIOD_PIXEL_2D(x) ((x) & 0x3fff) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define MIC_HBP_SIZE_2D(x) (((x) & 0x3fff) << 16) 878c2ecf20Sopenharmony_ci#define MIC_HFP_SIZE_2D(x) ((x) & 0x3fff) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define MIC_BS_SIZE_2D(x) ((x) & 0x3fff) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const char *const clk_names[] = { "pclk_mic0", "sclk_rgb_vclk_to_mic0" }; 928c2ecf20Sopenharmony_ci#define NUM_CLKS ARRAY_SIZE(clk_names) 938c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(mic_mutex); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct exynos_mic { 968c2ecf20Sopenharmony_ci struct device *dev; 978c2ecf20Sopenharmony_ci void __iomem *reg; 988c2ecf20Sopenharmony_ci struct regmap *sysreg; 998c2ecf20Sopenharmony_ci struct clk *clks[NUM_CLKS]; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci bool i80_mode; 1028c2ecf20Sopenharmony_ci struct videomode vm; 1038c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 1048c2ecf20Sopenharmony_ci struct drm_bridge bridge; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci bool enabled; 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void mic_set_path(struct exynos_mic *mic, bool enable) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int ret; 1128c2ecf20Sopenharmony_ci unsigned int val; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = regmap_read(mic->sysreg, DSD_CFG_MUX, &val); 1158c2ecf20Sopenharmony_ci if (ret) { 1168c2ecf20Sopenharmony_ci DRM_DEV_ERROR(mic->dev, 1178c2ecf20Sopenharmony_ci "mic: Failed to read system register\n"); 1188c2ecf20Sopenharmony_ci return; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (enable) { 1228c2ecf20Sopenharmony_ci if (mic->i80_mode) 1238c2ecf20Sopenharmony_ci val |= MIC0_I80_MUX; 1248c2ecf20Sopenharmony_ci else 1258c2ecf20Sopenharmony_ci val |= MIC0_RGB_MUX; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci val |= MIC0_ON_MUX; 1288c2ecf20Sopenharmony_ci } else 1298c2ecf20Sopenharmony_ci val &= ~(MIC0_RGB_MUX | MIC0_I80_MUX | MIC0_ON_MUX); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = regmap_write(mic->sysreg, DSD_CFG_MUX, val); 1328c2ecf20Sopenharmony_ci if (ret) 1338c2ecf20Sopenharmony_ci DRM_DEV_ERROR(mic->dev, 1348c2ecf20Sopenharmony_ci "mic: Failed to read system register\n"); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int mic_sw_reset(struct exynos_mic *mic) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci unsigned int retry = 100; 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci writel(MIC_SW_RST, mic->reg + MIC_OP); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci while (retry-- > 0) { 1458c2ecf20Sopenharmony_ci ret = readl(mic->reg + MIC_OP); 1468c2ecf20Sopenharmony_ci if (!(ret & MIC_SW_RST)) 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci udelay(10); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void mic_set_porch_timing(struct exynos_mic *mic) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct videomode vm = mic->vm; 1588c2ecf20Sopenharmony_ci u32 reg; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci reg = MIC_V_PULSE_WIDTH(vm.vsync_len) + 1618c2ecf20Sopenharmony_ci MIC_V_PERIOD_LINE(vm.vsync_len + vm.vactive + 1628c2ecf20Sopenharmony_ci vm.vback_porch + vm.vfront_porch); 1638c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_V_TIMING_0); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci reg = MIC_VBP_SIZE(vm.vback_porch) + 1668c2ecf20Sopenharmony_ci MIC_VFP_SIZE(vm.vfront_porch); 1678c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_V_TIMING_1); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci reg = MIC_V_PULSE_WIDTH(vm.hsync_len) + 1708c2ecf20Sopenharmony_ci MIC_V_PERIOD_LINE(vm.hsync_len + vm.hactive + 1718c2ecf20Sopenharmony_ci vm.hback_porch + vm.hfront_porch); 1728c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_INPUT_TIMING_0); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci reg = MIC_VBP_SIZE(vm.hback_porch) + 1758c2ecf20Sopenharmony_ci MIC_VFP_SIZE(vm.hfront_porch); 1768c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_INPUT_TIMING_1); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void mic_set_img_size(struct exynos_mic *mic) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct videomode *vm = &mic->vm; 1828c2ecf20Sopenharmony_ci u32 reg; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci reg = MIC_IMG_H_SIZE(vm->hactive) + 1858c2ecf20Sopenharmony_ci MIC_IMG_V_SIZE(vm->vactive); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_IMG_SIZE); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void mic_set_output_timing(struct exynos_mic *mic) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct videomode vm = mic->vm; 1938c2ecf20Sopenharmony_ci u32 reg, bs_size_2d; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci DRM_DEV_DEBUG(mic->dev, "w: %u, h: %u\n", vm.hactive, vm.vactive); 1968c2ecf20Sopenharmony_ci bs_size_2d = ((vm.hactive >> 2) << 1) + (vm.vactive % 4); 1978c2ecf20Sopenharmony_ci reg = MIC_BS_SIZE_2D(bs_size_2d); 1988c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_2); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!mic->i80_mode) { 2018c2ecf20Sopenharmony_ci reg = MIC_H_PULSE_WIDTH_2D(vm.hsync_len) + 2028c2ecf20Sopenharmony_ci MIC_H_PERIOD_PIXEL_2D(vm.hsync_len + bs_size_2d + 2038c2ecf20Sopenharmony_ci vm.hback_porch + vm.hfront_porch); 2048c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_0); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci reg = MIC_HBP_SIZE_2D(vm.hback_porch) + 2078c2ecf20Sopenharmony_ci MIC_H_PERIOD_PIXEL_2D(vm.hfront_porch); 2088c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_1); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void mic_set_reg_on(struct exynos_mic *mic, bool enable) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci u32 reg = readl(mic->reg + MIC_OP); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (enable) { 2178c2ecf20Sopenharmony_ci reg &= ~(MIC_MODE_SEL_MASK | MIC_CORE_VER_CONTROL | MIC_PSR_EN); 2188c2ecf20Sopenharmony_ci reg |= (MIC_CORE_EN | MIC_BS_CHG_OUT | MIC_ON_REG); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci reg &= ~MIC_MODE_SEL_COMMAND_MODE; 2218c2ecf20Sopenharmony_ci if (mic->i80_mode) 2228c2ecf20Sopenharmony_ci reg |= MIC_MODE_SEL_COMMAND_MODE; 2238c2ecf20Sopenharmony_ci } else { 2248c2ecf20Sopenharmony_ci reg &= ~MIC_CORE_EN; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci reg |= MIC_UPD_REG; 2288c2ecf20Sopenharmony_ci writel(reg, mic->reg + MIC_OP); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void mic_disable(struct drm_bridge *bridge) { } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void mic_post_disable(struct drm_bridge *bridge) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct exynos_mic *mic = bridge->driver_private; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci mutex_lock(&mic_mutex); 2388c2ecf20Sopenharmony_ci if (!mic->enabled) 2398c2ecf20Sopenharmony_ci goto already_disabled; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci mic_set_path(mic, 0); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci pm_runtime_put(mic->dev); 2448c2ecf20Sopenharmony_ci mic->enabled = 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cialready_disabled: 2478c2ecf20Sopenharmony_ci mutex_unlock(&mic_mutex); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void mic_mode_set(struct drm_bridge *bridge, 2518c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 2528c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct exynos_mic *mic = bridge->driver_private; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci mutex_lock(&mic_mutex); 2578c2ecf20Sopenharmony_ci drm_display_mode_to_videomode(mode, &mic->vm); 2588c2ecf20Sopenharmony_ci mic->i80_mode = to_exynos_crtc(bridge->encoder->crtc)->i80_mode; 2598c2ecf20Sopenharmony_ci mutex_unlock(&mic_mutex); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void mic_pre_enable(struct drm_bridge *bridge) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct exynos_mic *mic = bridge->driver_private; 2658c2ecf20Sopenharmony_ci int ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci mutex_lock(&mic_mutex); 2688c2ecf20Sopenharmony_ci if (mic->enabled) 2698c2ecf20Sopenharmony_ci goto unlock; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(mic->dev); 2728c2ecf20Sopenharmony_ci if (ret < 0) { 2738c2ecf20Sopenharmony_ci pm_runtime_put_noidle(mic->dev); 2748c2ecf20Sopenharmony_ci goto unlock; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci mic_set_path(mic, 1); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci ret = mic_sw_reset(mic); 2808c2ecf20Sopenharmony_ci if (ret) { 2818c2ecf20Sopenharmony_ci DRM_DEV_ERROR(mic->dev, "Failed to reset\n"); 2828c2ecf20Sopenharmony_ci goto turn_off; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (!mic->i80_mode) 2868c2ecf20Sopenharmony_ci mic_set_porch_timing(mic); 2878c2ecf20Sopenharmony_ci mic_set_img_size(mic); 2888c2ecf20Sopenharmony_ci mic_set_output_timing(mic); 2898c2ecf20Sopenharmony_ci mic_set_reg_on(mic, 1); 2908c2ecf20Sopenharmony_ci mic->enabled = 1; 2918c2ecf20Sopenharmony_ci mutex_unlock(&mic_mutex); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_citurn_off: 2968c2ecf20Sopenharmony_ci pm_runtime_put(mic->dev); 2978c2ecf20Sopenharmony_ciunlock: 2988c2ecf20Sopenharmony_ci mutex_unlock(&mic_mutex); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void mic_enable(struct drm_bridge *bridge) { } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs mic_bridge_funcs = { 3048c2ecf20Sopenharmony_ci .disable = mic_disable, 3058c2ecf20Sopenharmony_ci .post_disable = mic_post_disable, 3068c2ecf20Sopenharmony_ci .mode_set = mic_mode_set, 3078c2ecf20Sopenharmony_ci .pre_enable = mic_pre_enable, 3088c2ecf20Sopenharmony_ci .enable = mic_enable, 3098c2ecf20Sopenharmony_ci}; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int exynos_mic_bind(struct device *dev, struct device *master, 3128c2ecf20Sopenharmony_ci void *data) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct exynos_mic *mic = dev_get_drvdata(dev); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci mic->bridge.driver_private = mic; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void exynos_mic_unbind(struct device *dev, struct device *master, 3228c2ecf20Sopenharmony_ci void *data) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct exynos_mic *mic = dev_get_drvdata(dev); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci mutex_lock(&mic_mutex); 3278c2ecf20Sopenharmony_ci if (!mic->enabled) 3288c2ecf20Sopenharmony_ci goto already_disabled; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pm_runtime_put(mic->dev); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cialready_disabled: 3338c2ecf20Sopenharmony_ci mutex_unlock(&mic_mutex); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic const struct component_ops exynos_mic_component_ops = { 3378c2ecf20Sopenharmony_ci .bind = exynos_mic_bind, 3388c2ecf20Sopenharmony_ci .unbind = exynos_mic_unbind, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3428c2ecf20Sopenharmony_cistatic int exynos_mic_suspend(struct device *dev) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct exynos_mic *mic = dev_get_drvdata(dev); 3458c2ecf20Sopenharmony_ci int i; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci for (i = NUM_CLKS - 1; i > -1; i--) 3488c2ecf20Sopenharmony_ci clk_disable_unprepare(mic->clks[i]); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic int exynos_mic_resume(struct device *dev) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct exynos_mic *mic = dev_get_drvdata(dev); 3568c2ecf20Sopenharmony_ci int ret, i; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 0; i < NUM_CLKS; i++) { 3598c2ecf20Sopenharmony_ci ret = clk_prepare_enable(mic->clks[i]); 3608c2ecf20Sopenharmony_ci if (ret < 0) { 3618c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to enable clock (%s)\n", 3628c2ecf20Sopenharmony_ci clk_names[i]); 3638c2ecf20Sopenharmony_ci while (--i > -1) 3648c2ecf20Sopenharmony_ci clk_disable_unprepare(mic->clks[i]); 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci#endif 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic const struct dev_pm_ops exynos_mic_pm_ops = { 3738c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(exynos_mic_suspend, exynos_mic_resume, NULL) 3748c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 3758c2ecf20Sopenharmony_ci pm_runtime_force_resume) 3768c2ecf20Sopenharmony_ci}; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int exynos_mic_probe(struct platform_device *pdev) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3818c2ecf20Sopenharmony_ci struct exynos_mic *mic; 3828c2ecf20Sopenharmony_ci struct resource res; 3838c2ecf20Sopenharmony_ci int ret, i; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci mic = devm_kzalloc(dev, sizeof(*mic), GFP_KERNEL); 3868c2ecf20Sopenharmony_ci if (!mic) { 3878c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, 3888c2ecf20Sopenharmony_ci "mic: Failed to allocate memory for MIC object\n"); 3898c2ecf20Sopenharmony_ci ret = -ENOMEM; 3908c2ecf20Sopenharmony_ci goto err; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci mic->dev = dev; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci ret = of_address_to_resource(dev->of_node, 0, &res); 3968c2ecf20Sopenharmony_ci if (ret) { 3978c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "mic: Failed to get mem region for MIC\n"); 3988c2ecf20Sopenharmony_ci goto err; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci mic->reg = devm_ioremap(dev, res.start, resource_size(&res)); 4018c2ecf20Sopenharmony_ci if (!mic->reg) { 4028c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "mic: Failed to remap for MIC\n"); 4038c2ecf20Sopenharmony_ci ret = -ENOMEM; 4048c2ecf20Sopenharmony_ci goto err; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci mic->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, 4088c2ecf20Sopenharmony_ci "samsung,disp-syscon"); 4098c2ecf20Sopenharmony_ci if (IS_ERR(mic->sysreg)) { 4108c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "mic: Failed to get system register.\n"); 4118c2ecf20Sopenharmony_ci ret = PTR_ERR(mic->sysreg); 4128c2ecf20Sopenharmony_ci goto err; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci for (i = 0; i < NUM_CLKS; i++) { 4168c2ecf20Sopenharmony_ci mic->clks[i] = devm_clk_get(dev, clk_names[i]); 4178c2ecf20Sopenharmony_ci if (IS_ERR(mic->clks[i])) { 4188c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "mic: Failed to get clock (%s)\n", 4198c2ecf20Sopenharmony_ci clk_names[i]); 4208c2ecf20Sopenharmony_ci ret = PTR_ERR(mic->clks[i]); 4218c2ecf20Sopenharmony_ci goto err; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mic); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci mic->bridge.funcs = &mic_bridge_funcs; 4288c2ecf20Sopenharmony_ci mic->bridge.of_node = dev->of_node; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci drm_bridge_add(&mic->bridge); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci ret = component_add(dev, &exynos_mic_component_ops); 4358c2ecf20Sopenharmony_ci if (ret) 4368c2ecf20Sopenharmony_ci goto err_pm; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(dev, "MIC has been probed\n"); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cierr_pm: 4438c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 4448c2ecf20Sopenharmony_cierr: 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int exynos_mic_remove(struct platform_device *pdev) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct exynos_mic *mic = platform_get_drvdata(pdev); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci component_del(&pdev->dev, &exynos_mic_component_ops); 4538c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci drm_bridge_remove(&mic->bridge); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_mic_of_match[] = { 4618c2ecf20Sopenharmony_ci { .compatible = "samsung,exynos5433-mic" }, 4628c2ecf20Sopenharmony_ci { } 4638c2ecf20Sopenharmony_ci}; 4648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_mic_of_match); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistruct platform_driver mic_driver = { 4678c2ecf20Sopenharmony_ci .probe = exynos_mic_probe, 4688c2ecf20Sopenharmony_ci .remove = exynos_mic_remove, 4698c2ecf20Sopenharmony_ci .driver = { 4708c2ecf20Sopenharmony_ci .name = "exynos-mic", 4718c2ecf20Sopenharmony_ci .pm = &exynos_mic_pm_ops, 4728c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4738c2ecf20Sopenharmony_ci .of_match_table = exynos_mic_of_match, 4748c2ecf20Sopenharmony_ci }, 4758c2ecf20Sopenharmony_ci}; 476