Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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 <drm/drmP.h>
 15#include <linux/clk.h>
 16#include <linux/component.h>
 17#include <linux/of_device.h>
 18#include <linux/of_irq.h>
 19#include <linux/platform_device.h>
 20
 21#include "mtk_drm_crtc.h"
 22#include "mtk_drm_ddp_comp.h"
 23
 24#define DISP_REG_RDMA_INT_ENABLE		0x0000
 25#define DISP_REG_RDMA_INT_STATUS		0x0004
 26#define RDMA_TARGET_LINE_INT				BIT(5)
 27#define RDMA_FIFO_UNDERFLOW_INT				BIT(4)
 28#define RDMA_EOF_ABNORMAL_INT				BIT(3)
 29#define RDMA_FRAME_END_INT				BIT(2)
 30#define RDMA_FRAME_START_INT				BIT(1)
 31#define RDMA_REG_UPDATE_INT				BIT(0)
 32#define DISP_REG_RDMA_GLOBAL_CON		0x0010
 33#define RDMA_ENGINE_EN					BIT(0)
 34#define DISP_REG_RDMA_SIZE_CON_0		0x0014
 35#define DISP_REG_RDMA_SIZE_CON_1		0x0018
 36#define DISP_REG_RDMA_TARGET_LINE		0x001c
 37#define DISP_REG_RDMA_FIFO_CON			0x0040
 38#define RDMA_FIFO_UNDERFLOW_EN				BIT(31)
 39#define RDMA_FIFO_PSEUDO_SIZE(bytes)			(((bytes) / 16) << 16)
 40#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes)		((bytes) / 16)
 41#define RDMA_FIFO_SIZE(rdma)			((rdma)->data->fifo_size)
 42
 43struct mtk_disp_rdma_data {
 44	unsigned int fifo_size;
 45};
 46
 47/**
 48 * struct mtk_disp_rdma - DISP_RDMA driver structure
 49 * @ddp_comp - structure containing type enum and hardware resources
 50 * @crtc - associated crtc to report irq events to
 51 */
 52struct mtk_disp_rdma {
 53	struct mtk_ddp_comp		ddp_comp;
 54	struct drm_crtc			*crtc;
 55	const struct mtk_disp_rdma_data	*data;
 56};
 57
 58static inline struct mtk_disp_rdma *comp_to_rdma(struct mtk_ddp_comp *comp)
 59{
 60	return container_of(comp, struct mtk_disp_rdma, ddp_comp);
 61}
 62
 63static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id)
 64{
 65	struct mtk_disp_rdma *priv = dev_id;
 66	struct mtk_ddp_comp *rdma = &priv->ddp_comp;
 67
 68	/* Clear frame completion interrupt */
 69	writel(0x0, rdma->regs + DISP_REG_RDMA_INT_STATUS);
 70
 71	if (!priv->crtc)
 72		return IRQ_NONE;
 73
 74	mtk_crtc_ddp_irq(priv->crtc, rdma);
 75
 76	return IRQ_HANDLED;
 77}
 78
 79static void rdma_update_bits(struct mtk_ddp_comp *comp, unsigned int reg,
 80			     unsigned int mask, unsigned int val)
 81{
 82	unsigned int tmp = readl(comp->regs + reg);
 83
 84	tmp = (tmp & ~mask) | (val & mask);
 85	writel(tmp, comp->regs + reg);
 86}
 87
 88static void mtk_rdma_enable_vblank(struct mtk_ddp_comp *comp,
 89				   struct drm_crtc *crtc)
 90{
 91	struct mtk_disp_rdma *rdma = comp_to_rdma(comp);
 92
 93	rdma->crtc = crtc;
 94	rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
 95			 RDMA_FRAME_END_INT);
 96}
 97
 98static void mtk_rdma_disable_vblank(struct mtk_ddp_comp *comp)
 99{
100	struct mtk_disp_rdma *rdma = comp_to_rdma(comp);
101
102	rdma->crtc = NULL;
103	rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
104}
105
106static void mtk_rdma_start(struct mtk_ddp_comp *comp)
107{
108	rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN,
109			 RDMA_ENGINE_EN);
110}
111
112static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
113{
114	rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
115}
116
117static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
118			    unsigned int height, unsigned int vrefresh,
119			    unsigned int bpc)
120{
121	unsigned int threshold;
122	unsigned int reg;
123	struct mtk_disp_rdma *rdma = comp_to_rdma(comp);
124
125	rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_0, 0xfff, width);
126	rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_1, 0xfffff, height);
127
128	/*
129	 * Enable FIFO underflow since DSI and DPI can't be blocked.
130	 * Keep the FIFO pseudo size reset default of 8 KiB. Set the
131	 * output threshold to 6 microseconds with 7/6 overhead to
132	 * account for blanking, and with a pixel depth of 4 bytes:
133	 */
134	threshold = width * height * vrefresh * 4 * 7 / 1000000;
135	reg = RDMA_FIFO_UNDERFLOW_EN |
136	      RDMA_FIFO_PSEUDO_SIZE(RDMA_FIFO_SIZE(rdma)) |
137	      RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
138	writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON);
139}
140
141static const struct mtk_ddp_comp_funcs mtk_disp_rdma_funcs = {
142	.config = mtk_rdma_config,
143	.start = mtk_rdma_start,
144	.stop = mtk_rdma_stop,
145	.enable_vblank = mtk_rdma_enable_vblank,
146	.disable_vblank = mtk_rdma_disable_vblank,
147};
148
149static int mtk_disp_rdma_bind(struct device *dev, struct device *master,
150			      void *data)
151{
152	struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
153	struct drm_device *drm_dev = data;
154	int ret;
155
156	ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
157	if (ret < 0) {
158		dev_err(dev, "Failed to register component %pOF: %d\n",
159			dev->of_node, ret);
160		return ret;
161	}
162
163	return 0;
164
165}
166
167static void mtk_disp_rdma_unbind(struct device *dev, struct device *master,
168				 void *data)
169{
170	struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
171	struct drm_device *drm_dev = data;
172
173	mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
174}
175
176static const struct component_ops mtk_disp_rdma_component_ops = {
177	.bind	= mtk_disp_rdma_bind,
178	.unbind = mtk_disp_rdma_unbind,
179};
180
181static int mtk_disp_rdma_probe(struct platform_device *pdev)
182{
183	struct device *dev = &pdev->dev;
184	struct mtk_disp_rdma *priv;
185	int comp_id;
186	int irq;
187	int ret;
188
189	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
190	if (!priv)
191		return -ENOMEM;
192
193	irq = platform_get_irq(pdev, 0);
194	if (irq < 0)
195		return irq;
196
197	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_RDMA);
198	if (comp_id < 0) {
199		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
200		return comp_id;
201	}
202
203	ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
204				&mtk_disp_rdma_funcs);
205	if (ret) {
206		dev_err(dev, "Failed to initialize component: %d\n", ret);
207		return ret;
208	}
209
210	/* Disable and clear pending interrupts */
211	writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_ENABLE);
212	writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_STATUS);
213
214	ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler,
215			       IRQF_TRIGGER_NONE, dev_name(dev), priv);
216	if (ret < 0) {
217		dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
218		return ret;
219	}
220
221	priv->data = of_device_get_match_data(dev);
222
223	platform_set_drvdata(pdev, priv);
224
225	ret = component_add(dev, &mtk_disp_rdma_component_ops);
226	if (ret)
227		dev_err(dev, "Failed to add component: %d\n", ret);
228
229	return ret;
230}
231
232static int mtk_disp_rdma_remove(struct platform_device *pdev)
233{
234	component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
235
236	return 0;
237}
238
239static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = {
240	.fifo_size = SZ_4K,
241};
242
243static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = {
244	.fifo_size = SZ_8K,
245};
246
247static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
248	{ .compatible = "mediatek,mt2701-disp-rdma",
249	  .data = &mt2701_rdma_driver_data},
250	{ .compatible = "mediatek,mt8173-disp-rdma",
251	  .data = &mt8173_rdma_driver_data},
252	{},
253};
254MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
255
256struct platform_driver mtk_disp_rdma_driver = {
257	.probe		= mtk_disp_rdma_probe,
258	.remove		= mtk_disp_rdma_remove,
259	.driver		= {
260		.name	= "mediatek-disp-rdma",
261		.owner	= THIS_MODULE,
262		.of_match_table = mtk_disp_rdma_driver_dt_match,
263	},
264};