CK Hu | 119f517 | 2016-01-04 18:36:34 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 MediaTek Inc. |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License version 2 as |
| 6 | * published by the Free Software Foundation. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | */ |
| 13 | |
| 14 | #include <linux/clk.h> |
| 15 | #include <linux/module.h> |
| 16 | #include <linux/of_device.h> |
| 17 | #include <linux/platform_device.h> |
| 18 | #include <linux/regmap.h> |
| 19 | |
| 20 | #include "mtk_drm_ddp.h" |
| 21 | #include "mtk_drm_ddp_comp.h" |
| 22 | |
| 23 | #define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN 0x040 |
| 24 | #define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN 0x044 |
| 25 | #define DISP_REG_CONFIG_DISP_OD_MOUT_EN 0x048 |
| 26 | #define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN 0x04c |
| 27 | #define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN 0x050 |
| 28 | #define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084 |
| 29 | #define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088 |
| 30 | #define DISP_REG_CONFIG_DPI_SEL_IN 0x0ac |
| 31 | #define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN 0x0c8 |
| 32 | #define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100 |
| 33 | |
| 34 | #define DISP_REG_MUTEX_EN(n) (0x20 + 0x20 * (n)) |
| 35 | #define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * (n)) |
| 36 | #define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * (n)) |
| 37 | #define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * (n)) |
| 38 | |
| 39 | #define MUTEX_MOD_DISP_OVL0 BIT(11) |
| 40 | #define MUTEX_MOD_DISP_OVL1 BIT(12) |
| 41 | #define MUTEX_MOD_DISP_RDMA0 BIT(13) |
| 42 | #define MUTEX_MOD_DISP_RDMA1 BIT(14) |
| 43 | #define MUTEX_MOD_DISP_RDMA2 BIT(15) |
| 44 | #define MUTEX_MOD_DISP_WDMA0 BIT(16) |
| 45 | #define MUTEX_MOD_DISP_WDMA1 BIT(17) |
| 46 | #define MUTEX_MOD_DISP_COLOR0 BIT(18) |
| 47 | #define MUTEX_MOD_DISP_COLOR1 BIT(19) |
| 48 | #define MUTEX_MOD_DISP_AAL BIT(20) |
| 49 | #define MUTEX_MOD_DISP_GAMMA BIT(21) |
| 50 | #define MUTEX_MOD_DISP_UFOE BIT(22) |
| 51 | #define MUTEX_MOD_DISP_PWM0 BIT(23) |
| 52 | #define MUTEX_MOD_DISP_PWM1 BIT(24) |
| 53 | #define MUTEX_MOD_DISP_OD BIT(25) |
| 54 | |
| 55 | #define MUTEX_SOF_SINGLE_MODE 0 |
| 56 | #define MUTEX_SOF_DSI0 1 |
| 57 | #define MUTEX_SOF_DSI1 2 |
| 58 | #define MUTEX_SOF_DPI0 3 |
| 59 | |
| 60 | #define OVL0_MOUT_EN_COLOR0 0x1 |
| 61 | #define OD_MOUT_EN_RDMA0 0x1 |
| 62 | #define UFOE_MOUT_EN_DSI0 0x1 |
| 63 | #define COLOR0_SEL_IN_OVL0 0x1 |
| 64 | #define OVL1_MOUT_EN_COLOR1 0x1 |
| 65 | #define GAMMA_MOUT_EN_RDMA1 0x1 |
| 66 | #define RDMA1_MOUT_DPI0 0x2 |
| 67 | #define DPI0_SEL_IN_RDMA1 0x1 |
| 68 | #define COLOR1_SEL_IN_OVL1 0x1 |
| 69 | |
| 70 | struct mtk_disp_mutex { |
| 71 | int id; |
| 72 | bool claimed; |
| 73 | }; |
| 74 | |
| 75 | struct mtk_ddp { |
| 76 | struct device *dev; |
| 77 | struct clk *clk; |
| 78 | void __iomem *regs; |
| 79 | struct mtk_disp_mutex mutex[10]; |
| 80 | }; |
| 81 | |
| 82 | static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = { |
| 83 | [DDP_COMPONENT_AAL] = MUTEX_MOD_DISP_AAL, |
| 84 | [DDP_COMPONENT_COLOR0] = MUTEX_MOD_DISP_COLOR0, |
| 85 | [DDP_COMPONENT_COLOR1] = MUTEX_MOD_DISP_COLOR1, |
| 86 | [DDP_COMPONENT_GAMMA] = MUTEX_MOD_DISP_GAMMA, |
| 87 | [DDP_COMPONENT_OD] = MUTEX_MOD_DISP_OD, |
| 88 | [DDP_COMPONENT_OVL0] = MUTEX_MOD_DISP_OVL0, |
| 89 | [DDP_COMPONENT_OVL1] = MUTEX_MOD_DISP_OVL1, |
| 90 | [DDP_COMPONENT_PWM0] = MUTEX_MOD_DISP_PWM0, |
| 91 | [DDP_COMPONENT_PWM1] = MUTEX_MOD_DISP_PWM1, |
| 92 | [DDP_COMPONENT_RDMA0] = MUTEX_MOD_DISP_RDMA0, |
| 93 | [DDP_COMPONENT_RDMA1] = MUTEX_MOD_DISP_RDMA1, |
| 94 | [DDP_COMPONENT_RDMA2] = MUTEX_MOD_DISP_RDMA2, |
| 95 | [DDP_COMPONENT_UFOE] = MUTEX_MOD_DISP_UFOE, |
| 96 | [DDP_COMPONENT_WDMA0] = MUTEX_MOD_DISP_WDMA0, |
| 97 | [DDP_COMPONENT_WDMA1] = MUTEX_MOD_DISP_WDMA1, |
| 98 | }; |
| 99 | |
| 100 | static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur, |
| 101 | enum mtk_ddp_comp_id next, |
| 102 | unsigned int *addr) |
| 103 | { |
| 104 | unsigned int value; |
| 105 | |
| 106 | if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { |
| 107 | *addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN; |
| 108 | value = OVL0_MOUT_EN_COLOR0; |
| 109 | } else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) { |
| 110 | *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN; |
| 111 | value = OD_MOUT_EN_RDMA0; |
| 112 | } else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) { |
| 113 | *addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN; |
| 114 | value = UFOE_MOUT_EN_DSI0; |
| 115 | } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { |
| 116 | *addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN; |
| 117 | value = OVL1_MOUT_EN_COLOR1; |
| 118 | } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) { |
| 119 | *addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN; |
| 120 | value = GAMMA_MOUT_EN_RDMA1; |
| 121 | } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { |
| 122 | *addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN; |
| 123 | value = RDMA1_MOUT_DPI0; |
| 124 | } else { |
| 125 | value = 0; |
| 126 | } |
| 127 | |
| 128 | return value; |
| 129 | } |
| 130 | |
| 131 | static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur, |
| 132 | enum mtk_ddp_comp_id next, |
| 133 | unsigned int *addr) |
| 134 | { |
| 135 | unsigned int value; |
| 136 | |
| 137 | if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { |
| 138 | *addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN; |
| 139 | value = COLOR0_SEL_IN_OVL0; |
| 140 | } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { |
| 141 | *addr = DISP_REG_CONFIG_DPI_SEL_IN; |
| 142 | value = DPI0_SEL_IN_RDMA1; |
| 143 | } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { |
| 144 | *addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN; |
| 145 | value = COLOR1_SEL_IN_OVL1; |
| 146 | } else { |
| 147 | value = 0; |
| 148 | } |
| 149 | |
| 150 | return value; |
| 151 | } |
| 152 | |
| 153 | void mtk_ddp_add_comp_to_path(void __iomem *config_regs, |
| 154 | enum mtk_ddp_comp_id cur, |
| 155 | enum mtk_ddp_comp_id next) |
| 156 | { |
| 157 | unsigned int addr, value, reg; |
| 158 | |
| 159 | value = mtk_ddp_mout_en(cur, next, &addr); |
| 160 | if (value) { |
| 161 | reg = readl_relaxed(config_regs + addr) | value; |
| 162 | writel_relaxed(reg, config_regs + addr); |
| 163 | } |
| 164 | |
| 165 | value = mtk_ddp_sel_in(cur, next, &addr); |
| 166 | if (value) { |
| 167 | reg = readl_relaxed(config_regs + addr) | value; |
| 168 | writel_relaxed(reg, config_regs + addr); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | void mtk_ddp_remove_comp_from_path(void __iomem *config_regs, |
| 173 | enum mtk_ddp_comp_id cur, |
| 174 | enum mtk_ddp_comp_id next) |
| 175 | { |
| 176 | unsigned int addr, value, reg; |
| 177 | |
| 178 | value = mtk_ddp_mout_en(cur, next, &addr); |
| 179 | if (value) { |
| 180 | reg = readl_relaxed(config_regs + addr) & ~value; |
| 181 | writel_relaxed(reg, config_regs + addr); |
| 182 | } |
| 183 | |
| 184 | value = mtk_ddp_sel_in(cur, next, &addr); |
| 185 | if (value) { |
| 186 | reg = readl_relaxed(config_regs + addr) & ~value; |
| 187 | writel_relaxed(reg, config_regs + addr); |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id) |
| 192 | { |
| 193 | struct mtk_ddp *ddp = dev_get_drvdata(dev); |
| 194 | |
| 195 | if (id >= 10) |
| 196 | return ERR_PTR(-EINVAL); |
| 197 | if (ddp->mutex[id].claimed) |
| 198 | return ERR_PTR(-EBUSY); |
| 199 | |
| 200 | ddp->mutex[id].claimed = true; |
| 201 | |
| 202 | return &ddp->mutex[id]; |
| 203 | } |
| 204 | |
| 205 | void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex) |
| 206 | { |
| 207 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, |
| 208 | mutex[mutex->id]); |
| 209 | |
| 210 | WARN_ON(&ddp->mutex[mutex->id] != mutex); |
| 211 | |
| 212 | mutex->claimed = false; |
| 213 | } |
| 214 | |
| 215 | int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex) |
| 216 | { |
| 217 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, |
| 218 | mutex[mutex->id]); |
| 219 | return clk_prepare_enable(ddp->clk); |
| 220 | } |
| 221 | |
| 222 | void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex) |
| 223 | { |
| 224 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, |
| 225 | mutex[mutex->id]); |
| 226 | clk_disable_unprepare(ddp->clk); |
| 227 | } |
| 228 | |
| 229 | void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex, |
| 230 | enum mtk_ddp_comp_id id) |
| 231 | { |
| 232 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, |
| 233 | mutex[mutex->id]); |
| 234 | unsigned int reg; |
| 235 | |
| 236 | WARN_ON(&ddp->mutex[mutex->id] != mutex); |
| 237 | |
| 238 | switch (id) { |
| 239 | case DDP_COMPONENT_DSI0: |
| 240 | reg = MUTEX_SOF_DSI0; |
| 241 | break; |
| 242 | case DDP_COMPONENT_DSI1: |
| 243 | reg = MUTEX_SOF_DSI0; |
| 244 | break; |
| 245 | case DDP_COMPONENT_DPI0: |
| 246 | reg = MUTEX_SOF_DPI0; |
| 247 | break; |
| 248 | default: |
| 249 | reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); |
| 250 | reg |= mutex_mod[id]; |
| 251 | writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); |
| 252 | return; |
| 253 | } |
| 254 | |
| 255 | writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_SOF(mutex->id)); |
| 256 | } |
| 257 | |
| 258 | void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex, |
| 259 | enum mtk_ddp_comp_id id) |
| 260 | { |
| 261 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, |
| 262 | mutex[mutex->id]); |
| 263 | unsigned int reg; |
| 264 | |
| 265 | WARN_ON(&ddp->mutex[mutex->id] != mutex); |
| 266 | |
| 267 | switch (id) { |
| 268 | case DDP_COMPONENT_DSI0: |
| 269 | case DDP_COMPONENT_DSI1: |
| 270 | case DDP_COMPONENT_DPI0: |
| 271 | writel_relaxed(MUTEX_SOF_SINGLE_MODE, |
| 272 | ddp->regs + DISP_REG_MUTEX_SOF(mutex->id)); |
| 273 | break; |
| 274 | default: |
| 275 | reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); |
| 276 | reg &= ~mutex_mod[id]; |
| 277 | writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); |
| 278 | break; |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex) |
| 283 | { |
| 284 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, |
| 285 | mutex[mutex->id]); |
| 286 | |
| 287 | WARN_ON(&ddp->mutex[mutex->id] != mutex); |
| 288 | |
| 289 | writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id)); |
| 290 | } |
| 291 | |
| 292 | void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex) |
| 293 | { |
| 294 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, |
| 295 | mutex[mutex->id]); |
| 296 | |
| 297 | WARN_ON(&ddp->mutex[mutex->id] != mutex); |
| 298 | |
| 299 | writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id)); |
| 300 | } |
| 301 | |
| 302 | static int mtk_ddp_probe(struct platform_device *pdev) |
| 303 | { |
| 304 | struct device *dev = &pdev->dev; |
| 305 | struct mtk_ddp *ddp; |
| 306 | struct resource *regs; |
| 307 | int i; |
| 308 | |
| 309 | ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL); |
| 310 | if (!ddp) |
| 311 | return -ENOMEM; |
| 312 | |
| 313 | for (i = 0; i < 10; i++) |
| 314 | ddp->mutex[i].id = i; |
| 315 | |
| 316 | ddp->clk = devm_clk_get(dev, NULL); |
| 317 | if (IS_ERR(ddp->clk)) { |
| 318 | dev_err(dev, "Failed to get clock\n"); |
| 319 | return PTR_ERR(ddp->clk); |
| 320 | } |
| 321 | |
| 322 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 323 | ddp->regs = devm_ioremap_resource(dev, regs); |
| 324 | if (IS_ERR(ddp->regs)) { |
| 325 | dev_err(dev, "Failed to map mutex registers\n"); |
| 326 | return PTR_ERR(ddp->regs); |
| 327 | } |
| 328 | |
| 329 | platform_set_drvdata(pdev, ddp); |
| 330 | |
| 331 | return 0; |
| 332 | } |
| 333 | |
| 334 | static int mtk_ddp_remove(struct platform_device *pdev) |
| 335 | { |
| 336 | return 0; |
| 337 | } |
| 338 | |
| 339 | static const struct of_device_id ddp_driver_dt_match[] = { |
| 340 | { .compatible = "mediatek,mt8173-disp-mutex" }, |
| 341 | {}, |
| 342 | }; |
| 343 | MODULE_DEVICE_TABLE(of, ddp_driver_dt_match); |
| 344 | |
| 345 | struct platform_driver mtk_ddp_driver = { |
| 346 | .probe = mtk_ddp_probe, |
| 347 | .remove = mtk_ddp_remove, |
| 348 | .driver = { |
| 349 | .name = "mediatek-ddp", |
| 350 | .owner = THIS_MODULE, |
| 351 | .of_match_table = ddp_driver_dt_match, |
| 352 | }, |
| 353 | }; |