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