162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/phy/phy.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci#include <linux/soc/samsung/exynos-regs-pmu.h>
1962306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cienum exynos_mipi_phy_id {
2262306a36Sopenharmony_ci	EXYNOS_MIPI_PHY_ID_NONE = -1,
2362306a36Sopenharmony_ci	EXYNOS_MIPI_PHY_ID_CSIS0,
2462306a36Sopenharmony_ci	EXYNOS_MIPI_PHY_ID_DSIM0,
2562306a36Sopenharmony_ci	EXYNOS_MIPI_PHY_ID_CSIS1,
2662306a36Sopenharmony_ci	EXYNOS_MIPI_PHY_ID_DSIM1,
2762306a36Sopenharmony_ci	EXYNOS_MIPI_PHY_ID_CSIS2,
2862306a36Sopenharmony_ci	EXYNOS_MIPI_PHYS_NUM
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cienum exynos_mipi_phy_regmap_id {
3262306a36Sopenharmony_ci	EXYNOS_MIPI_REGMAP_PMU,
3362306a36Sopenharmony_ci	EXYNOS_MIPI_REGMAP_DISP,
3462306a36Sopenharmony_ci	EXYNOS_MIPI_REGMAP_CAM0,
3562306a36Sopenharmony_ci	EXYNOS_MIPI_REGMAP_CAM1,
3662306a36Sopenharmony_ci	EXYNOS_MIPI_REGMAPS_NUM
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct mipi_phy_device_desc {
4062306a36Sopenharmony_ci	int num_phys;
4162306a36Sopenharmony_ci	int num_regmaps;
4262306a36Sopenharmony_ci	const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM];
4362306a36Sopenharmony_ci	struct exynos_mipi_phy_desc {
4462306a36Sopenharmony_ci		enum exynos_mipi_phy_id	coupled_phy_id;
4562306a36Sopenharmony_ci		u32 enable_val;
4662306a36Sopenharmony_ci		unsigned int enable_reg;
4762306a36Sopenharmony_ci		enum exynos_mipi_phy_regmap_id enable_map;
4862306a36Sopenharmony_ci		u32 resetn_val;
4962306a36Sopenharmony_ci		unsigned int resetn_reg;
5062306a36Sopenharmony_ci		enum exynos_mipi_phy_regmap_id resetn_map;
5162306a36Sopenharmony_ci	} phys[EXYNOS_MIPI_PHYS_NUM];
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic const struct mipi_phy_device_desc s5pv210_mipi_phy = {
5562306a36Sopenharmony_ci	.num_regmaps = 1,
5662306a36Sopenharmony_ci	.regmap_names = {"syscon"},
5762306a36Sopenharmony_ci	.num_phys = 4,
5862306a36Sopenharmony_ci	.phys = {
5962306a36Sopenharmony_ci		{
6062306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS0 */
6162306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
6262306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
6362306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
6462306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
6562306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
6662306a36Sopenharmony_ci			.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
6762306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
6862306a36Sopenharmony_ci		}, {
6962306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_DSIM0 */
7062306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
7162306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
7262306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
7362306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
7462306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
7562306a36Sopenharmony_ci			.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
7662306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
7762306a36Sopenharmony_ci		}, {
7862306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS1 */
7962306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
8062306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
8162306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
8262306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
8362306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
8462306a36Sopenharmony_ci			.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
8562306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
8662306a36Sopenharmony_ci		}, {
8762306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_DSIM1 */
8862306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
8962306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
9062306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
9162306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
9262306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
9362306a36Sopenharmony_ci			.resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
9462306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
9562306a36Sopenharmony_ci		},
9662306a36Sopenharmony_ci	},
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct mipi_phy_device_desc exynos5420_mipi_phy = {
10062306a36Sopenharmony_ci	.num_regmaps = 1,
10162306a36Sopenharmony_ci	.regmap_names = {"syscon"},
10262306a36Sopenharmony_ci	.num_phys = 5,
10362306a36Sopenharmony_ci	.phys = {
10462306a36Sopenharmony_ci		{
10562306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS0 */
10662306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
10762306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
10862306a36Sopenharmony_ci			.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
10962306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
11062306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
11162306a36Sopenharmony_ci			.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
11262306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
11362306a36Sopenharmony_ci		}, {
11462306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_DSIM0 */
11562306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
11662306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
11762306a36Sopenharmony_ci			.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
11862306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
11962306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
12062306a36Sopenharmony_ci			.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
12162306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
12262306a36Sopenharmony_ci		}, {
12362306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS1 */
12462306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
12562306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
12662306a36Sopenharmony_ci			.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
12762306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
12862306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
12962306a36Sopenharmony_ci			.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
13062306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
13162306a36Sopenharmony_ci		}, {
13262306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_DSIM1 */
13362306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
13462306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
13562306a36Sopenharmony_ci			.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
13662306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
13762306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
13862306a36Sopenharmony_ci			.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
13962306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
14062306a36Sopenharmony_ci		}, {
14162306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS2 */
14262306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
14362306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
14462306a36Sopenharmony_ci			.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2),
14562306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
14662306a36Sopenharmony_ci			.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
14762306a36Sopenharmony_ci			.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2),
14862306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
14962306a36Sopenharmony_ci		},
15062306a36Sopenharmony_ci	},
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci#define EXYNOS5433_SYSREG_DISP_MIPI_PHY		0x100C
15462306a36Sopenharmony_ci#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON	0x1014
15562306a36Sopenharmony_ci#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON	0x1020
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic const struct mipi_phy_device_desc exynos5433_mipi_phy = {
15862306a36Sopenharmony_ci	.num_regmaps = 4,
15962306a36Sopenharmony_ci	.regmap_names = {
16062306a36Sopenharmony_ci		"samsung,pmu-syscon",
16162306a36Sopenharmony_ci		"samsung,disp-sysreg",
16262306a36Sopenharmony_ci		"samsung,cam0-sysreg",
16362306a36Sopenharmony_ci		"samsung,cam1-sysreg"
16462306a36Sopenharmony_ci	},
16562306a36Sopenharmony_ci	.num_phys = 5,
16662306a36Sopenharmony_ci	.phys = {
16762306a36Sopenharmony_ci		{
16862306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS0 */
16962306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
17062306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
17162306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
17262306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
17362306a36Sopenharmony_ci			.resetn_val = BIT(0),
17462306a36Sopenharmony_ci			.resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
17562306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_CAM0,
17662306a36Sopenharmony_ci		}, {
17762306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_DSIM0 */
17862306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
17962306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
18062306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
18162306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
18262306a36Sopenharmony_ci			.resetn_val = BIT(0),
18362306a36Sopenharmony_ci			.resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
18462306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_DISP,
18562306a36Sopenharmony_ci		}, {
18662306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS1 */
18762306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
18862306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
18962306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
19062306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
19162306a36Sopenharmony_ci			.resetn_val = BIT(1),
19262306a36Sopenharmony_ci			.resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
19362306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_CAM0,
19462306a36Sopenharmony_ci		}, {
19562306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_DSIM1 */
19662306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
19762306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
19862306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
19962306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
20062306a36Sopenharmony_ci			.resetn_val = BIT(1),
20162306a36Sopenharmony_ci			.resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
20262306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_DISP,
20362306a36Sopenharmony_ci		}, {
20462306a36Sopenharmony_ci			/* EXYNOS_MIPI_PHY_ID_CSIS2 */
20562306a36Sopenharmony_ci			.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
20662306a36Sopenharmony_ci			.enable_val = EXYNOS4_PHY_ENABLE,
20762306a36Sopenharmony_ci			.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2),
20862306a36Sopenharmony_ci			.enable_map = EXYNOS_MIPI_REGMAP_PMU,
20962306a36Sopenharmony_ci			.resetn_val = BIT(0),
21062306a36Sopenharmony_ci			.resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON,
21162306a36Sopenharmony_ci			.resetn_map = EXYNOS_MIPI_REGMAP_CAM1,
21262306a36Sopenharmony_ci		},
21362306a36Sopenharmony_ci	},
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistruct exynos_mipi_video_phy {
21762306a36Sopenharmony_ci	struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM];
21862306a36Sopenharmony_ci	int num_phys;
21962306a36Sopenharmony_ci	struct video_phy_desc {
22062306a36Sopenharmony_ci		struct phy *phy;
22162306a36Sopenharmony_ci		unsigned int index;
22262306a36Sopenharmony_ci		const struct exynos_mipi_phy_desc *data;
22362306a36Sopenharmony_ci	} phys[EXYNOS_MIPI_PHYS_NUM];
22462306a36Sopenharmony_ci	spinlock_t slock;
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int __set_phy_state(const struct exynos_mipi_phy_desc *data,
22862306a36Sopenharmony_ci			   struct exynos_mipi_video_phy *state, unsigned int on)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct regmap *enable_map = state->regmaps[data->enable_map];
23162306a36Sopenharmony_ci	struct regmap *resetn_map = state->regmaps[data->resetn_map];
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	spin_lock(&state->slock);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* disable in PMU sysreg */
23662306a36Sopenharmony_ci	if (!on && data->coupled_phy_id >= 0 &&
23762306a36Sopenharmony_ci	    state->phys[data->coupled_phy_id].phy->power_count == 0)
23862306a36Sopenharmony_ci		regmap_update_bits(enable_map, data->enable_reg,
23962306a36Sopenharmony_ci				   data->enable_val, 0);
24062306a36Sopenharmony_ci	/* PHY reset */
24162306a36Sopenharmony_ci	if (on)
24262306a36Sopenharmony_ci		regmap_update_bits(resetn_map, data->resetn_reg,
24362306a36Sopenharmony_ci				   data->resetn_val, data->resetn_val);
24462306a36Sopenharmony_ci	else
24562306a36Sopenharmony_ci		regmap_update_bits(resetn_map, data->resetn_reg,
24662306a36Sopenharmony_ci				   data->resetn_val, 0);
24762306a36Sopenharmony_ci	/* enable in PMU sysreg */
24862306a36Sopenharmony_ci	if (on)
24962306a36Sopenharmony_ci		regmap_update_bits(enable_map, data->enable_reg,
25062306a36Sopenharmony_ci				   data->enable_val, data->enable_val);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	spin_unlock(&state->slock);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci#define to_mipi_video_phy(desc) \
25862306a36Sopenharmony_ci	container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index])
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int exynos_mipi_video_phy_power_on(struct phy *phy)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
26362306a36Sopenharmony_ci	struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return __set_phy_state(phy_desc->data, state, 1);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int exynos_mipi_video_phy_power_off(struct phy *phy)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
27162306a36Sopenharmony_ci	struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return __set_phy_state(phy_desc->data, state, 0);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
27762306a36Sopenharmony_ci					struct of_phandle_args *args)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (WARN_ON(args->args[0] >= state->num_phys))
28262306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return state->phys[args->args[0]].phy;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic const struct phy_ops exynos_mipi_video_phy_ops = {
28862306a36Sopenharmony_ci	.power_on	= exynos_mipi_video_phy_power_on,
28962306a36Sopenharmony_ci	.power_off	= exynos_mipi_video_phy_power_off,
29062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int exynos_mipi_video_phy_probe(struct platform_device *pdev)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	const struct mipi_phy_device_desc *phy_dev;
29662306a36Sopenharmony_ci	struct exynos_mipi_video_phy *state;
29762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
29862306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
29962306a36Sopenharmony_ci	struct phy_provider *phy_provider;
30062306a36Sopenharmony_ci	unsigned int i = 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	phy_dev = of_device_get_match_data(dev);
30362306a36Sopenharmony_ci	if (!phy_dev)
30462306a36Sopenharmony_ci		return -ENODEV;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
30762306a36Sopenharmony_ci	if (!state)
30862306a36Sopenharmony_ci		return -ENOMEM;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	state->regmaps[i] = syscon_node_to_regmap(dev->parent->of_node);
31162306a36Sopenharmony_ci	if (!IS_ERR(state->regmaps[i]))
31262306a36Sopenharmony_ci		i++;
31362306a36Sopenharmony_ci	for (; i < phy_dev->num_regmaps; i++) {
31462306a36Sopenharmony_ci		state->regmaps[i] = syscon_regmap_lookup_by_phandle(np,
31562306a36Sopenharmony_ci						phy_dev->regmap_names[i]);
31662306a36Sopenharmony_ci		if (IS_ERR(state->regmaps[i]))
31762306a36Sopenharmony_ci			return PTR_ERR(state->regmaps[i]);
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci	state->num_phys = phy_dev->num_phys;
32062306a36Sopenharmony_ci	spin_lock_init(&state->slock);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	dev_set_drvdata(dev, state);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	for (i = 0; i < state->num_phys; i++) {
32562306a36Sopenharmony_ci		struct phy *phy = devm_phy_create(dev, NULL,
32662306a36Sopenharmony_ci						  &exynos_mipi_video_phy_ops);
32762306a36Sopenharmony_ci		if (IS_ERR(phy)) {
32862306a36Sopenharmony_ci			dev_err(dev, "failed to create PHY %d\n", i);
32962306a36Sopenharmony_ci			return PTR_ERR(phy);
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		state->phys[i].phy = phy;
33362306a36Sopenharmony_ci		state->phys[i].index = i;
33462306a36Sopenharmony_ci		state->phys[i].data = &phy_dev->phys[i];
33562306a36Sopenharmony_ci		phy_set_drvdata(phy, &state->phys[i]);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev,
33962306a36Sopenharmony_ci					exynos_mipi_video_phy_xlate);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(phy_provider);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic const struct of_device_id exynos_mipi_video_phy_of_match[] = {
34562306a36Sopenharmony_ci	{
34662306a36Sopenharmony_ci		.compatible = "samsung,s5pv210-mipi-video-phy",
34762306a36Sopenharmony_ci		.data = &s5pv210_mipi_phy,
34862306a36Sopenharmony_ci	}, {
34962306a36Sopenharmony_ci		.compatible = "samsung,exynos5420-mipi-video-phy",
35062306a36Sopenharmony_ci		.data = &exynos5420_mipi_phy,
35162306a36Sopenharmony_ci	}, {
35262306a36Sopenharmony_ci		.compatible = "samsung,exynos5433-mipi-video-phy",
35362306a36Sopenharmony_ci		.data = &exynos5433_mipi_phy,
35462306a36Sopenharmony_ci	},
35562306a36Sopenharmony_ci	{ /* sentinel */ },
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic struct platform_driver exynos_mipi_video_phy_driver = {
36062306a36Sopenharmony_ci	.probe	= exynos_mipi_video_phy_probe,
36162306a36Sopenharmony_ci	.driver = {
36262306a36Sopenharmony_ci		.of_match_table	= exynos_mipi_video_phy_of_match,
36362306a36Sopenharmony_ci		.name  = "exynos-mipi-video-phy",
36462306a36Sopenharmony_ci		.suppress_bind_attrs = true,
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci};
36762306a36Sopenharmony_cimodule_platform_driver(exynos_mipi_video_phy_driver);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver");
37062306a36Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
37162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
372