aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/Kconfig34
-rw-r--r--drivers/gpu/drm/msm/Makefile25
-rw-r--r--drivers/gpu/drm/msm/NOTES69
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c235
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h112
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_connector.c461
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_i2c.c281
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c141
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c214
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_crtc.c684
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c317
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_format.c56
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_irq.c203
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_kms.c368
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_kms.h194
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_plane.c243
-rw-r--r--drivers/gpu/drm/msm/msm_connector.c34
-rw-r--r--drivers/gpu/drm/msm/msm_connector.h68
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c532
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h187
-rw-r--r--drivers/gpu/drm/msm/msm_fb.c202
-rw-r--r--drivers/gpu/drm/msm/msm_fbdev.c258
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c521
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h41
26 files changed, 5483 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 626bc0cb1046..39573c5f7518 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -223,3 +223,5 @@ source "drivers/gpu/drm/omapdrm/Kconfig"
223source "drivers/gpu/drm/tilcdc/Kconfig" 223source "drivers/gpu/drm/tilcdc/Kconfig"
224 224
225source "drivers/gpu/drm/qxl/Kconfig" 225source "drivers/gpu/drm/qxl/Kconfig"
226
227source "drivers/gpu/drm/msm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 7b2343a2f5eb..f089adfe70ee 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -54,4 +54,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
54obj-$(CONFIG_DRM_OMAP) += omapdrm/ 54obj-$(CONFIG_DRM_OMAP) += omapdrm/
55obj-$(CONFIG_DRM_TILCDC) += tilcdc/ 55obj-$(CONFIG_DRM_TILCDC) += tilcdc/
56obj-$(CONFIG_DRM_QXL) += qxl/ 56obj-$(CONFIG_DRM_QXL) += qxl/
57obj-$(CONFIG_DRM_MSM) += msm/
57obj-y += i2c/ 58obj-y += i2c/
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
new file mode 100644
index 000000000000..a06c19cc56f8
--- /dev/null
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -0,0 +1,34 @@
1
2config DRM_MSM
3 tristate "MSM DRM"
4 depends on DRM
5 depends on ARCH_MSM
6 depends on ARCH_MSM8960
7 select DRM_KMS_HELPER
8 select SHMEM
9 select TMPFS
10 default y
11 help
12 DRM/KMS driver for MSM/snapdragon.
13
14config DRM_MSM_FBDEV
15 bool "Enable legacy fbdev support for MSM modesetting driver"
16 depends on DRM_MSM
17 select FB_SYS_FILLRECT
18 select FB_SYS_COPYAREA
19 select FB_SYS_IMAGEBLIT
20 select FB_SYS_FOPS
21 default y
22 help
23 Choose this option if you have a need for the legacy fbdev
24 support. Note that this support also provide the linux console
25 support on top of the MSM modesetting driver.
26
27config DRM_MSM_REGISTER_LOGGING
28 bool "MSM DRM register logging"
29 depends on DRM_MSM
30 default n
31 help
32 Compile in support for logging register reads/writes in a format
33 that can be parsed by envytools demsm tool. If enabled, register
34 logging can be switched on via msm.reglog=y module param.
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
new file mode 100644
index 000000000000..4068122a9377
--- /dev/null
+++ b/drivers/gpu/drm/msm/Makefile
@@ -0,0 +1,25 @@
1ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm
2ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
3 ccflags-y += -Werror
4endif
5
6msm-y := \
7 hdmi/hdmi.o \
8 hdmi/hdmi_connector.o \
9 hdmi/hdmi_i2c.o \
10 hdmi/hdmi_phy_8960.o \
11 hdmi/hdmi_phy_8x60.o \
12 mdp4/mdp4_crtc.o \
13 mdp4/mdp4_dtv_encoder.o \
14 mdp4/mdp4_format.o \
15 mdp4/mdp4_irq.o \
16 mdp4/mdp4_kms.o \
17 mdp4/mdp4_plane.o \
18 msm_connector.o \
19 msm_drv.o \
20 msm_fb.o \
21 msm_gem.o
22
23msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
24
25obj-$(CONFIG_DRM_MSM) += msm.o
diff --git a/drivers/gpu/drm/msm/NOTES b/drivers/gpu/drm/msm/NOTES
new file mode 100644
index 000000000000..e036f6c1db94
--- /dev/null
+++ b/drivers/gpu/drm/msm/NOTES
@@ -0,0 +1,69 @@
1NOTES about msm drm/kms driver:
2
3In the current snapdragon SoC's, we have (at least) 3 different
4display controller blocks at play:
5 + MDP3 - ?? seems to be what is on geeksphone peak device
6 + MDP4 - S3 (APQ8060, touchpad), S4-pro (APQ8064, nexus4 & ifc6410)
7 + MDSS - snapdragon 800
8
9(I don't have a completely clear picture on which display controller
10maps to which part #)
11
12Plus a handful of blocks around them for HDMI/DSI/etc output.
13
14And on gpu side of things:
15 + zero, one, or two 2d cores (z180)
16 + and either a2xx or a3xx 3d core.
17
18But, HDMI/DSI/etc blocks seem like they can be shared across multiple
19display controller blocks. And I for sure don't want to have to deal
20with N different kms devices from xf86-video-freedreno. Plus, it
21seems like we can do some clever tricks like use GPU to trigger
22pageflip after rendering completes (ie. have the kms/crtc code build
23up gpu cmdstream to update scanout and write FLUSH register after).
24
25So, the approach is one drm driver, with some modularity. Different
26'struct msm_kms' implementations, depending on display controller.
27And one or more 'struct msm_gpu' for the various different gpu sub-
28modules.
29
30(Second part is not implemented yet. So far this is just basic KMS
31driver, and not exposing any custom ioctls to userspace for now.)
32
33The kms module provides the plane, crtc, and encoder objects, and
34loads whatever connectors are appropriate.
35
36For MDP4, the mapping is:
37
38 plane -> PIPE{RGBn,VGn} \
39 crtc -> OVLP{n} + DMA{P,S,E} (??) |-> MDP "device"
40 encoder -> DTV/LCDC/DSI (within MDP4) /
41 connector -> HDMI/DSI/etc --> other device(s)
42
43Since the irq's that drm core mostly cares about are vblank/framedone,
44we'll let msm_mdp4_kms provide the irq install/uninstall/etc functions
45and treat the MDP4 block's irq as "the" irq. Even though the connectors
46may have their own irqs which they install themselves. For this reason
47the display controller is the "master" device.
48
49Each connector probably ends up being a separate device, just for the
50logistics of finding/mapping io region, irq, etc. Idealy we would
51have a better way than just stashing the platform device in a global
52(ie. like DT super-node.. but I don't have any snapdragon hw yet that
53is using DT).
54
55Note that so far I've not been able to get any docs on the hw, and it
56seems that access to such docs would prevent me from working on the
57freedreno gallium driver. So there may be some mistakes in register
58names (I had to invent a few, since no sufficient hint was given in
59the downstream android fbdev driver), bitfield sizes, etc. My current
60state of understanding the registers is given in the envytools rnndb
61files at:
62
63 https://github.com/freedreno/envytools/tree/master/rnndb
64 (the mdp4/hdmi/dsi directories)
65
66These files are used both for a parser tool (in the same tree) to
67parse logged register reads/writes (both from downstream android fbdev
68driver, and this driver with register logging enabled), as well as to
69generate the register level headers.
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
new file mode 100644
index 000000000000..12ecfb928f75
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -0,0 +1,235 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "hdmi.h"
19
20static struct platform_device *hdmi_pdev;
21
22void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
23{
24 uint32_t ctrl = 0;
25
26 if (power_on) {
27 ctrl |= HDMI_CTRL_ENABLE;
28 if (!hdmi->hdmi_mode) {
29 ctrl |= HDMI_CTRL_HDMI;
30 hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
31 ctrl &= ~HDMI_CTRL_HDMI;
32 } else {
33 ctrl |= HDMI_CTRL_HDMI;
34 }
35 } else {
36 ctrl = HDMI_CTRL_HDMI;
37 }
38
39 hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
40 DBG("HDMI Core: %s, HDMI_CTRL=0x%08x",
41 power_on ? "Enable" : "Disable", ctrl);
42}
43
44static irqreturn_t hdmi_irq(int irq, void *dev_id)
45{
46 struct hdmi *hdmi = dev_id;
47
48 /* Process HPD: */
49 hdmi_connector_irq(hdmi->connector);
50
51 /* Process DDC: */
52 hdmi_i2c_irq(hdmi->i2c);
53
54 /* TODO audio.. */
55
56 return IRQ_HANDLED;
57}
58
59void hdmi_destroy(struct hdmi *hdmi)
60{
61 struct hdmi_phy *phy = hdmi->phy;
62
63 if (phy)
64 phy->funcs->destroy(phy);
65
66 if (hdmi->i2c)
67 hdmi_i2c_destroy(hdmi->i2c);
68
69 put_device(&hdmi->pdev->dev);
70}
71
72/* initialize connector */
73int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
74 struct drm_connector *connector)
75{
76 struct platform_device *pdev = hdmi_pdev;
77 struct hdmi_platform_config *config;
78 int ret;
79
80 if (!pdev) {
81 dev_err(dev->dev, "no hdmi device\n");
82 ret = -ENXIO;
83 goto fail;
84 }
85
86 config = pdev->dev.platform_data;
87
88 get_device(&pdev->dev);
89
90 hdmi->dev = dev;
91 hdmi->pdev = pdev;
92 hdmi->connector = connector;
93
94 /* not sure about which phy maps to which msm.. probably I miss some */
95 if (config->phy_init)
96 hdmi->phy = config->phy_init(hdmi);
97 else
98 hdmi->phy = ERR_PTR(-ENXIO);
99
100 if (IS_ERR(hdmi->phy)) {
101 ret = PTR_ERR(hdmi->phy);
102 dev_err(dev->dev, "failed to load phy: %d\n", ret);
103 hdmi->phy = NULL;
104 goto fail;
105 }
106
107 hdmi->mmio = msm_ioremap(pdev, "hdmi_msm_hdmi_addr", "HDMI");
108 if (IS_ERR(hdmi->mmio)) {
109 ret = PTR_ERR(hdmi->mmio);
110 goto fail;
111 }
112
113 hdmi->mvs = devm_regulator_get(&pdev->dev, "8901_hdmi_mvs");
114 if (IS_ERR(hdmi->mvs))
115 hdmi->mvs = devm_regulator_get(&pdev->dev, "hdmi_mvs");
116 if (IS_ERR(hdmi->mvs)) {
117 ret = PTR_ERR(hdmi->mvs);
118 dev_err(dev->dev, "failed to get mvs regulator: %d\n", ret);
119 goto fail;
120 }
121
122 hdmi->mpp0 = devm_regulator_get(&pdev->dev, "8901_mpp0");
123 if (IS_ERR(hdmi->mpp0))
124 hdmi->mpp0 = NULL;
125
126 hdmi->clk = devm_clk_get(&pdev->dev, "core_clk");
127 if (IS_ERR(hdmi->clk)) {
128 ret = PTR_ERR(hdmi->clk);
129 dev_err(dev->dev, "failed to get 'clk': %d\n", ret);
130 goto fail;
131 }
132
133 hdmi->m_pclk = devm_clk_get(&pdev->dev, "master_iface_clk");
134 if (IS_ERR(hdmi->m_pclk)) {
135 ret = PTR_ERR(hdmi->m_pclk);
136 dev_err(dev->dev, "failed to get 'm_pclk': %d\n", ret);
137 goto fail;
138 }
139
140 hdmi->s_pclk = devm_clk_get(&pdev->dev, "slave_iface_clk");
141 if (IS_ERR(hdmi->s_pclk)) {
142 ret = PTR_ERR(hdmi->s_pclk);
143 dev_err(dev->dev, "failed to get 's_pclk': %d\n", ret);
144 goto fail;
145 }
146
147 hdmi->i2c = hdmi_i2c_init(hdmi);
148 if (IS_ERR(hdmi->i2c)) {
149 ret = PTR_ERR(hdmi->i2c);
150 dev_err(dev->dev, "failed to get i2c: %d\n", ret);
151 hdmi->i2c = NULL;
152 goto fail;
153 }
154
155 hdmi->irq = platform_get_irq(pdev, 0);
156 if (hdmi->irq < 0) {
157 ret = hdmi->irq;
158 dev_err(dev->dev, "failed to get irq: %d\n", ret);
159 goto fail;
160 }
161
162 ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq,
163 NULL, hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
164 "hdmi_isr", hdmi);
165 if (ret < 0) {
166 dev_err(dev->dev, "failed to request IRQ%u: %d\n",
167 hdmi->irq, ret);
168 goto fail;
169 }
170
171 return 0;
172
173fail:
174 if (hdmi)
175 hdmi_destroy(hdmi);
176
177 return ret;
178}
179
180/*
181 * The hdmi device:
182 */
183
184static int hdmi_dev_probe(struct platform_device *pdev)
185{
186 static struct hdmi_platform_config config = {};
187#ifdef CONFIG_OF
188 /* TODO */
189#else
190 if (cpu_is_apq8064()) {
191 config.phy_init = hdmi_phy_8960_init;
192 config.ddc_clk_gpio = 70;
193 config.ddc_data_gpio = 71;
194 config.hpd_gpio = 72;
195 config.pmic_gpio = 13 + NR_GPIO_IRQS;
196 } else if (cpu_is_msm8960()) {
197 config.phy_init = hdmi_phy_8960_init;
198 config.ddc_clk_gpio = 100;
199 config.ddc_data_gpio = 101;
200 config.hpd_gpio = 102;
201 config.pmic_gpio = -1;
202 } else if (cpu_is_msm8x60()) {
203 config.phy_init = hdmi_phy_8x60_init;
204 config.ddc_clk_gpio = 170;
205 config.ddc_data_gpio = 171;
206 config.hpd_gpio = 172;
207 config.pmic_gpio = -1;
208 }
209#endif
210 pdev->dev.platform_data = &config;
211 hdmi_pdev = pdev;
212 return 0;
213}
214
215static int hdmi_dev_remove(struct platform_device *pdev)
216{
217 hdmi_pdev = NULL;
218 return 0;
219}
220
221static struct platform_driver hdmi_driver = {
222 .probe = hdmi_dev_probe,
223 .remove = hdmi_dev_remove,
224 .driver.name = "hdmi_msm",
225};
226
227void __init hdmi_register(void)
228{
229 platform_driver_register(&hdmi_driver);
230}
231
232void __exit hdmi_unregister(void)
233{
234 platform_driver_unregister(&hdmi_driver);
235}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
new file mode 100644
index 000000000000..34703fea22ca
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -0,0 +1,112 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __HDMI_CONNECTOR_H__
19#define __HDMI_CONNECTOR_H__
20
21#include <linux/i2c.h>
22#include <linux/clk.h>
23#include <linux/platform_device.h>
24#include <linux/regulator/consumer.h>
25
26#include "msm_drv.h"
27#include "hdmi.xml.h"
28
29
30struct hdmi_phy;
31
32struct hdmi {
33 struct drm_device *dev;
34 struct platform_device *pdev;
35
36 void __iomem *mmio;
37
38 struct regulator *mvs; /* HDMI_5V */
39 struct regulator *mpp0; /* External 5V */
40
41 struct clk *clk;
42 struct clk *m_pclk;
43 struct clk *s_pclk;
44
45 struct hdmi_phy *phy;
46 struct i2c_adapter *i2c;
47 struct drm_connector *connector;
48
49 bool hdmi_mode; /* are we in hdmi mode? */
50
51 int irq;
52};
53
54/* platform config data (ie. from DT, or pdata) */
55struct hdmi_platform_config {
56 struct hdmi_phy *(*phy_init)(struct hdmi *hdmi);
57 int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, pmic_gpio;
58};
59
60void hdmi_set_mode(struct hdmi *hdmi, bool power_on);
61void hdmi_destroy(struct hdmi *hdmi);
62int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
63 struct drm_connector *connector);
64
65static inline void hdmi_write(struct hdmi *hdmi, u32 reg, u32 data)
66{
67 msm_writel(data, hdmi->mmio + reg);
68}
69
70static inline u32 hdmi_read(struct hdmi *hdmi, u32 reg)
71{
72 return msm_readl(hdmi->mmio + reg);
73}
74
75/*
76 * The phy appears to be different, for example between 8960 and 8x60,
77 * so split the phy related functions out and load the correct one at
78 * runtime:
79 */
80
81struct hdmi_phy_funcs {
82 void (*destroy)(struct hdmi_phy *phy);
83 void (*reset)(struct hdmi_phy *phy);
84 void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock);
85 void (*powerdown)(struct hdmi_phy *phy);
86};
87
88struct hdmi_phy {
89 const struct hdmi_phy_funcs *funcs;
90};
91
92/*
93 * phy can be different on different generations:
94 */
95struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi);
96struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi);
97
98/*
99 * hdmi connector:
100 */
101
102void hdmi_connector_irq(struct drm_connector *connector);
103
104/*
105 * i2c adapter for ddc:
106 */
107
108void hdmi_i2c_irq(struct i2c_adapter *i2c);
109void hdmi_i2c_destroy(struct i2c_adapter *i2c);
110struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi);
111
112#endif /* __HDMI_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
new file mode 100644
index 000000000000..7d63f5ffa7ba
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
@@ -0,0 +1,461 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/gpio.h>
19
20#include "msm_connector.h"
21#include "hdmi.h"
22
23struct hdmi_connector {
24 struct msm_connector base;
25 struct hdmi hdmi;
26 unsigned long int pixclock;
27 bool enabled;
28};
29#define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)
30
31static int gpio_config(struct hdmi *hdmi, bool on)
32{
33 struct drm_device *dev = hdmi->dev;
34 struct hdmi_platform_config *config =
35 hdmi->pdev->dev.platform_data;
36 int ret;
37
38 if (on) {
39 ret = gpio_request(config->ddc_clk_gpio, "HDMI_DDC_CLK");
40 if (ret) {
41 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
42 "HDMI_DDC_CLK", config->ddc_clk_gpio, ret);
43 goto error1;
44 }
45 ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA");
46 if (ret) {
47 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
48 "HDMI_DDC_DATA", config->ddc_data_gpio, ret);
49 goto error2;
50 }
51 ret = gpio_request(config->hpd_gpio, "HDMI_HPD");
52 if (ret) {
53 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
54 "HDMI_HPD", config->hpd_gpio, ret);
55 goto error3;
56 }
57 if (config->pmic_gpio != -1) {
58 ret = gpio_request(config->pmic_gpio, "PMIC_HDMI_MUX_SEL");
59 if (ret) {
60 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
61 "PMIC_HDMI_MUX_SEL", config->pmic_gpio, ret);
62 goto error4;
63 }
64 gpio_set_value_cansleep(config->pmic_gpio, 0);
65 }
66 DBG("gpio on");
67 } else {
68 gpio_free(config->ddc_clk_gpio);
69 gpio_free(config->ddc_data_gpio);
70 gpio_free(config->hpd_gpio);
71
72 if (config->pmic_gpio != -1) {
73 gpio_set_value_cansleep(config->pmic_gpio, 1);
74 gpio_free(config->pmic_gpio);
75 }
76 DBG("gpio off");
77 }
78
79 return 0;
80
81error4:
82 gpio_free(config->hpd_gpio);
83error3:
84 gpio_free(config->ddc_data_gpio);
85error2:
86 gpio_free(config->ddc_clk_gpio);
87error1:
88 return ret;
89}
90
91static int hpd_enable(struct hdmi_connector *hdmi_connector)
92{
93 struct hdmi *hdmi = &hdmi_connector->hdmi;
94 struct drm_device *dev = hdmi_connector->base.base.dev;
95 struct hdmi_phy *phy = hdmi->phy;
96 uint32_t hpd_ctrl;
97 int ret;
98
99 ret = gpio_config(hdmi, true);
100 if (ret) {
101 dev_err(dev->dev, "failed to configure GPIOs: %d\n", ret);
102 goto fail;
103 }
104
105 ret = clk_prepare_enable(hdmi->clk);
106 if (ret) {
107 dev_err(dev->dev, "failed to enable 'clk': %d\n", ret);
108 goto fail;
109 }
110
111 ret = clk_prepare_enable(hdmi->m_pclk);
112 if (ret) {
113 dev_err(dev->dev, "failed to enable 'm_pclk': %d\n", ret);
114 goto fail;
115 }
116
117 ret = clk_prepare_enable(hdmi->s_pclk);
118 if (ret) {
119 dev_err(dev->dev, "failed to enable 's_pclk': %d\n", ret);
120 goto fail;
121 }
122
123 if (hdmi->mpp0)
124 ret = regulator_enable(hdmi->mpp0);
125 if (!ret)
126 ret = regulator_enable(hdmi->mvs);
127 if (ret) {
128 dev_err(dev->dev, "failed to enable regulators: %d\n", ret);
129 goto fail;
130 }
131
132 hdmi_set_mode(hdmi, false);
133 phy->funcs->reset(phy);
134 hdmi_set_mode(hdmi, true);
135
136 hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
137
138 /* enable HPD events: */
139 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
140 HDMI_HPD_INT_CTRL_INT_CONNECT |
141 HDMI_HPD_INT_CTRL_INT_EN);
142
143 /* set timeout to 4.1ms (max) for hardware debounce */
144 hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
145 hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
146
147 /* Toggle HPD circuit to trigger HPD sense */
148 hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
149 ~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
150 hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
151 HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
152
153 return 0;
154
155fail:
156 return ret;
157}
158
159static int hdp_disable(struct hdmi_connector *hdmi_connector)
160{
161 struct hdmi *hdmi = &hdmi_connector->hdmi;
162 struct drm_device *dev = hdmi_connector->base.base.dev;
163 int ret = 0;
164
165 /* Disable HPD interrupt */
166 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
167
168 hdmi_set_mode(hdmi, false);
169
170 if (hdmi->mpp0)
171 ret = regulator_disable(hdmi->mpp0);
172 if (!ret)
173 ret = regulator_disable(hdmi->mvs);
174 if (ret) {
175 dev_err(dev->dev, "failed to enable regulators: %d\n", ret);
176 goto fail;
177 }
178
179 clk_disable_unprepare(hdmi->clk);
180 clk_disable_unprepare(hdmi->m_pclk);
181 clk_disable_unprepare(hdmi->s_pclk);
182
183 ret = gpio_config(hdmi, false);
184 if (ret) {
185 dev_err(dev->dev, "failed to unconfigure GPIOs: %d\n", ret);
186 goto fail;
187 }
188
189 return 0;
190
191fail:
192 return ret;
193}
194
195void hdmi_connector_irq(struct drm_connector *connector)
196{
197 struct msm_connector *msm_connector = to_msm_connector(connector);
198 struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
199 struct hdmi *hdmi = &hdmi_connector->hdmi;
200 uint32_t hpd_int_status, hpd_int_ctrl;
201
202 /* Process HPD: */
203 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
204 hpd_int_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
205
206 if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
207 (hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
208 bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
209
210 DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
211
212 /* ack the irq: */
213 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
214 hpd_int_ctrl | HDMI_HPD_INT_CTRL_INT_ACK);
215
216 drm_helper_hpd_irq_event(connector->dev);
217
218 /* detect disconnect if we are connected or visa versa: */
219 hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
220 if (!detected)
221 hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
222 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
223 }
224}
225
226static enum drm_connector_status hdmi_connector_detect(
227 struct drm_connector *connector, bool force)
228{
229 struct msm_connector *msm_connector = to_msm_connector(connector);
230 struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
231 struct hdmi *hdmi = &hdmi_connector->hdmi;
232 uint32_t hpd_int_status;
233 int retry = 20;
234
235 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
236
237 /* sense seems to in some cases be momentarily de-asserted, don't
238 * let that trick us into thinking the monitor is gone:
239 */
240 while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
241 mdelay(10);
242 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
243 DBG("status=%08x", hpd_int_status);
244 }
245
246 return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
247 connector_status_connected : connector_status_disconnected;
248}
249
250static void hdmi_connector_destroy(struct drm_connector *connector)
251{
252 struct msm_connector *msm_connector = to_msm_connector(connector);
253 struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
254
255 hdp_disable(hdmi_connector);
256
257 drm_sysfs_connector_remove(connector);
258 drm_connector_cleanup(connector);
259
260 hdmi_destroy(&hdmi_connector->hdmi);
261
262 kfree(hdmi_connector);
263}
264
265static int hdmi_connector_get_modes(struct drm_connector *connector)
266{
267 struct msm_connector *msm_connector = to_msm_connector(connector);
268 struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
269 struct hdmi *hdmi = &hdmi_connector->hdmi;
270 struct edid *edid;
271 uint32_t hdmi_ctrl;
272 int ret = 0;
273
274 hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
275 hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
276
277 edid = drm_get_edid(connector, hdmi->i2c);
278
279 hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
280
281 drm_mode_connector_update_edid_property(connector, edid);
282
283 if (edid) {
284 ret = drm_add_edid_modes(connector, edid);
285 kfree(edid);
286 }
287
288 return ret;
289}
290
291static int hdmi_connector_mode_valid(struct drm_connector *connector,
292 struct drm_display_mode *mode)
293{
294 struct msm_connector *msm_connector = to_msm_connector(connector);
295 struct msm_drm_private *priv = connector->dev->dev_private;
296 struct msm_kms *kms = priv->kms;
297 long actual, requested;
298
299 requested = 1000 * mode->clock;
300 actual = kms->funcs->round_pixclk(kms,
301 requested, msm_connector->encoder);
302
303 DBG("requested=%ld, actual=%ld", requested, actual);
304
305 if (actual != requested)
306 return MODE_CLOCK_RANGE;
307
308 return 0;
309}
310
311static const struct drm_connector_funcs hdmi_connector_funcs = {
312 .dpms = drm_helper_connector_dpms,
313 .detect = hdmi_connector_detect,
314 .fill_modes = drm_helper_probe_single_connector_modes,
315 .destroy = hdmi_connector_destroy,
316};
317
318static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
319 .get_modes = hdmi_connector_get_modes,
320 .mode_valid = hdmi_connector_mode_valid,
321 .best_encoder = msm_connector_attached_encoder,
322};
323
324static void hdmi_connector_dpms(struct msm_connector *msm_connector, int mode)
325{
326 struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
327 struct hdmi *hdmi = &hdmi_connector->hdmi;
328 struct hdmi_phy *phy = hdmi->phy;
329 bool enabled = (mode == DRM_MODE_DPMS_ON);
330
331 DBG("mode=%d", mode);
332
333 if (enabled == hdmi_connector->enabled)
334 return;
335
336 if (enabled) {
337 phy->funcs->powerup(phy, hdmi_connector->pixclock);
338 hdmi_set_mode(hdmi, true);
339 } else {
340 hdmi_set_mode(hdmi, false);
341 phy->funcs->powerdown(phy);
342 }
343
344 hdmi_connector->enabled = enabled;
345}
346
347static void hdmi_connector_mode_set(struct msm_connector *msm_connector,
348 struct drm_display_mode *mode)
349{
350 struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
351 struct hdmi *hdmi = &hdmi_connector->hdmi;
352 int hstart, hend, vstart, vend;
353 uint32_t frame_ctrl;
354
355 hdmi_connector->pixclock = mode->clock * 1000;
356
357 hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1;
358
359 hstart = mode->htotal - mode->hsync_start;
360 hend = mode->htotal - mode->hsync_start + mode->hdisplay;
361
362 vstart = mode->vtotal - mode->vsync_start - 1;
363 vend = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;
364
365 DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",
366 mode->htotal, mode->vtotal, hstart, hend, vstart, vend);
367
368 hdmi_write(hdmi, REG_HDMI_TOTAL,
369 HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
370 HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));
371
372 hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
373 HDMI_ACTIVE_HSYNC_START(hstart) |
374 HDMI_ACTIVE_HSYNC_END(hend));
375 hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,
376 HDMI_ACTIVE_VSYNC_START(vstart) |
377 HDMI_ACTIVE_VSYNC_END(vend));
378
379 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
380 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
381 HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
382 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
383 HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
384 HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
385 } else {
386 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
387 HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
388 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
389 HDMI_VSYNC_ACTIVE_F2_START(0) |
390 HDMI_VSYNC_ACTIVE_F2_END(0));
391 }
392
393 frame_ctrl = 0;
394 if (mode->flags & DRM_MODE_FLAG_NHSYNC)
395 frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW;
396 if (mode->flags & DRM_MODE_FLAG_NVSYNC)
397 frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW;
398 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
399 frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN;
400 DBG("frame_ctrl=%08x", frame_ctrl);
401 hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);
402
403 // TODO until we have audio, this might be safest:
404 if (hdmi->hdmi_mode)
405 hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE);
406}
407
408static const struct msm_connector_funcs msm_connector_funcs = {
409 .dpms = hdmi_connector_dpms,
410 .mode_set = hdmi_connector_mode_set,
411};
412
413/* initialize connector */
414struct drm_connector *hdmi_connector_init(struct drm_device *dev,
415 struct drm_encoder *encoder)
416{
417 struct drm_connector *connector = NULL;
418 struct hdmi_connector *hdmi_connector;
419 int ret;
420
421 hdmi_connector = kzalloc(sizeof(*hdmi_connector), GFP_KERNEL);
422 if (!hdmi_connector) {
423 ret = -ENOMEM;
424 goto fail;
425 }
426
427 connector = &hdmi_connector->base.base;
428
429 msm_connector_init(&hdmi_connector->base,
430 &msm_connector_funcs, encoder);
431 drm_connector_init(dev, connector, &hdmi_connector_funcs,
432 DRM_MODE_CONNECTOR_HDMIA);
433 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
434
435 connector->polled = DRM_CONNECTOR_POLL_HPD;
436
437 connector->interlace_allowed = 1;
438 connector->doublescan_allowed = 0;
439
440 drm_sysfs_connector_add(connector);
441
442 ret = hdmi_init(&hdmi_connector->hdmi, dev, connector);
443 if (ret)
444 goto fail;
445
446 ret = hpd_enable(hdmi_connector);
447 if (ret) {
448 dev_err(dev->dev, "failed to enable HPD: %d\n", ret);
449 goto fail;
450 }
451
452 drm_mode_connector_attach_encoder(connector, encoder);
453
454 return connector;
455
456fail:
457 if (connector)
458 hdmi_connector_destroy(connector);
459
460 return ERR_PTR(ret);
461}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
new file mode 100644
index 000000000000..f4ab7f70fed1
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
@@ -0,0 +1,281 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "hdmi.h"
19
20struct hdmi_i2c_adapter {
21 struct i2c_adapter base;
22 struct hdmi *hdmi;
23 bool sw_done;
24 wait_queue_head_t ddc_event;
25};
26#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base)
27
28static void init_ddc(struct hdmi_i2c_adapter *hdmi_i2c)
29{
30 struct hdmi *hdmi = hdmi_i2c->hdmi;
31
32 hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
33 HDMI_DDC_CTRL_SW_STATUS_RESET);
34 hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
35 HDMI_DDC_CTRL_SOFT_RESET);
36
37 hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
38 HDMI_DDC_SPEED_THRESHOLD(2) |
39 HDMI_DDC_SPEED_PRESCALE(10));
40
41 hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
42 HDMI_DDC_SETUP_TIMEOUT(0xff));
43
44 /* enable reference timer for 27us */
45 hdmi_write(hdmi, REG_HDMI_DDC_REF,
46 HDMI_DDC_REF_REFTIMER_ENABLE |
47 HDMI_DDC_REF_REFTIMER(27));
48}
49
50static int ddc_clear_irq(struct hdmi_i2c_adapter *hdmi_i2c)
51{
52 struct hdmi *hdmi = hdmi_i2c->hdmi;
53 struct drm_device *dev = hdmi->dev;
54 uint32_t retry = 0xffff;
55 uint32_t ddc_int_ctrl;
56
57 do {
58 --retry;
59
60 hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
61 HDMI_DDC_INT_CTRL_SW_DONE_ACK |
62 HDMI_DDC_INT_CTRL_SW_DONE_MASK);
63
64 ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
65
66 } while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry);
67
68 if (!retry) {
69 dev_err(dev->dev, "timeout waiting for DDC\n");
70 return -ETIMEDOUT;
71 }
72
73 hdmi_i2c->sw_done = false;
74
75 return 0;
76}
77
78#define MAX_TRANSACTIONS 4
79
80static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c)
81{
82 struct hdmi *hdmi = hdmi_i2c->hdmi;
83
84 if (!hdmi_i2c->sw_done) {
85 uint32_t ddc_int_ctrl;
86
87 ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
88
89 if ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_MASK) &&
90 (ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT)) {
91 hdmi_i2c->sw_done = true;
92 hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
93 HDMI_DDC_INT_CTRL_SW_DONE_ACK);
94 }
95 }
96
97 return hdmi_i2c->sw_done;
98}
99
100static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
101 struct i2c_msg *msgs, int num)
102{
103 struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
104 struct hdmi *hdmi = hdmi_i2c->hdmi;
105 struct drm_device *dev = hdmi->dev;
106 static const uint32_t nack[] = {
107 HDMI_DDC_SW_STATUS_NACK0, HDMI_DDC_SW_STATUS_NACK1,
108 HDMI_DDC_SW_STATUS_NACK2, HDMI_DDC_SW_STATUS_NACK3,
109 };
110 int indices[MAX_TRANSACTIONS];
111 int ret, i, j, index = 0;
112 uint32_t ddc_status, ddc_data, i2c_trans;
113
114 num = min(num, MAX_TRANSACTIONS);
115
116 WARN_ON(!(hdmi_read(hdmi, REG_HDMI_CTRL) & HDMI_CTRL_ENABLE));
117
118 if (num == 0)
119 return num;
120
121 init_ddc(hdmi_i2c);
122
123 ret = ddc_clear_irq(hdmi_i2c);
124 if (ret)
125 return ret;
126
127 for (i = 0; i < num; i++) {
128 struct i2c_msg *p = &msgs[i];
129 uint32_t raw_addr = p->addr << 1;
130
131 if (p->flags & I2C_M_RD)
132 raw_addr |= 1;
133
134 ddc_data = HDMI_DDC_DATA_DATA(raw_addr) |
135 HDMI_DDC_DATA_DATA_RW(DDC_WRITE);
136
137 if (i == 0) {
138 ddc_data |= HDMI_DDC_DATA_INDEX(0) |
139 HDMI_DDC_DATA_INDEX_WRITE;
140 }
141
142 hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
143 index++;
144
145 indices[i] = index;
146
147 if (p->flags & I2C_M_RD) {
148 index += p->len;
149 } else {
150 for (j = 0; j < p->len; j++) {
151 ddc_data = HDMI_DDC_DATA_DATA(p->buf[j]) |
152 HDMI_DDC_DATA_DATA_RW(DDC_WRITE);
153 hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
154 index++;
155 }
156 }
157
158 i2c_trans = HDMI_I2C_TRANSACTION_REG_CNT(p->len) |
159 HDMI_I2C_TRANSACTION_REG_RW(
160 (p->flags & I2C_M_RD) ? DDC_READ : DDC_WRITE) |
161 HDMI_I2C_TRANSACTION_REG_START;
162
163 if (i == (num - 1))
164 i2c_trans |= HDMI_I2C_TRANSACTION_REG_STOP;
165
166 hdmi_write(hdmi, REG_HDMI_I2C_TRANSACTION(i), i2c_trans);
167 }
168
169 /* trigger the transfer: */
170 hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
171 HDMI_DDC_CTRL_TRANSACTION_CNT(num - 1) |
172 HDMI_DDC_CTRL_GO);
173
174 ret = wait_event_timeout(hdmi_i2c->ddc_event, sw_done(hdmi_i2c), HZ/4);
175 if (ret <= 0) {
176 if (ret == 0)
177 ret = -ETIMEDOUT;
178 dev_warn(dev->dev, "DDC timeout: %d\n", ret);
179 DBG("sw_status=%08x, hw_status=%08x, int_ctrl=%08x",
180 hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS),
181 hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS),
182 hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL));
183 return ret;
184 }
185
186 ddc_status = hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS);
187
188 /* read back results of any read transactions: */
189 for (i = 0; i < num; i++) {
190 struct i2c_msg *p = &msgs[i];
191
192 if (!(p->flags & I2C_M_RD))
193 continue;
194
195 /* check for NACK: */
196 if (ddc_status & nack[i]) {
197 DBG("ddc_status=%08x", ddc_status);
198 break;
199 }
200
201 ddc_data = HDMI_DDC_DATA_DATA_RW(DDC_READ) |
202 HDMI_DDC_DATA_INDEX(indices[i]) |
203 HDMI_DDC_DATA_INDEX_WRITE;
204
205 hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
206
207 /* discard first byte: */
208 hdmi_read(hdmi, REG_HDMI_DDC_DATA);
209
210 for (j = 0; j < p->len; j++) {
211 ddc_data = hdmi_read(hdmi, REG_HDMI_DDC_DATA);
212 p->buf[j] = FIELD(ddc_data, HDMI_DDC_DATA_DATA);
213 }
214 }
215
216 return i;
217}
218
219static u32 hdmi_i2c_func(struct i2c_adapter *adapter)
220{
221 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
222}
223
224static const struct i2c_algorithm hdmi_i2c_algorithm = {
225 .master_xfer = hdmi_i2c_xfer,
226 .functionality = hdmi_i2c_func,
227};
228
229void hdmi_i2c_irq(struct i2c_adapter *i2c)
230{
231 struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
232
233 if (sw_done(hdmi_i2c))
234 wake_up_all(&hdmi_i2c->ddc_event);
235}
236
237void hdmi_i2c_destroy(struct i2c_adapter *i2c)
238{
239 struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
240 i2c_del_adapter(i2c);
241 kfree(hdmi_i2c);
242}
243
244struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi)
245{
246 struct drm_device *dev = hdmi->dev;
247 struct hdmi_i2c_adapter *hdmi_i2c;
248 struct i2c_adapter *i2c = NULL;
249 int ret;
250
251 hdmi_i2c = kzalloc(sizeof(*hdmi_i2c), GFP_KERNEL);
252 if (!hdmi_i2c) {
253 ret = -ENOMEM;
254 goto fail;
255 }
256
257 i2c = &hdmi_i2c->base;
258
259 hdmi_i2c->hdmi = hdmi;
260 init_waitqueue_head(&hdmi_i2c->ddc_event);
261
262
263 i2c->owner = THIS_MODULE;
264 i2c->class = I2C_CLASS_DDC;
265 snprintf(i2c->name, sizeof(i2c->name), "msm hdmi i2c");
266 i2c->dev.parent = &hdmi->pdev->dev;
267 i2c->algo = &hdmi_i2c_algorithm;
268
269 ret = i2c_add_adapter(i2c);
270 if (ret) {
271 dev_err(dev->dev, "failed to register hdmi i2c: %d\n", ret);
272 goto fail;
273 }
274
275 return i2c;
276
277fail:
278 if (i2c)
279 hdmi_i2c_destroy(i2c);
280 return ERR_PTR(ret);
281}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
new file mode 100644
index 000000000000..e5b7ed5b8f01
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
@@ -0,0 +1,141 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "hdmi.h"
19
20struct hdmi_phy_8960 {
21 struct hdmi_phy base;
22 struct hdmi *hdmi;
23};
24#define to_hdmi_phy_8960(x) container_of(x, struct hdmi_phy_8960, base)
25
26static void hdmi_phy_8960_destroy(struct hdmi_phy *phy)
27{
28 struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
29 kfree(phy_8960);
30}
31
32static void hdmi_phy_8960_reset(struct hdmi_phy *phy)
33{
34 struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
35 struct hdmi *hdmi = phy_8960->hdmi;
36 unsigned int val;
37
38 val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
39
40 if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
41 /* pull low */
42 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
43 val & ~HDMI_PHY_CTRL_SW_RESET);
44 } else {
45 /* pull high */
46 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
47 val | HDMI_PHY_CTRL_SW_RESET);
48 }
49
50 if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
51 /* pull low */
52 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
53 val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
54 } else {
55 /* pull high */
56 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
57 val | HDMI_PHY_CTRL_SW_RESET_PLL);
58 }
59
60 msleep(100);
61
62 if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
63 /* pull high */
64 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
65 val | HDMI_PHY_CTRL_SW_RESET);
66 } else {
67 /* pull low */
68 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
69 val & ~HDMI_PHY_CTRL_SW_RESET);
70 }
71
72 if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
73 /* pull high */
74 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
75 val | HDMI_PHY_CTRL_SW_RESET_PLL);
76 } else {
77 /* pull low */
78 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
79 val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
80 }
81}
82
83static void hdmi_phy_8960_powerup(struct hdmi_phy *phy,
84 unsigned long int pixclock)
85{
86 struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
87 struct hdmi *hdmi = phy_8960->hdmi;
88
89 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b);
90 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2);
91 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00);
92 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG5, 0x00);
93 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG6, 0x00);
94 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG7, 0x00);
95 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG8, 0x00);
96 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG9, 0x00);
97 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG10, 0x00);
98 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG11, 0x00);
99 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG3, 0x20);
100}
101
102static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy)
103{
104 struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
105 struct hdmi *hdmi = phy_8960->hdmi;
106
107 hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f);
108}
109
110static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = {
111 .destroy = hdmi_phy_8960_destroy,
112 .reset = hdmi_phy_8960_reset,
113 .powerup = hdmi_phy_8960_powerup,
114 .powerdown = hdmi_phy_8960_powerdown,
115};
116
117struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
118{
119 struct hdmi_phy_8960 *phy_8960;
120 struct hdmi_phy *phy = NULL;
121 int ret;
122
123 phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL);
124 if (!phy_8960) {
125 ret = -ENOMEM;
126 goto fail;
127 }
128
129 phy = &phy_8960->base;
130
131 phy->funcs = &hdmi_phy_8960_funcs;
132
133 phy_8960->hdmi = hdmi;
134
135 return phy;
136
137fail:
138 if (phy)
139 hdmi_phy_8960_destroy(phy);
140 return ERR_PTR(ret);
141}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c
new file mode 100644
index 000000000000..391433c1af7c
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c
@@ -0,0 +1,214 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "hdmi.h"
19
20struct hdmi_phy_8x60 {
21 struct hdmi_phy base;
22 struct hdmi *hdmi;
23};
24#define to_hdmi_phy_8x60(x) container_of(x, struct hdmi_phy_8x60, base)
25
26static void hdmi_phy_8x60_destroy(struct hdmi_phy *phy)
27{
28 struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
29 kfree(phy_8x60);
30}
31
32static void hdmi_phy_8x60_reset(struct hdmi_phy *phy)
33{
34 struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
35 struct hdmi *hdmi = phy_8x60->hdmi;
36 unsigned int val;
37
38 val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
39
40 if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
41 /* pull low */
42 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
43 val & ~HDMI_PHY_CTRL_SW_RESET);
44 } else {
45 /* pull high */
46 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
47 val | HDMI_PHY_CTRL_SW_RESET);
48 }
49
50 msleep(100);
51
52 if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
53 /* pull high */
54 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
55 val | HDMI_PHY_CTRL_SW_RESET);
56 } else {
57 /* pull low */
58 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
59 val & ~HDMI_PHY_CTRL_SW_RESET);
60 }
61}
62
63static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy,
64 unsigned long int pixclock)
65{
66 struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
67 struct hdmi *hdmi = phy_8x60->hdmi;
68
69 /* De-serializer delay D/C for non-lbk mode: */
70 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG0,
71 HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(3));
72
73 if (pixclock == 27000000) {
74 /* video_format == HDMI_VFRMT_720x480p60_16_9 */
75 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1,
76 HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) |
77 HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(3));
78 } else {
79 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1,
80 HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) |
81 HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(4));
82 }
83
84 /* No matter what, start from the power down mode: */
85 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
86 HDMI_8x60_PHY_REG2_PD_PWRGEN |
87 HDMI_8x60_PHY_REG2_PD_PLL |
88 HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
89 HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
90 HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
91 HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
92 HDMI_8x60_PHY_REG2_PD_DESER);
93
94 /* Turn PowerGen on: */
95 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
96 HDMI_8x60_PHY_REG2_PD_PLL |
97 HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
98 HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
99 HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
100 HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
101 HDMI_8x60_PHY_REG2_PD_DESER);
102
103 /* Turn PLL power on: */
104 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
105 HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
106 HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
107 HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
108 HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
109 HDMI_8x60_PHY_REG2_PD_DESER);
110
111 /* Write to HIGH after PLL power down de-assert: */
112 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3,
113 HDMI_8x60_PHY_REG3_PLL_ENABLE);
114
115 /* ASIC power on; PHY REG9 = 0 */
116 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0);
117
118 /* Enable PLL lock detect, PLL lock det will go high after lock
119 * Enable the re-time logic
120 */
121 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12,
122 HDMI_8x60_PHY_REG12_RETIMING_EN |
123 HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN);
124
125 /* Drivers are on: */
126 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
127 HDMI_8x60_PHY_REG2_PD_DESER);
128
129 /* If the RX detector is needed: */
130 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
131 HDMI_8x60_PHY_REG2_RCV_SENSE_EN |
132 HDMI_8x60_PHY_REG2_PD_DESER);
133
134 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG4, 0);
135 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG5, 0);
136 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG6, 0);
137 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG7, 0);
138 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG8, 0);
139 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0);
140 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG10, 0);
141 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG11, 0);
142
143 /* If we want to use lock enable based on counting: */
144 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12,
145 HDMI_8x60_PHY_REG12_RETIMING_EN |
146 HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN |
147 HDMI_8x60_PHY_REG12_FORCE_LOCK);
148}
149
150static void hdmi_phy_8x60_powerdown(struct hdmi_phy *phy)
151{
152 struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
153 struct hdmi *hdmi = phy_8x60->hdmi;
154
155 /* Assert RESET PHY from controller */
156 hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
157 HDMI_PHY_CTRL_SW_RESET);
158 udelay(10);
159 /* De-assert RESET PHY from controller */
160 hdmi_write(hdmi, REG_HDMI_PHY_CTRL, 0);
161 /* Turn off Driver */
162 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
163 HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
164 HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
165 HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
166 HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
167 HDMI_8x60_PHY_REG2_PD_DESER);
168 udelay(10);
169 /* Disable PLL */
170 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3, 0);
171 /* Power down PHY, but keep RX-sense: */
172 hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
173 HDMI_8x60_PHY_REG2_RCV_SENSE_EN |
174 HDMI_8x60_PHY_REG2_PD_PWRGEN |
175 HDMI_8x60_PHY_REG2_PD_PLL |
176 HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
177 HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
178 HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
179 HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
180 HDMI_8x60_PHY_REG2_PD_DESER);
181}
182
183static const struct hdmi_phy_funcs hdmi_phy_8x60_funcs = {
184 .destroy = hdmi_phy_8x60_destroy,
185 .reset = hdmi_phy_8x60_reset,
186 .powerup = hdmi_phy_8x60_powerup,
187 .powerdown = hdmi_phy_8x60_powerdown,
188};
189
190struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi)
191{
192 struct hdmi_phy_8x60 *phy_8x60;
193 struct hdmi_phy *phy = NULL;
194 int ret;
195
196 phy_8x60 = kzalloc(sizeof(*phy_8x60), GFP_KERNEL);
197 if (!phy_8x60) {
198 ret = -ENOMEM;
199 goto fail;
200 }
201
202 phy = &phy_8x60->base;
203
204 phy->funcs = &hdmi_phy_8x60_funcs;
205
206 phy_8x60->hdmi = hdmi;
207
208 return phy;
209
210fail:
211 if (phy)
212 hdmi_phy_8x60_destroy(phy);
213 return ERR_PTR(ret);
214}
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
new file mode 100644
index 000000000000..bda0fc40b207
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
@@ -0,0 +1,684 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "mdp4_kms.h"
19
20#include <drm/drm_mode.h>
21#include "drm_crtc.h"
22#include "drm_crtc_helper.h"
23#include "drm_flip_work.h"
24
25struct mdp4_crtc {
26 struct drm_crtc base;
27 char name[8];
28 struct drm_plane *plane;
29 int id;
30 int ovlp;
31 enum mdp4_dma dma;
32 bool enabled;
33
34 /* which mixer/encoder we route output to: */
35 int mixer;
36
37 struct {
38 spinlock_t lock;
39 bool stale;
40 uint32_t width, height;
41
42 /* next cursor to scan-out: */
43 uint32_t next_iova;
44 struct drm_gem_object *next_bo;
45
46 /* current cursor being scanned out: */
47 struct drm_gem_object *scanout_bo;
48 } cursor;
49
50
51 /* if there is a pending flip, these will be non-null: */
52 struct drm_pending_vblank_event *event;
53 struct work_struct pageflip_work;
54
55 /* the fb that we currently hold a scanout ref to: */
56 struct drm_framebuffer *fb;
57
58 /* for unref'ing framebuffers after scanout completes: */
59 struct drm_flip_work unref_fb_work;
60
61 /* for unref'ing cursor bo's after scanout completes: */
62 struct drm_flip_work unref_cursor_work;
63
64 struct mdp4_irq vblank;
65 struct mdp4_irq err;
66};
67#define to_mdp4_crtc(x) container_of(x, struct mdp4_crtc, base)
68
69static struct mdp4_kms *get_kms(struct drm_crtc *crtc)
70{
71 struct msm_drm_private *priv = crtc->dev->dev_private;
72 return to_mdp4_kms(priv->kms);
73}
74
75static void update_fb(struct drm_crtc *crtc, bool async,
76 struct drm_framebuffer *new_fb)
77{
78 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
79 struct drm_framebuffer *old_fb = mdp4_crtc->fb;
80
81 if (old_fb)
82 drm_flip_work_queue(&mdp4_crtc->unref_fb_work, old_fb);
83
84 /* grab reference to incoming scanout fb: */
85 drm_framebuffer_reference(new_fb);
86 mdp4_crtc->base.fb = new_fb;
87 mdp4_crtc->fb = new_fb;
88
89 if (!async) {
90 /* enable vblank to pick up the old_fb */
91 mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank);
92 }
93}
94
95static void complete_flip(struct drm_crtc *crtc, bool canceled)
96{
97 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
98 struct drm_device *dev = crtc->dev;
99 struct drm_pending_vblank_event *event;
100 unsigned long flags;
101
102 spin_lock_irqsave(&dev->event_lock, flags);
103 event = mdp4_crtc->event;
104 if (event) {
105 mdp4_crtc->event = NULL;
106 if (canceled)
107 event->base.destroy(&event->base);
108 else
109 drm_send_vblank_event(dev, mdp4_crtc->id, event);
110 }
111 spin_unlock_irqrestore(&dev->event_lock, flags);
112}
113
114static void crtc_flush(struct drm_crtc *crtc)
115{
116 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
117 struct mdp4_kms *mdp4_kms = get_kms(crtc);
118 uint32_t flush = 0;
119
120 flush |= pipe2flush(mdp4_plane_pipe(mdp4_crtc->plane));
121 flush |= ovlp2flush(mdp4_crtc->ovlp);
122
123 DBG("%s: flush=%08x", mdp4_crtc->name, flush);
124
125 mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush);
126}
127
128static void pageflip_worker(struct work_struct *work)
129{
130 struct mdp4_crtc *mdp4_crtc =
131 container_of(work, struct mdp4_crtc, pageflip_work);
132 struct drm_crtc *crtc = &mdp4_crtc->base;
133
134 mdp4_plane_set_scanout(mdp4_crtc->plane, crtc->fb);
135 crtc_flush(crtc);
136
137 /* enable vblank to complete flip: */
138 mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank);
139}
140
141static void unref_fb_worker(struct drm_flip_work *work, void *val)
142{
143 struct mdp4_crtc *mdp4_crtc =
144 container_of(work, struct mdp4_crtc, unref_fb_work);
145 struct drm_device *dev = mdp4_crtc->base.dev;
146
147 mutex_lock(&dev->mode_config.mutex);
148 drm_framebuffer_unreference(val);
149 mutex_unlock(&dev->mode_config.mutex);
150}
151
152static void unref_cursor_worker(struct drm_flip_work *work, void *val)
153{
154 struct mdp4_crtc *mdp4_crtc =
155 container_of(work, struct mdp4_crtc, unref_cursor_work);
156 struct mdp4_kms *mdp4_kms = get_kms(&mdp4_crtc->base);
157
158 msm_gem_put_iova(val, mdp4_kms->id);
159 drm_gem_object_unreference_unlocked(val);
160}
161
162static void mdp4_crtc_destroy(struct drm_crtc *crtc)
163{
164 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
165
166 mdp4_crtc->plane->funcs->destroy(mdp4_crtc->plane);
167
168 drm_crtc_cleanup(crtc);
169 drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work);
170 drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work);
171
172 kfree(mdp4_crtc);
173}
174
175static void mdp4_crtc_dpms(struct drm_crtc *crtc, int mode)
176{
177 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
178 struct mdp4_kms *mdp4_kms = get_kms(crtc);
179 bool enabled = (mode == DRM_MODE_DPMS_ON);
180
181 DBG("%s: mode=%d", mdp4_crtc->name, mode);
182
183 if (enabled != mdp4_crtc->enabled) {
184 if (enabled) {
185 mdp4_enable(mdp4_kms);
186 mdp4_irq_register(mdp4_kms, &mdp4_crtc->err);
187 } else {
188 mdp4_irq_unregister(mdp4_kms, &mdp4_crtc->err);
189 mdp4_disable(mdp4_kms);
190 }
191 mdp4_crtc->enabled = enabled;
192 }
193}
194
195static bool mdp4_crtc_mode_fixup(struct drm_crtc *crtc,
196 const struct drm_display_mode *mode,
197 struct drm_display_mode *adjusted_mode)
198{
199 return true;
200}
201
202static void blend_setup(struct drm_crtc *crtc)
203{
204 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
205 struct mdp4_kms *mdp4_kms = get_kms(crtc);
206 int i, ovlp = mdp4_crtc->ovlp;
207 uint32_t mixer_cfg = 0;
208
209 /*
210 * This probably would also need to be triggered by any attached
211 * plane when it changes.. for now since we are only using a single
212 * private plane, the configuration is hard-coded:
213 */
214
215 mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW0(ovlp), 0);
216 mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW1(ovlp), 0);
217 mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0);
218 mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0);
219
220 for (i = 0; i < 4; i++) {
221 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_FG_ALPHA(ovlp, i), 0);
222 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_BG_ALPHA(ovlp, i), 0);
223 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_OP(ovlp, i),
224 MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_CONST) |
225 MDP4_OVLP_STAGE_OP_BG_ALPHA(BG_CONST));
226 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_CO3(ovlp, i), 0);
227 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW0(ovlp, i), 0);
228 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW1(ovlp, i), 0);
229 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH0(ovlp, i), 0);
230 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(ovlp, i), 0);
231 }
232
233 /* TODO single register for all CRTCs, so this won't work properly
234 * when multiple CRTCs are active..
235 */
236 switch (mdp4_plane_pipe(mdp4_crtc->plane)) {
237 case VG1:
238 mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(STAGE_BASE) |
239 COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1);
240 break;
241 case VG2:
242 mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(STAGE_BASE) |
243 COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1);
244 break;
245 case RGB1:
246 mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(STAGE_BASE) |
247 COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1);
248 break;
249 case RGB2:
250 mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(STAGE_BASE) |
251 COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1);
252 break;
253 case RGB3:
254 mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(STAGE_BASE) |
255 COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1);
256 break;
257 case VG3:
258 mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(STAGE_BASE) |
259 COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1);
260 break;
261 case VG4:
262 mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(STAGE_BASE) |
263 COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1);
264 break;
265 default:
266 WARN_ON("invalid pipe");
267 break;
268 }
269 mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg);
270}
271
272static int mdp4_crtc_mode_set(struct drm_crtc *crtc,
273 struct drm_display_mode *mode,
274 struct drm_display_mode *adjusted_mode,
275 int x, int y,
276 struct drm_framebuffer *old_fb)
277{
278 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
279 struct mdp4_kms *mdp4_kms = get_kms(crtc);
280 enum mdp4_dma dma = mdp4_crtc->dma;
281 int ret, ovlp = mdp4_crtc->ovlp;
282
283 mode = adjusted_mode;
284
285 DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
286 mdp4_crtc->name, mode->base.id, mode->name,
287 mode->vrefresh, mode->clock,
288 mode->hdisplay, mode->hsync_start,
289 mode->hsync_end, mode->htotal,
290 mode->vdisplay, mode->vsync_start,
291 mode->vsync_end, mode->vtotal,
292 mode->type, mode->flags);
293
294 mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_SIZE(dma),
295 MDP4_DMA_SRC_SIZE_WIDTH(mode->hdisplay) |
296 MDP4_DMA_SRC_SIZE_HEIGHT(mode->vdisplay));
297
298 /* take data from pipe: */
299 mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_BASE(dma), 0);
300 mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_STRIDE(dma),
301 crtc->fb->pitches[0]);
302 mdp4_write(mdp4_kms, REG_MDP4_DMA_DST_SIZE(dma),
303 MDP4_DMA_DST_SIZE_WIDTH(0) |
304 MDP4_DMA_DST_SIZE_HEIGHT(0));
305
306 mdp4_write(mdp4_kms, REG_MDP4_OVLP_BASE(ovlp), 0);
307 mdp4_write(mdp4_kms, REG_MDP4_OVLP_SIZE(ovlp),
308 MDP4_OVLP_SIZE_WIDTH(mode->hdisplay) |
309 MDP4_OVLP_SIZE_HEIGHT(mode->vdisplay));
310 mdp4_write(mdp4_kms, REG_MDP4_OVLP_STRIDE(ovlp),
311 crtc->fb->pitches[0]);
312
313 mdp4_write(mdp4_kms, REG_MDP4_OVLP_CFG(ovlp), 1);
314
315 update_fb(crtc, false, crtc->fb);
316
317 ret = mdp4_plane_mode_set(mdp4_crtc->plane, crtc, crtc->fb,
318 0, 0, mode->hdisplay, mode->vdisplay,
319 x << 16, y << 16,
320 mode->hdisplay << 16, mode->vdisplay << 16);
321 if (ret) {
322 dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n",
323 mdp4_crtc->name, ret);
324 return ret;
325 }
326
327 if (dma == DMA_E) {
328 mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(0), 0x00ff0000);
329 mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(1), 0x00ff0000);
330 mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(2), 0x00ff0000);
331 }
332
333 return 0;
334}
335
336static void mdp4_crtc_prepare(struct drm_crtc *crtc)
337{
338 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
339 DBG("%s", mdp4_crtc->name);
340 /* make sure we hold a ref to mdp clks while setting up mode: */
341 mdp4_enable(get_kms(crtc));
342 mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
343}
344
345static void mdp4_crtc_commit(struct drm_crtc *crtc)
346{
347 mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
348 crtc_flush(crtc);
349 /* drop the ref to mdp clk's that we got in prepare: */
350 mdp4_disable(get_kms(crtc));
351}
352
353static int mdp4_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
354 struct drm_framebuffer *old_fb)
355{
356 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
357 struct drm_plane *plane = mdp4_crtc->plane;
358 struct drm_display_mode *mode = &crtc->mode;
359
360 update_fb(crtc, false, crtc->fb);
361
362 return mdp4_plane_mode_set(plane, crtc, crtc->fb,
363 0, 0, mode->hdisplay, mode->vdisplay,
364 x << 16, y << 16,
365 mode->hdisplay << 16, mode->vdisplay << 16);
366}
367
368static void mdp4_crtc_load_lut(struct drm_crtc *crtc)
369{
370}
371
372static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
373 struct drm_framebuffer *new_fb,
374 struct drm_pending_vblank_event *event)
375{
376 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
377 struct drm_device *dev = crtc->dev;
378 struct drm_gem_object *obj;
379
380 if (mdp4_crtc->event) {
381 dev_err(dev->dev, "already pending flip!\n");
382 return -EBUSY;
383 }
384
385 obj = msm_framebuffer_bo(new_fb, 0);
386
387 mdp4_crtc->event = event;
388 update_fb(crtc, true, new_fb);
389
390 return msm_gem_queue_inactive_work(obj,
391 &mdp4_crtc->pageflip_work);
392}
393
394static int mdp4_crtc_set_property(struct drm_crtc *crtc,
395 struct drm_property *property, uint64_t val)
396{
397 // XXX
398 return -EINVAL;
399}
400
401#define CURSOR_WIDTH 64
402#define CURSOR_HEIGHT 64
403
404/* called from IRQ to update cursor related registers (if needed). The
405 * cursor registers, other than x/y position, appear not to be double
406 * buffered, and changing them other than from vblank seems to trigger
407 * underflow.
408 */
409static void update_cursor(struct drm_crtc *crtc)
410{
411 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
412 enum mdp4_dma dma = mdp4_crtc->dma;
413 unsigned long flags;
414
415 spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags);
416 if (mdp4_crtc->cursor.stale) {
417 struct mdp4_kms *mdp4_kms = get_kms(crtc);
418 struct drm_gem_object *next_bo = mdp4_crtc->cursor.next_bo;
419 struct drm_gem_object *prev_bo = mdp4_crtc->cursor.scanout_bo;
420 uint32_t iova = mdp4_crtc->cursor.next_iova;
421
422 if (next_bo) {
423 /* take a obj ref + iova ref when we start scanning out: */
424 drm_gem_object_reference(next_bo);
425 msm_gem_get_iova_locked(next_bo, mdp4_kms->id, &iova);
426
427 /* enable cursor: */
428 mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_SIZE(dma),
429 MDP4_DMA_CURSOR_SIZE_WIDTH(mdp4_crtc->cursor.width) |
430 MDP4_DMA_CURSOR_SIZE_HEIGHT(mdp4_crtc->cursor.height));
431 mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), iova);
432 mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BLEND_CONFIG(dma),
433 MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(CURSOR_ARGB) |
434 MDP4_DMA_CURSOR_BLEND_CONFIG_CURSOR_EN);
435 } else {
436 /* disable cursor: */
437 mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), 0);
438 mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BLEND_CONFIG(dma),
439 MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(CURSOR_ARGB));
440 }
441
442 /* and drop the iova ref + obj rev when done scanning out: */
443 if (prev_bo)
444 drm_flip_work_queue(&mdp4_crtc->unref_cursor_work, prev_bo);
445
446 mdp4_crtc->cursor.scanout_bo = next_bo;
447 mdp4_crtc->cursor.stale = false;
448 }
449 spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
450}
451
452static int mdp4_crtc_cursor_set(struct drm_crtc *crtc,
453 struct drm_file *file_priv, uint32_t handle,
454 uint32_t width, uint32_t height)
455{
456 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
457 struct mdp4_kms *mdp4_kms = get_kms(crtc);
458 struct drm_device *dev = crtc->dev;
459 struct drm_gem_object *cursor_bo, *old_bo;
460 unsigned long flags;
461 uint32_t iova;
462 int ret;
463
464 if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) {
465 dev_err(dev->dev, "bad cursor size: %dx%d\n", width, height);
466 return -EINVAL;
467 }
468
469 if (handle) {
470 cursor_bo = drm_gem_object_lookup(dev, file_priv, handle);
471 if (!cursor_bo)
472 return -ENOENT;
473 } else {
474 cursor_bo = NULL;
475 }
476
477 if (cursor_bo) {
478 ret = msm_gem_get_iova(cursor_bo, mdp4_kms->id, &iova);
479 if (ret)
480 goto fail;
481 } else {
482 iova = 0;
483 }
484
485 spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags);
486 old_bo = mdp4_crtc->cursor.next_bo;
487 mdp4_crtc->cursor.next_bo = cursor_bo;
488 mdp4_crtc->cursor.next_iova = iova;
489 mdp4_crtc->cursor.width = width;
490 mdp4_crtc->cursor.height = height;
491 mdp4_crtc->cursor.stale = true;
492 spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
493
494 if (old_bo) {
495 /* drop our previous reference: */
496 msm_gem_put_iova(old_bo, mdp4_kms->id);
497 drm_gem_object_unreference_unlocked(old_bo);
498 }
499
500 return 0;
501
502fail:
503 drm_gem_object_unreference_unlocked(cursor_bo);
504 return ret;
505}
506
507static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
508{
509 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
510 struct mdp4_kms *mdp4_kms = get_kms(crtc);
511 enum mdp4_dma dma = mdp4_crtc->dma;
512
513 mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_POS(dma),
514 MDP4_DMA_CURSOR_POS_X(x) |
515 MDP4_DMA_CURSOR_POS_Y(y));
516
517 return 0;
518}
519
520static const struct drm_crtc_funcs mdp4_crtc_funcs = {
521 .set_config = drm_crtc_helper_set_config,
522 .destroy = mdp4_crtc_destroy,
523 .page_flip = mdp4_crtc_page_flip,
524 .set_property = mdp4_crtc_set_property,
525 .cursor_set = mdp4_crtc_cursor_set,
526 .cursor_move = mdp4_crtc_cursor_move,
527};
528
529static const struct drm_crtc_helper_funcs mdp4_crtc_helper_funcs = {
530 .dpms = mdp4_crtc_dpms,
531 .mode_fixup = mdp4_crtc_mode_fixup,
532 .mode_set = mdp4_crtc_mode_set,
533 .prepare = mdp4_crtc_prepare,
534 .commit = mdp4_crtc_commit,
535 .mode_set_base = mdp4_crtc_mode_set_base,
536 .load_lut = mdp4_crtc_load_lut,
537};
538
539static void mdp4_crtc_vblank_irq(struct mdp4_irq *irq, uint32_t irqstatus)
540{
541 struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, vblank);
542 struct drm_crtc *crtc = &mdp4_crtc->base;
543 struct msm_drm_private *priv = crtc->dev->dev_private;
544
545 update_cursor(crtc);
546 complete_flip(crtc, false);
547 mdp4_irq_unregister(get_kms(crtc), &mdp4_crtc->vblank);
548
549 drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
550 drm_flip_work_commit(&mdp4_crtc->unref_cursor_work, priv->wq);
551}
552
553static void mdp4_crtc_err_irq(struct mdp4_irq *irq, uint32_t irqstatus)
554{
555 struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err);
556 struct drm_crtc *crtc = &mdp4_crtc->base;
557 DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
558 crtc_flush(crtc);
559}
560
561uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
562{
563 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
564 return mdp4_crtc->vblank.irqmask;
565}
566
567void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc)
568{
569 complete_flip(crtc, true);
570}
571
572/* set dma config, ie. the format the encoder wants. */
573void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config)
574{
575 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
576 struct mdp4_kms *mdp4_kms = get_kms(crtc);
577
578 mdp4_write(mdp4_kms, REG_MDP4_DMA_CONFIG(mdp4_crtc->dma), config);
579}
580
581/* set interface for routing crtc->encoder: */
582void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf)
583{
584 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
585 struct mdp4_kms *mdp4_kms = get_kms(crtc);
586 uint32_t intf_sel;
587
588 intf_sel = mdp4_read(mdp4_kms, REG_MDP4_DISP_INTF_SEL);
589
590 switch (mdp4_crtc->dma) {
591 case DMA_P:
592 intf_sel &= ~MDP4_DISP_INTF_SEL_PRIM__MASK;
593 intf_sel |= MDP4_DISP_INTF_SEL_PRIM(intf);
594 break;
595 case DMA_S:
596 intf_sel &= ~MDP4_DISP_INTF_SEL_SEC__MASK;
597 intf_sel |= MDP4_DISP_INTF_SEL_SEC(intf);
598 break;
599 case DMA_E:
600 intf_sel &= ~MDP4_DISP_INTF_SEL_EXT__MASK;
601 intf_sel |= MDP4_DISP_INTF_SEL_EXT(intf);
602 break;
603 }
604
605 if (intf == INTF_DSI_VIDEO) {
606 intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_CMD;
607 intf_sel |= MDP4_DISP_INTF_SEL_DSI_VIDEO;
608 mdp4_crtc->mixer = 0;
609 } else if (intf == INTF_DSI_CMD) {
610 intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_VIDEO;
611 intf_sel |= MDP4_DISP_INTF_SEL_DSI_CMD;
612 mdp4_crtc->mixer = 0;
613 } else if (intf == INTF_LCDC_DTV){
614 mdp4_crtc->mixer = 1;
615 }
616
617 blend_setup(crtc);
618
619 DBG("%s: intf_sel=%08x", mdp4_crtc->name, intf_sel);
620
621 mdp4_write(mdp4_kms, REG_MDP4_DISP_INTF_SEL, intf_sel);
622}
623
624static const char *dma_names[] = {
625 "DMA_P", "DMA_S", "DMA_E",
626};
627
628/* initialize crtc */
629struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
630 struct drm_plane *plane, int id, int ovlp_id,
631 enum mdp4_dma dma_id)
632{
633 struct drm_crtc *crtc = NULL;
634 struct mdp4_crtc *mdp4_crtc;
635 int ret;
636
637 mdp4_crtc = kzalloc(sizeof(*mdp4_crtc), GFP_KERNEL);
638 if (!mdp4_crtc) {
639 ret = -ENOMEM;
640 goto fail;
641 }
642
643 crtc = &mdp4_crtc->base;
644
645 mdp4_crtc->plane = plane;
646 mdp4_crtc->plane->crtc = crtc;
647
648 mdp4_crtc->ovlp = ovlp_id;
649 mdp4_crtc->dma = dma_id;
650
651 mdp4_crtc->vblank.irqmask = dma2irq(mdp4_crtc->dma);
652 mdp4_crtc->vblank.irq = mdp4_crtc_vblank_irq;
653
654 mdp4_crtc->err.irqmask = dma2err(mdp4_crtc->dma);
655 mdp4_crtc->err.irq = mdp4_crtc_err_irq;
656
657 snprintf(mdp4_crtc->name, sizeof(mdp4_crtc->name), "%s:%d",
658 dma_names[dma_id], ovlp_id);
659
660 spin_lock_init(&mdp4_crtc->cursor.lock);
661
662 ret = drm_flip_work_init(&mdp4_crtc->unref_fb_work, 16,
663 "unref fb", unref_fb_worker);
664 if (ret)
665 goto fail;
666
667 ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,
668 "unref cursor", unref_cursor_worker);
669
670 INIT_WORK(&mdp4_crtc->pageflip_work, pageflip_worker);
671
672 drm_crtc_init(dev, crtc, &mdp4_crtc_funcs);
673 drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
674
675 mdp4_plane_install_properties(mdp4_crtc->plane, &crtc->base);
676
677 return crtc;
678
679fail:
680 if (crtc)
681 mdp4_crtc_destroy(crtc);
682
683 return ERR_PTR(ret);
684}
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c b/drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c
new file mode 100644
index 000000000000..06d49e309d34
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c
@@ -0,0 +1,317 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <mach/clk.h>
19
20#include "mdp4_kms.h"
21#include "msm_connector.h"
22
23#include "drm_crtc.h"
24#include "drm_crtc_helper.h"
25
26
27struct mdp4_dtv_encoder {
28 struct drm_encoder base;
29 struct clk *src_clk;
30 struct clk *hdmi_clk;
31 struct clk *mdp_clk;
32 unsigned long int pixclock;
33 bool enabled;
34 uint32_t bsc;
35};
36#define to_mdp4_dtv_encoder(x) container_of(x, struct mdp4_dtv_encoder, base)
37
38static struct mdp4_kms *get_kms(struct drm_encoder *encoder)
39{
40 struct msm_drm_private *priv = encoder->dev->dev_private;
41 return to_mdp4_kms(priv->kms);
42}
43
44#ifdef CONFIG_MSM_BUS_SCALING
45#include <mach/board.h>
46/* not ironically named at all.. no, really.. */
47static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder)
48{
49 struct drm_device *dev = mdp4_dtv_encoder->base.dev;
50 struct lcdc_platform_data *dtv_pdata = mdp4_find_pdata("dtv.0");
51
52 if (!dtv_pdata) {
53 dev_err(dev->dev, "could not find dtv pdata\n");
54 return;
55 }
56
57 if (dtv_pdata->bus_scale_table) {
58 mdp4_dtv_encoder->bsc = msm_bus_scale_register_client(
59 dtv_pdata->bus_scale_table);
60 DBG("bus scale client: %08x", mdp4_dtv_encoder->bsc);
61 DBG("lcdc_power_save: %p", dtv_pdata->lcdc_power_save);
62 if (dtv_pdata->lcdc_power_save)
63 dtv_pdata->lcdc_power_save(1);
64 }
65}
66
67static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder)
68{
69 if (mdp4_dtv_encoder->bsc) {
70 msm_bus_scale_unregister_client(mdp4_dtv_encoder->bsc);
71 mdp4_dtv_encoder->bsc = 0;
72 }
73}
74
75static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx)
76{
77 if (mdp4_dtv_encoder->bsc) {
78 DBG("set bus scaling: %d", idx);
79 msm_bus_scale_client_update_request(mdp4_dtv_encoder->bsc, idx);
80 }
81}
82#else
83static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {}
84static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {}
85static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx) {}
86#endif
87
88static void mdp4_dtv_encoder_destroy(struct drm_encoder *encoder)
89{
90 struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
91 bs_fini(mdp4_dtv_encoder);
92 drm_encoder_cleanup(encoder);
93 kfree(mdp4_dtv_encoder);
94}
95
96static const struct drm_encoder_funcs mdp4_dtv_encoder_funcs = {
97 .destroy = mdp4_dtv_encoder_destroy,
98};
99
100static void mdp4_dtv_encoder_dpms(struct drm_encoder *encoder, int mode)
101{
102 struct drm_device *dev = encoder->dev;
103 struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
104 struct msm_connector *msm_connector = get_connector(encoder);
105 struct mdp4_kms *mdp4_kms = get_kms(encoder);
106 bool enabled = (mode == DRM_MODE_DPMS_ON);
107
108 DBG("mode=%d", mode);
109
110 if (enabled == mdp4_dtv_encoder->enabled)
111 return;
112
113 if (enabled) {
114 unsigned long pc = mdp4_dtv_encoder->pixclock;
115 int ret;
116
117 bs_set(mdp4_dtv_encoder, 1);
118
119 if (msm_connector)
120 msm_connector->funcs->dpms(msm_connector, mode);
121
122 DBG("setting src_clk=%lu", pc);
123
124 ret = clk_set_rate(mdp4_dtv_encoder->src_clk, pc);
125 if (ret)
126 dev_err(dev->dev, "failed to set src_clk to %lu: %d\n", pc, ret);
127 clk_prepare_enable(mdp4_dtv_encoder->src_clk);
128 ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk);
129 if (ret)
130 dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret);
131 ret = clk_prepare_enable(mdp4_dtv_encoder->mdp_clk);
132 if (ret)
133 dev_err(dev->dev, "failed to enabled mdp_clk: %d\n", ret);
134
135 mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 1);
136 } else {
137 mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);
138
139 /*
140 * Wait for a vsync so we know the ENABLE=0 latched before
141 * the (connector) source of the vsync's gets disabled,
142 * otherwise we end up in a funny state if we re-enable
143 * before the disable latches, which results that some of
144 * the settings changes for the new modeset (like new
145 * scanout buffer) don't latch properly..
146 */
147 mdp4_irq_wait(mdp4_kms, MDP4_IRQ_EXTERNAL_VSYNC);
148
149 clk_disable_unprepare(mdp4_dtv_encoder->src_clk);
150 clk_disable_unprepare(mdp4_dtv_encoder->hdmi_clk);
151 clk_disable_unprepare(mdp4_dtv_encoder->mdp_clk);
152
153 if (msm_connector)
154 msm_connector->funcs->dpms(msm_connector, mode);
155
156 bs_set(mdp4_dtv_encoder, 0);
157 }
158
159 mdp4_dtv_encoder->enabled = enabled;
160}
161
162static bool mdp4_dtv_encoder_mode_fixup(struct drm_encoder *encoder,
163 const struct drm_display_mode *mode,
164 struct drm_display_mode *adjusted_mode)
165{
166 return true;
167}
168
169static void mdp4_dtv_encoder_mode_set(struct drm_encoder *encoder,
170 struct drm_display_mode *mode,
171 struct drm_display_mode *adjusted_mode)
172{
173 struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
174 struct msm_connector *msm_connector = get_connector(encoder);
175 struct mdp4_kms *mdp4_kms = get_kms(encoder);
176 uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol;
177 uint32_t display_v_start, display_v_end;
178 uint32_t hsync_start_x, hsync_end_x;
179
180 mode = adjusted_mode;
181
182 DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
183 mode->base.id, mode->name,
184 mode->vrefresh, mode->clock,
185 mode->hdisplay, mode->hsync_start,
186 mode->hsync_end, mode->htotal,
187 mode->vdisplay, mode->vsync_start,
188 mode->vsync_end, mode->vtotal,
189 mode->type, mode->flags);
190
191 mdp4_dtv_encoder->pixclock = mode->clock * 1000;
192
193 DBG("pixclock=%lu", mdp4_dtv_encoder->pixclock);
194
195 ctrl_pol = 0;
196 if (mode->flags & DRM_MODE_FLAG_NHSYNC)
197 ctrl_pol |= MDP4_DTV_CTRL_POLARITY_HSYNC_LOW;
198 if (mode->flags & DRM_MODE_FLAG_NVSYNC)
199 ctrl_pol |= MDP4_DTV_CTRL_POLARITY_VSYNC_LOW;
200 /* probably need to get DATA_EN polarity from panel.. */
201
202 dtv_hsync_skew = 0; /* get this from panel? */
203
204 hsync_start_x = (mode->htotal - mode->hsync_start);
205 hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;
206
207 vsync_period = mode->vtotal * mode->htotal;
208 vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
209 display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew;
210 display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1;
211
212 mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_CTRL,
213 MDP4_DTV_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
214 MDP4_DTV_HSYNC_CTRL_PERIOD(mode->htotal));
215 mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_PERIOD, vsync_period);
216 mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_LEN, vsync_len);
217 mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_HCTRL,
218 MDP4_DTV_DISPLAY_HCTRL_START(hsync_start_x) |
219 MDP4_DTV_DISPLAY_HCTRL_END(hsync_end_x));
220 mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VSTART, display_v_start);
221 mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VEND, display_v_end);
222 mdp4_write(mdp4_kms, REG_MDP4_DTV_BORDER_CLR, 0);
223 mdp4_write(mdp4_kms, REG_MDP4_DTV_UNDERFLOW_CLR,
224 MDP4_DTV_UNDERFLOW_CLR_ENABLE_RECOVERY |
225 MDP4_DTV_UNDERFLOW_CLR_COLOR(0xff));
226 mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_SKEW, dtv_hsync_skew);
227 mdp4_write(mdp4_kms, REG_MDP4_DTV_CTRL_POLARITY, ctrl_pol);
228 mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_HCTL,
229 MDP4_DTV_ACTIVE_HCTL_START(0) |
230 MDP4_DTV_ACTIVE_HCTL_END(0));
231 mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VSTART, 0);
232 mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VEND, 0);
233
234 if (msm_connector)
235 msm_connector->funcs->mode_set(msm_connector, mode);
236}
237
238static void mdp4_dtv_encoder_prepare(struct drm_encoder *encoder)
239{
240 mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
241}
242
243static void mdp4_dtv_encoder_commit(struct drm_encoder *encoder)
244{
245 mdp4_crtc_set_config(encoder->crtc,
246 MDP4_DMA_CONFIG_R_BPC(BPC8) |
247 MDP4_DMA_CONFIG_G_BPC(BPC8) |
248 MDP4_DMA_CONFIG_B_BPC(BPC8) |
249 MDP4_DMA_CONFIG_PACK(0x21));
250 mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV);
251 mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
252}
253
254static const struct drm_encoder_helper_funcs mdp4_dtv_encoder_helper_funcs = {
255 .dpms = mdp4_dtv_encoder_dpms,
256 .mode_fixup = mdp4_dtv_encoder_mode_fixup,
257 .mode_set = mdp4_dtv_encoder_mode_set,
258 .prepare = mdp4_dtv_encoder_prepare,
259 .commit = mdp4_dtv_encoder_commit,
260};
261
262long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate)
263{
264 struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
265 return clk_round_rate(mdp4_dtv_encoder->src_clk, rate);
266}
267
268/* initialize encoder */
269struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev)
270{
271 struct drm_encoder *encoder = NULL;
272 struct mdp4_dtv_encoder *mdp4_dtv_encoder;
273 int ret;
274
275 mdp4_dtv_encoder = kzalloc(sizeof(*mdp4_dtv_encoder), GFP_KERNEL);
276 if (!mdp4_dtv_encoder) {
277 ret = -ENOMEM;
278 goto fail;
279 }
280
281 encoder = &mdp4_dtv_encoder->base;
282
283 drm_encoder_init(dev, encoder, &mdp4_dtv_encoder_funcs,
284 DRM_MODE_ENCODER_TMDS);
285 drm_encoder_helper_add(encoder, &mdp4_dtv_encoder_helper_funcs);
286
287 mdp4_dtv_encoder->src_clk = devm_clk_get(dev->dev, "src_clk");
288 if (IS_ERR(mdp4_dtv_encoder->src_clk)) {
289 dev_err(dev->dev, "failed to get src_clk\n");
290 ret = PTR_ERR(mdp4_dtv_encoder->src_clk);
291 goto fail;
292 }
293
294 mdp4_dtv_encoder->hdmi_clk = devm_clk_get(dev->dev, "hdmi_clk");
295 if (IS_ERR(mdp4_dtv_encoder->hdmi_clk)) {
296 dev_err(dev->dev, "failed to get hdmi_clk\n");
297 ret = PTR_ERR(mdp4_dtv_encoder->hdmi_clk);
298 goto fail;
299 }
300
301 mdp4_dtv_encoder->mdp_clk = devm_clk_get(dev->dev, "mdp_clk");
302 if (IS_ERR(mdp4_dtv_encoder->mdp_clk)) {
303 dev_err(dev->dev, "failed to get mdp_clk\n");
304 ret = PTR_ERR(mdp4_dtv_encoder->mdp_clk);
305 goto fail;
306 }
307
308 bs_init(mdp4_dtv_encoder);
309
310 return encoder;
311
312fail:
313 if (encoder)
314 mdp4_dtv_encoder_destroy(encoder);
315
316 return ERR_PTR(ret);
317}
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_format.c b/drivers/gpu/drm/msm/mdp4/mdp4_format.c
new file mode 100644
index 000000000000..7b645f2e837a
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_format.c
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19#include "msm_drv.h"
20#include "mdp4_kms.h"
21
22#define FMT(name, a, r, g, b, e0, e1, e2, e3, alpha, tight, c, cnt) { \
23 .base = { .pixel_format = DRM_FORMAT_ ## name }, \
24 .bpc_a = BPC ## a ## A, \
25 .bpc_r = BPC ## r, \
26 .bpc_g = BPC ## g, \
27 .bpc_b = BPC ## b, \
28 .unpack = { e0, e1, e2, e3 }, \
29 .alpha_enable = alpha, \
30 .unpack_tight = tight, \
31 .cpp = c, \
32 .unpack_count = cnt, \
33 }
34
35#define BPC0A 0
36
37static const struct mdp4_format formats[] = {
38 /* name a r g b e0 e1 e2 e3 alpha tight cpp cnt */
39 FMT(ARGB8888, 8, 8, 8, 8, 1, 0, 2, 3, true, true, 4, 4),
40 FMT(XRGB8888, 8, 8, 8, 8, 1, 0, 2, 3, false, true, 4, 4),
41 FMT(RGB888, 0, 8, 8, 8, 1, 0, 2, 0, false, true, 3, 3),
42 FMT(BGR888, 0, 8, 8, 8, 2, 0, 1, 0, false, true, 3, 3),
43 FMT(RGB565, 0, 5, 6, 5, 1, 0, 2, 0, false, true, 2, 3),
44 FMT(BGR565, 0, 5, 6, 5, 2, 0, 1, 0, false, true, 2, 3),
45};
46
47const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format)
48{
49 int i;
50 for (i = 0; i < ARRAY_SIZE(formats); i++) {
51 const struct mdp4_format *f = &formats[i];
52 if (f->base.pixel_format == format)
53 return &f->base;
54 }
55 return NULL;
56}
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_irq.c b/drivers/gpu/drm/msm/mdp4/mdp4_irq.c
new file mode 100644
index 000000000000..5c6b7fca4edd
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_irq.c
@@ -0,0 +1,203 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19#include "msm_drv.h"
20#include "mdp4_kms.h"
21
22
23struct mdp4_irq_wait {
24 struct mdp4_irq irq;
25 int count;
26};
27
28static DECLARE_WAIT_QUEUE_HEAD(wait_event);
29
30static DEFINE_SPINLOCK(list_lock);
31
32static void update_irq(struct mdp4_kms *mdp4_kms)
33{
34 struct mdp4_irq *irq;
35 uint32_t irqmask = mdp4_kms->vblank_mask;
36
37 BUG_ON(!spin_is_locked(&list_lock));
38
39 list_for_each_entry(irq, &mdp4_kms->irq_list, node)
40 irqmask |= irq->irqmask;
41
42 mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, irqmask);
43}
44
45static void update_irq_unlocked(struct mdp4_kms *mdp4_kms)
46{
47 unsigned long flags;
48 spin_lock_irqsave(&list_lock, flags);
49 update_irq(mdp4_kms);
50 spin_unlock_irqrestore(&list_lock, flags);
51}
52
53static void mdp4_irq_error_handler(struct mdp4_irq *irq, uint32_t irqstatus)
54{
55 DRM_ERROR("errors: %08x\n", irqstatus);
56}
57
58void mdp4_irq_preinstall(struct msm_kms *kms)
59{
60 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
61 mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff);
62}
63
64int mdp4_irq_postinstall(struct msm_kms *kms)
65{
66 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
67 struct mdp4_irq *error_handler = &mdp4_kms->error_handler;
68
69 INIT_LIST_HEAD(&mdp4_kms->irq_list);
70
71 error_handler->irq = mdp4_irq_error_handler;
72 error_handler->irqmask = MDP4_IRQ_PRIMARY_INTF_UDERRUN |
73 MDP4_IRQ_EXTERNAL_INTF_UDERRUN;
74
75 mdp4_irq_register(mdp4_kms, error_handler);
76
77 return 0;
78}
79
80void mdp4_irq_uninstall(struct msm_kms *kms)
81{
82 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
83 mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000);
84}
85
86irqreturn_t mdp4_irq(struct msm_kms *kms)
87{
88 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
89 struct drm_device *dev = mdp4_kms->dev;
90 struct msm_drm_private *priv = dev->dev_private;
91 struct mdp4_irq *handler, *n;
92 unsigned long flags;
93 unsigned int id;
94 uint32_t status;
95
96 status = mdp4_read(mdp4_kms, REG_MDP4_INTR_STATUS);
97 mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, status);
98
99 VERB("status=%08x", status);
100
101 for (id = 0; id < priv->num_crtcs; id++)
102 if (status & mdp4_crtc_vblank(priv->crtcs[id]))
103 drm_handle_vblank(dev, id);
104
105 spin_lock_irqsave(&list_lock, flags);
106 mdp4_kms->in_irq = true;
107 list_for_each_entry_safe(handler, n, &mdp4_kms->irq_list, node) {
108 if (handler->irqmask & status) {
109 spin_unlock_irqrestore(&list_lock, flags);
110 handler->irq(handler, handler->irqmask & status);
111 spin_lock_irqsave(&list_lock, flags);
112 }
113 }
114 mdp4_kms->in_irq = false;
115 update_irq(mdp4_kms);
116 spin_unlock_irqrestore(&list_lock, flags);
117
118 return IRQ_HANDLED;
119}
120
121int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
122{
123 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
124 unsigned long flags;
125
126 spin_lock_irqsave(&list_lock, flags);
127 mdp4_kms->vblank_mask |= mdp4_crtc_vblank(crtc);
128 update_irq(mdp4_kms);
129 spin_unlock_irqrestore(&list_lock, flags);
130
131 return 0;
132}
133
134void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
135{
136 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
137 unsigned long flags;
138
139 spin_lock_irqsave(&list_lock, flags);
140 mdp4_kms->vblank_mask &= ~mdp4_crtc_vblank(crtc);
141 update_irq(mdp4_kms);
142 spin_unlock_irqrestore(&list_lock, flags);
143}
144
145static void wait_irq(struct mdp4_irq *irq, uint32_t irqstatus)
146{
147 struct mdp4_irq_wait *wait =
148 container_of(irq, struct mdp4_irq_wait, irq);
149 wait->count--;
150 wake_up_all(&wait_event);
151}
152
153void mdp4_irq_wait(struct mdp4_kms *mdp4_kms, uint32_t irqmask)
154{
155 struct mdp4_irq_wait wait = {
156 .irq = {
157 .irq = wait_irq,
158 .irqmask = irqmask,
159 },
160 .count = 1,
161 };
162 mdp4_irq_register(mdp4_kms, &wait.irq);
163 wait_event(wait_event, (wait.count <= 0));
164 mdp4_irq_unregister(mdp4_kms, &wait.irq);
165}
166
167void mdp4_irq_register(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq)
168{
169 unsigned long flags;
170 bool needs_update = false;
171
172 spin_lock_irqsave(&list_lock, flags);
173
174 if (!irq->registered) {
175 irq->registered = true;
176 list_add(&irq->node, &mdp4_kms->irq_list);
177 needs_update = !mdp4_kms->in_irq;
178 }
179
180 spin_unlock_irqrestore(&list_lock, flags);
181
182 if (needs_update)
183 update_irq_unlocked(mdp4_kms);
184}
185
186void mdp4_irq_unregister(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq)
187{
188 unsigned long flags;
189 bool needs_update = false;
190
191 spin_lock_irqsave(&list_lock, flags);
192
193 if (irq->registered) {
194 irq->registered = false;
195 list_del(&irq->node);
196 needs_update = !mdp4_kms->in_irq;
197 }
198
199 spin_unlock_irqrestore(&list_lock, flags);
200
201 if (needs_update)
202 update_irq_unlocked(mdp4_kms);
203}
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
new file mode 100644
index 000000000000..960cd894da78
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
@@ -0,0 +1,368 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19#include "msm_drv.h"
20#include "mdp4_kms.h"
21
22#include <mach/iommu.h>
23
24static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev);
25
26static int mdp4_hw_init(struct msm_kms *kms)
27{
28 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
29 struct drm_device *dev = mdp4_kms->dev;
30 uint32_t version, major, minor, dmap_cfg, vg_cfg;
31 unsigned long clk;
32 int ret = 0;
33
34 pm_runtime_get_sync(dev->dev);
35
36 version = mdp4_read(mdp4_kms, REG_MDP4_VERSION);
37
38 major = FIELD(version, MDP4_VERSION_MAJOR);
39 minor = FIELD(version, MDP4_VERSION_MINOR);
40
41 DBG("found MDP version v%d.%d", major, minor);
42
43 if (major != 4) {
44 dev_err(dev->dev, "unexpected MDP version: v%d.%d\n",
45 major, minor);
46 ret = -ENXIO;
47 goto out;
48 }
49
50 mdp4_kms->rev = minor;
51
52 if (mdp4_kms->dsi_pll_vdda) {
53 if ((mdp4_kms->rev == 2) || (mdp4_kms->rev == 4)) {
54 ret = regulator_set_voltage(mdp4_kms->dsi_pll_vdda,
55 1200000, 1200000);
56 if (ret) {
57 dev_err(dev->dev,
58 "failed to set dsi_pll_vdda voltage: %d\n", ret);
59 goto out;
60 }
61 }
62 }
63
64 if (mdp4_kms->dsi_pll_vddio) {
65 if (mdp4_kms->rev == 2) {
66 ret = regulator_set_voltage(mdp4_kms->dsi_pll_vddio,
67 1800000, 1800000);
68 if (ret) {
69 dev_err(dev->dev,
70 "failed to set dsi_pll_vddio voltage: %d\n", ret);
71 goto out;
72 }
73 }
74 }
75
76 if (mdp4_kms->rev > 1) {
77 mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER0, 0x0707ffff);
78 mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER1, 0x03073f3f);
79 }
80
81 mdp4_write(mdp4_kms, REG_MDP4_PORTMAP_MODE, 0x3);
82
83 /* max read pending cmd config, 3 pending requests: */
84 mdp4_write(mdp4_kms, REG_MDP4_READ_CNFG, 0x02222);
85
86 clk = clk_get_rate(mdp4_kms->clk);
87
88 if ((mdp4_kms->rev >= 1) || (clk >= 90000000)) {
89 dmap_cfg = 0x47; /* 16 bytes-burst x 8 req */
90 vg_cfg = 0x47; /* 16 bytes-burs x 8 req */
91 } else {
92 dmap_cfg = 0x27; /* 8 bytes-burst x 8 req */
93 vg_cfg = 0x43; /* 16 bytes-burst x 4 req */
94 }
95
96 DBG("fetch config: dmap=%02x, vg=%02x", dmap_cfg, vg_cfg);
97
98 mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_P), dmap_cfg);
99 mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_E), dmap_cfg);
100
101 mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG1), vg_cfg);
102 mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG2), vg_cfg);
103 mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB1), vg_cfg);
104 mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB2), vg_cfg);
105
106 if (mdp4_kms->rev >= 2)
107 mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1);
108
109 /* disable CSC matrix / YUV by default: */
110 mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0);
111 mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG2), 0);
112 mdp4_write(mdp4_kms, REG_MDP4_DMA_P_OP_MODE, 0);
113 mdp4_write(mdp4_kms, REG_MDP4_DMA_S_OP_MODE, 0);
114 mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(1), 0);
115 mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(2), 0);
116
117 if (mdp4_kms->rev > 1)
118 mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1);
119
120out:
121 pm_runtime_put_sync(dev->dev);
122
123 return ret;
124}
125
126static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
127 struct drm_encoder *encoder)
128{
129 /* if we had >1 encoder, we'd need something more clever: */
130 return mdp4_dtv_round_pixclk(encoder, rate);
131}
132
133static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
134{
135 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
136 struct msm_drm_private *priv = mdp4_kms->dev->dev_private;
137 unsigned i;
138
139 for (i = 0; i < priv->num_crtcs; i++)
140 mdp4_crtc_cancel_pending_flip(priv->crtcs[i]);
141}
142
143static void mdp4_destroy(struct msm_kms *kms)
144{
145 struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
146 kfree(mdp4_kms);
147}
148
149static const struct msm_kms_funcs kms_funcs = {
150 .hw_init = mdp4_hw_init,
151 .irq_preinstall = mdp4_irq_preinstall,
152 .irq_postinstall = mdp4_irq_postinstall,
153 .irq_uninstall = mdp4_irq_uninstall,
154 .irq = mdp4_irq,
155 .enable_vblank = mdp4_enable_vblank,
156 .disable_vblank = mdp4_disable_vblank,
157 .get_format = mdp4_get_format,
158 .round_pixclk = mdp4_round_pixclk,
159 .preclose = mdp4_preclose,
160 .destroy = mdp4_destroy,
161};
162
163int mdp4_disable(struct mdp4_kms *mdp4_kms)
164{
165 DBG("");
166
167 clk_disable_unprepare(mdp4_kms->clk);
168 if (mdp4_kms->pclk)
169 clk_disable_unprepare(mdp4_kms->pclk);
170 clk_disable_unprepare(mdp4_kms->lut_clk);
171
172 return 0;
173}
174
175int mdp4_enable(struct mdp4_kms *mdp4_kms)
176{
177 DBG("");
178
179 clk_prepare_enable(mdp4_kms->clk);
180 if (mdp4_kms->pclk)
181 clk_prepare_enable(mdp4_kms->pclk);
182 clk_prepare_enable(mdp4_kms->lut_clk);
183
184 return 0;
185}
186
187static int modeset_init(struct mdp4_kms *mdp4_kms)
188{
189 struct drm_device *dev = mdp4_kms->dev;
190 struct msm_drm_private *priv = dev->dev_private;
191 struct drm_plane *plane;
192 struct drm_crtc *crtc;
193 struct drm_encoder *encoder;
194 struct drm_connector *connector;
195 int ret;
196
197 /*
198 * NOTE: this is a bit simplistic until we add support
199 * for more than just RGB1->DMA_E->DTV->HDMI
200 */
201
202 /* the CRTCs get constructed with a private plane: */
203 plane = mdp4_plane_init(dev, RGB1, true);
204 if (IS_ERR(plane)) {
205 dev_err(dev->dev, "failed to construct plane for RGB1\n");
206 ret = PTR_ERR(plane);
207 goto fail;
208 }
209
210 crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, 1, DMA_E);
211 if (IS_ERR(crtc)) {
212 dev_err(dev->dev, "failed to construct crtc for DMA_E\n");
213 ret = PTR_ERR(crtc);
214 goto fail;
215 }
216 priv->crtcs[priv->num_crtcs++] = crtc;
217
218 encoder = mdp4_dtv_encoder_init(dev);
219 if (IS_ERR(encoder)) {
220 dev_err(dev->dev, "failed to construct DTV encoder\n");
221 ret = PTR_ERR(encoder);
222 goto fail;
223 }
224 encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */
225 priv->encoders[priv->num_encoders++] = encoder;
226
227 connector = hdmi_connector_init(dev, encoder);
228 if (IS_ERR(connector)) {
229 dev_err(dev->dev, "failed to construct HDMI connector\n");
230 ret = PTR_ERR(connector);
231 goto fail;
232 }
233 priv->connectors[priv->num_connectors++] = connector;
234
235 return 0;
236
237fail:
238 return ret;
239}
240
241static const char *iommu_ports[] = {
242 "mdp_port0_cb0", "mdp_port1_cb0",
243};
244
245struct msm_kms *mdp4_kms_init(struct drm_device *dev)
246{
247 struct platform_device *pdev = dev->platformdev;
248 struct mdp4_platform_config *config = mdp4_get_config(pdev);
249 struct mdp4_kms *mdp4_kms;
250 struct msm_kms *kms = NULL;
251 int ret;
252
253 mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL);
254 if (!mdp4_kms) {
255 dev_err(dev->dev, "failed to allocate kms\n");
256 ret = -ENOMEM;
257 goto fail;
258 }
259
260 kms = &mdp4_kms->base;
261 kms->funcs = &kms_funcs;
262
263 mdp4_kms->dev = dev;
264
265 mdp4_kms->mmio = msm_ioremap(pdev, NULL, "MDP4");
266 if (IS_ERR(mdp4_kms->mmio)) {
267 ret = PTR_ERR(mdp4_kms->mmio);
268 goto fail;
269 }
270
271 mdp4_kms->dsi_pll_vdda = devm_regulator_get(&pdev->dev, "dsi_pll_vdda");
272 if (IS_ERR(mdp4_kms->dsi_pll_vdda))
273 mdp4_kms->dsi_pll_vdda = NULL;
274
275 mdp4_kms->dsi_pll_vddio = devm_regulator_get(&pdev->dev, "dsi_pll_vddio");
276 if (IS_ERR(mdp4_kms->dsi_pll_vddio))
277 mdp4_kms->dsi_pll_vddio = NULL;
278
279 mdp4_kms->vdd = devm_regulator_get(&pdev->dev, "vdd");
280 if (IS_ERR(mdp4_kms->vdd))
281 mdp4_kms->vdd = NULL;
282
283 if (mdp4_kms->vdd) {
284 ret = regulator_enable(mdp4_kms->vdd);
285 if (ret) {
286 dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret);
287 goto fail;
288 }
289 }
290
291 mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk");
292 if (IS_ERR(mdp4_kms->clk)) {
293 dev_err(dev->dev, "failed to get core_clk\n");
294 ret = PTR_ERR(mdp4_kms->clk);
295 goto fail;
296 }
297
298 mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk");
299 if (IS_ERR(mdp4_kms->pclk))
300 mdp4_kms->pclk = NULL;
301
302 // XXX if (rev >= MDP_REV_42) { ???
303 mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk");
304 if (IS_ERR(mdp4_kms->lut_clk)) {
305 dev_err(dev->dev, "failed to get lut_clk\n");
306 ret = PTR_ERR(mdp4_kms->lut_clk);
307 goto fail;
308 }
309
310 clk_set_rate(mdp4_kms->clk, config->max_clk);
311 clk_set_rate(mdp4_kms->lut_clk, config->max_clk);
312
313 if (!config->iommu) {
314 dev_err(dev->dev, "no iommu\n");
315 ret = -ENXIO;
316 goto fail;
317 }
318
319 /* make sure things are off before attaching iommu (bootloader could
320 * have left things on, in which case we'll start getting faults if
321 * we don't disable):
322 */
323 mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);
324 mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
325 mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0);
326 mdelay(16);
327
328 ret = msm_iommu_attach(dev, config->iommu,
329 iommu_ports, ARRAY_SIZE(iommu_ports));
330 if (ret)
331 goto fail;
332
333 mdp4_kms->id = msm_register_iommu(dev, config->iommu);
334 if (mdp4_kms->id < 0) {
335 ret = mdp4_kms->id;
336 dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret);
337 goto fail;
338 }
339
340 ret = modeset_init(mdp4_kms);
341 if (ret) {
342 dev_err(dev->dev, "modeset_init failed: %d\n", ret);
343 goto fail;
344 }
345
346 return kms;
347
348fail:
349 if (kms)
350 mdp4_destroy(kms);
351 return ERR_PTR(ret);
352}
353
354static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev)
355{
356 static struct mdp4_platform_config config = {};
357#ifdef CONFIG_OF
358 /* TODO */
359#else
360 if (cpu_is_apq8064())
361 config.max_clk = 266667000;
362 else
363 config.max_clk = 200000000;
364
365 config.iommu = msm_get_iommu_domain(DISPLAY_READ_DOMAIN);
366#endif
367 return &config;
368}
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp4/mdp4_kms.h
new file mode 100644
index 000000000000..1e83554955f3
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_kms.h
@@ -0,0 +1,194 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __MDP4_KMS_H__
19#define __MDP4_KMS_H__
20
21#include <linux/clk.h>
22#include <linux/platform_device.h>
23#include <linux/regulator/consumer.h>
24
25#include "msm_drv.h"
26#include "mdp4.xml.h"
27
28
29/* For transiently registering for different MDP4 irqs that various parts
30 * of the KMS code need during setup/configuration. We these are not
31 * necessarily the same as what drm_vblank_get/put() are requesting, and
32 * the hysteresis in drm_vblank_put() is not necessarily desirable for
33 * internal housekeeping related irq usage.
34 */
35struct mdp4_irq {
36 struct list_head node;
37 uint32_t irqmask;
38 bool registered;
39 void (*irq)(struct mdp4_irq *irq, uint32_t irqstatus);
40};
41
42struct mdp4_kms {
43 struct msm_kms base;
44
45 struct drm_device *dev;
46
47 int rev;
48
49 /* mapper-id used to request GEM buffer mapped for scanout: */
50 int id;
51
52 void __iomem *mmio;
53
54 struct regulator *dsi_pll_vdda;
55 struct regulator *dsi_pll_vddio;
56 struct regulator *vdd;
57
58 struct clk *clk;
59 struct clk *pclk;
60 struct clk *lut_clk;
61
62 /* irq handling: */
63 bool in_irq;
64 struct list_head irq_list; /* list of mdp4_irq */
65 uint32_t vblank_mask; /* irq bits set for userspace vblank */
66 struct mdp4_irq error_handler;
67};
68#define to_mdp4_kms(x) container_of(x, struct mdp4_kms, base)
69
70/* platform config data (ie. from DT, or pdata) */
71struct mdp4_platform_config {
72 struct iommu_domain *iommu;
73 uint32_t max_clk;
74};
75
76struct mdp4_format {
77 struct msm_format base;
78 enum mpd4_bpc bpc_r, bpc_g, bpc_b;
79 enum mpd4_bpc_alpha bpc_a;
80 uint8_t unpack[4];
81 bool alpha_enable, unpack_tight;
82 uint8_t cpp, unpack_count;
83};
84#define to_mdp4_format(x) container_of(x, struct mdp4_format, base)
85
86static inline void mdp4_write(struct mdp4_kms *mdp4_kms, u32 reg, u32 data)
87{
88 msm_writel(data, mdp4_kms->mmio + reg);
89}
90
91static inline u32 mdp4_read(struct mdp4_kms *mdp4_kms, u32 reg)
92{
93 return msm_readl(mdp4_kms->mmio + reg);
94}
95
96static inline uint32_t pipe2flush(enum mpd4_pipe pipe)
97{
98 switch (pipe) {
99 case VG1: return MDP4_OVERLAY_FLUSH_VG1;
100 case VG2: return MDP4_OVERLAY_FLUSH_VG2;
101 case RGB1: return MDP4_OVERLAY_FLUSH_RGB1;
102 case RGB2: return MDP4_OVERLAY_FLUSH_RGB1;
103 default: return 0;
104 }
105}
106
107static inline uint32_t ovlp2flush(int ovlp)
108{
109 switch (ovlp) {
110 case 0: return MDP4_OVERLAY_FLUSH_OVLP0;
111 case 1: return MDP4_OVERLAY_FLUSH_OVLP1;
112 default: return 0;
113 }
114}
115
116static inline uint32_t dma2irq(enum mdp4_dma dma)
117{
118 switch (dma) {
119 case DMA_P: return MDP4_IRQ_DMA_P_DONE;
120 case DMA_S: return MDP4_IRQ_DMA_S_DONE;
121 case DMA_E: return MDP4_IRQ_DMA_E_DONE;
122 default: return 0;
123 }
124}
125
126static inline uint32_t dma2err(enum mdp4_dma dma)
127{
128 switch (dma) {
129 case DMA_P: return MDP4_IRQ_PRIMARY_INTF_UDERRUN;
130 case DMA_S: return 0; // ???
131 case DMA_E: return MDP4_IRQ_EXTERNAL_INTF_UDERRUN;
132 default: return 0;
133 }
134}
135
136int mdp4_disable(struct mdp4_kms *mdp4_kms);
137int mdp4_enable(struct mdp4_kms *mdp4_kms);
138
139void mdp4_irq_preinstall(struct msm_kms *kms);
140int mdp4_irq_postinstall(struct msm_kms *kms);
141void mdp4_irq_uninstall(struct msm_kms *kms);
142irqreturn_t mdp4_irq(struct msm_kms *kms);
143void mdp4_irq_wait(struct mdp4_kms *mdp4_kms, uint32_t irqmask);
144void mdp4_irq_register(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq);
145void mdp4_irq_unregister(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq);
146int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
147void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
148
149const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format);
150
151void mdp4_plane_install_properties(struct drm_plane *plane,
152 struct drm_mode_object *obj);
153void mdp4_plane_set_scanout(struct drm_plane *plane,
154 struct drm_framebuffer *fb);
155int mdp4_plane_mode_set(struct drm_plane *plane,
156 struct drm_crtc *crtc, struct drm_framebuffer *fb,
157 int crtc_x, int crtc_y,
158 unsigned int crtc_w, unsigned int crtc_h,
159 uint32_t src_x, uint32_t src_y,
160 uint32_t src_w, uint32_t src_h);
161enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane);
162struct drm_plane *mdp4_plane_init(struct drm_device *dev,
163 enum mpd4_pipe pipe_id, bool private_plane);
164
165uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
166void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc);
167void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
168void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf);
169struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
170 struct drm_plane *plane, int id, int ovlp_id,
171 enum mdp4_dma dma_id);
172
173long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
174struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev);
175
176#ifdef CONFIG_MSM_BUS_SCALING
177static inline int match_dev_name(struct device *dev, void *data)
178{
179 return !strcmp(dev_name(dev), data);
180}
181/* bus scaling data is associated with extra pointless platform devices,
182 * "dtv", etc.. this is a bit of a hack, but we need a way for encoders
183 * to find their pdata to make the bus-scaling stuff work.
184 */
185static inline void *mdp4_find_pdata(const char *devname)
186{
187 struct device *dev;
188 dev = bus_find_device(&platform_bus_type, NULL,
189 (void *)devname, match_dev_name);
190 return dev ? dev->platform_data : NULL;
191}
192#endif
193
194#endif /* __MDP4_KMS_H__ */
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
new file mode 100644
index 000000000000..3468229d58b3
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
@@ -0,0 +1,243 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "mdp4_kms.h"
19
20
21struct mdp4_plane {
22 struct drm_plane base;
23 const char *name;
24
25 enum mpd4_pipe pipe;
26
27 uint32_t nformats;
28 uint32_t formats[32];
29
30 bool enabled;
31};
32#define to_mdp4_plane(x) container_of(x, struct mdp4_plane, base)
33
34static struct mdp4_kms *get_kms(struct drm_plane *plane)
35{
36 struct msm_drm_private *priv = plane->dev->dev_private;
37 return to_mdp4_kms(priv->kms);
38}
39
40static int mdp4_plane_update(struct drm_plane *plane,
41 struct drm_crtc *crtc, struct drm_framebuffer *fb,
42 int crtc_x, int crtc_y,
43 unsigned int crtc_w, unsigned int crtc_h,
44 uint32_t src_x, uint32_t src_y,
45 uint32_t src_w, uint32_t src_h)
46{
47 struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
48
49 mdp4_plane->enabled = true;
50
51 if (plane->fb)
52 drm_framebuffer_unreference(plane->fb);
53
54 drm_framebuffer_reference(fb);
55
56 return mdp4_plane_mode_set(plane, crtc, fb,
57 crtc_x, crtc_y, crtc_w, crtc_h,
58 src_x, src_y, src_w, src_h);
59}
60
61static int mdp4_plane_disable(struct drm_plane *plane)
62{
63 struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
64 DBG("%s: TODO", mdp4_plane->name); // XXX
65 return 0;
66}
67
68static void mdp4_plane_destroy(struct drm_plane *plane)
69{
70 struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
71
72 mdp4_plane_disable(plane);
73 drm_plane_cleanup(plane);
74
75 kfree(mdp4_plane);
76}
77
78/* helper to install properties which are common to planes and crtcs */
79void mdp4_plane_install_properties(struct drm_plane *plane,
80 struct drm_mode_object *obj)
81{
82 // XXX
83}
84
85int mdp4_plane_set_property(struct drm_plane *plane,
86 struct drm_property *property, uint64_t val)
87{
88 // XXX
89 return -EINVAL;
90}
91
92static const struct drm_plane_funcs mdp4_plane_funcs = {
93 .update_plane = mdp4_plane_update,
94 .disable_plane = mdp4_plane_disable,
95 .destroy = mdp4_plane_destroy,
96 .set_property = mdp4_plane_set_property,
97};
98
99void mdp4_plane_set_scanout(struct drm_plane *plane,
100 struct drm_framebuffer *fb)
101{
102 struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
103 struct mdp4_kms *mdp4_kms = get_kms(plane);
104 enum mpd4_pipe pipe = mdp4_plane->pipe;
105 uint32_t iova;
106
107 mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe),
108 MDP4_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
109 MDP4_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));
110
111 mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_B(pipe),
112 MDP4_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
113 MDP4_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));
114
115 msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova);
116 mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova);
117
118 plane->fb = fb;
119}
120
121#define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000
122
123int mdp4_plane_mode_set(struct drm_plane *plane,
124 struct drm_crtc *crtc, struct drm_framebuffer *fb,
125 int crtc_x, int crtc_y,
126 unsigned int crtc_w, unsigned int crtc_h,
127 uint32_t src_x, uint32_t src_y,
128 uint32_t src_w, uint32_t src_h)
129{
130 struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
131 struct mdp4_kms *mdp4_kms = get_kms(plane);
132 enum mpd4_pipe pipe = mdp4_plane->pipe;
133 const struct mdp4_format *format;
134 uint32_t op_mode = 0;
135 uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
136 uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;
137
138 /* src values are in Q16 fixed point, convert to integer: */
139 src_x = src_x >> 16;
140 src_y = src_y >> 16;
141 src_w = src_w >> 16;
142 src_h = src_h >> 16;
143
144 if (src_w != crtc_w) {
145 op_mode |= MDP4_PIPE_OP_MODE_SCALEX_EN;
146 /* TODO calc phasex_step */
147 }
148
149 if (src_h != crtc_h) {
150 op_mode |= MDP4_PIPE_OP_MODE_SCALEY_EN;
151 /* TODO calc phasey_step */
152 }
153
154 mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_SIZE(pipe),
155 MDP4_PIPE_SRC_SIZE_WIDTH(src_w) |
156 MDP4_PIPE_SRC_SIZE_HEIGHT(src_h));
157
158 mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_XY(pipe),
159 MDP4_PIPE_SRC_XY_X(src_x) |
160 MDP4_PIPE_SRC_XY_Y(src_y));
161
162 mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_SIZE(pipe),
163 MDP4_PIPE_DST_SIZE_WIDTH(crtc_w) |
164 MDP4_PIPE_DST_SIZE_HEIGHT(crtc_h));
165
166 mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_XY(pipe),
167 MDP4_PIPE_SRC_XY_X(crtc_x) |
168 MDP4_PIPE_SRC_XY_Y(crtc_y));
169
170 mdp4_plane_set_scanout(plane, fb);
171
172 format = to_mdp4_format(msm_framebuffer_format(fb));
173
174 mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_FORMAT(pipe),
175 MDP4_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |
176 MDP4_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) |
177 MDP4_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) |
178 MDP4_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) |
179 COND(format->alpha_enable, MDP4_PIPE_SRC_FORMAT_ALPHA_ENABLE) |
180 MDP4_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
181 MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
182 COND(format->unpack_tight, MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT));
183
184 mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_UNPACK(pipe),
185 MDP4_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) |
186 MDP4_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) |
187 MDP4_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) |
188 MDP4_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));
189
190 mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(pipe), op_mode);
191 mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step);
192 mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step);
193
194 plane->crtc = crtc;
195
196 return 0;
197}
198
199static const char *pipe_names[] = {
200 "VG1", "VG2",
201 "RGB1", "RGB2", "RGB3",
202 "VG3", "VG4",
203};
204
205enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane)
206{
207 struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
208 return mdp4_plane->pipe;
209}
210
211/* initialize plane */
212struct drm_plane *mdp4_plane_init(struct drm_device *dev,
213 enum mpd4_pipe pipe_id, bool private_plane)
214{
215 struct msm_drm_private *priv = dev->dev_private;
216 struct drm_plane *plane = NULL;
217 struct mdp4_plane *mdp4_plane;
218 int ret;
219
220 mdp4_plane = kzalloc(sizeof(*mdp4_plane), GFP_KERNEL);
221 if (!mdp4_plane) {
222 ret = -ENOMEM;
223 goto fail;
224 }
225
226 plane = &mdp4_plane->base;
227
228 mdp4_plane->pipe = pipe_id;
229 mdp4_plane->name = pipe_names[pipe_id];
230
231 drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &mdp4_plane_funcs,
232 mdp4_plane->formats, mdp4_plane->nformats, private_plane);
233
234 mdp4_plane_install_properties(plane, &plane->base);
235
236 return plane;
237
238fail:
239 if (plane)
240 mdp4_plane_destroy(plane);
241
242 return ERR_PTR(ret);
243}
diff --git a/drivers/gpu/drm/msm/msm_connector.c b/drivers/gpu/drm/msm/msm_connector.c
new file mode 100644
index 000000000000..aeea8879e36f
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_connector.c
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19#include "msm_connector.h"
20
21void msm_connector_init(struct msm_connector *connector,
22 const struct msm_connector_funcs *funcs,
23 struct drm_encoder *encoder)
24{
25 connector->funcs = funcs;
26 connector->encoder = encoder;
27}
28
29struct drm_encoder *msm_connector_attached_encoder(
30 struct drm_connector *connector)
31{
32 struct msm_connector *msm_connector = to_msm_connector(connector);
33 return msm_connector->encoder;
34}
diff --git a/drivers/gpu/drm/msm/msm_connector.h b/drivers/gpu/drm/msm/msm_connector.h
new file mode 100644
index 000000000000..0b41866adc08
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_connector.h
@@ -0,0 +1,68 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __MSM_CONNECTOR_H__
19#define __MSM_CONNECTOR_H__
20
21#include "msm_drv.h"
22
23/*
24 * Base class for MSM connectors. Typically a connector is a bit more
25 * passive. But with the split between (for example) DTV within MDP4,
26 * and HDMI encoder, we really need two parts to an encoder. Instead
27 * what we do is have the part external to the display controller block
28 * in the connector, which is called from the encoder to delegate the
29 * appropriate parts of modeset.
30 */
31
32struct msm_connector;
33
34struct msm_connector_funcs {
35 void (*dpms)(struct msm_connector *connector, int mode);
36 void (*mode_set)(struct msm_connector *connector,
37 struct drm_display_mode *mode);
38};
39
40struct msm_connector {
41 struct drm_connector base;
42 struct drm_encoder *encoder;
43 const struct msm_connector_funcs *funcs;
44};
45#define to_msm_connector(x) container_of(x, struct msm_connector, base)
46
47void msm_connector_init(struct msm_connector *connector,
48 const struct msm_connector_funcs *funcs,
49 struct drm_encoder *encoder);
50
51struct drm_encoder *msm_connector_attached_encoder(
52 struct drm_connector *connector);
53
54static inline struct msm_connector *get_connector(struct drm_encoder *encoder)
55{
56 struct msm_drm_private *priv = encoder->dev->dev_private;
57 int i;
58
59 for (i = 0; i < priv->num_connectors; i++) {
60 struct drm_connector *connector = priv->connectors[i];
61 if (msm_connector_attached_encoder(connector) == encoder)
62 return to_msm_connector(connector);
63 }
64
65 return NULL;
66}
67
68#endif /* __MSM_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
new file mode 100644
index 000000000000..b5ae0dbe1eb8
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -0,0 +1,532 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19
20#include <mach/iommu.h>
21
22static void msm_fb_output_poll_changed(struct drm_device *dev)
23{
24 struct msm_drm_private *priv = dev->dev_private;
25 if (priv->fbdev)
26 drm_fb_helper_hotplug_event(priv->fbdev);
27}
28
29static const struct drm_mode_config_funcs mode_config_funcs = {
30 .fb_create = msm_framebuffer_create,
31 .output_poll_changed = msm_fb_output_poll_changed,
32};
33
34static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
35 unsigned long iova, int flags, void *arg)
36{
37 DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
38 return 0;
39}
40
41int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu)
42{
43 struct msm_drm_private *priv = dev->dev_private;
44 int idx = priv->num_iommus++;
45
46 if (WARN_ON(idx >= ARRAY_SIZE(priv->iommus)))
47 return -EINVAL;
48
49 priv->iommus[idx] = iommu;
50
51 iommu_set_fault_handler(iommu, msm_fault_handler, dev);
52
53 /* need to iommu_attach_device() somewhere?? on resume?? */
54
55 return idx;
56}
57
58int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
59 const char **names, int cnt)
60{
61 int i, ret;
62
63 for (i = 0; i < cnt; i++) {
64 struct device *ctx = msm_iommu_get_ctx(names[i]);
65 if (!ctx)
66 continue;
67 ret = iommu_attach_device(iommu, ctx);
68 if (ret) {
69 dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
70 return ret;
71 }
72 }
73 return 0;
74}
75
76#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING
77static bool reglog = false;
78MODULE_PARM_DESC(reglog, "Enable register read/write logging");
79module_param(reglog, bool, 0600);
80#else
81#define reglog 0
82#endif
83
84void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
85 const char *dbgname)
86{
87 struct resource *res;
88 unsigned long size;
89 void __iomem *ptr;
90
91 if (name)
92 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
93 else
94 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
95
96 if (!res) {
97 dev_err(&pdev->dev, "failed to get memory resource: %s\n", name);
98 return ERR_PTR(-EINVAL);
99 }
100
101 size = resource_size(res);
102
103 ptr = devm_ioremap_nocache(&pdev->dev, res->start, size);
104 if (!ptr) {
105 dev_err(&pdev->dev, "failed to ioremap: %s\n", name);
106 return ERR_PTR(-ENOMEM);
107 }
108
109 if (reglog)
110 printk(KERN_DEBUG "IO:region %s %08x %08lx\n", dbgname, (u32)ptr, size);
111
112 return ptr;
113}
114
115void msm_writel(u32 data, void __iomem *addr)
116{
117 if (reglog)
118 printk(KERN_DEBUG "IO:W %08x %08x\n", (u32)addr, data);
119 writel(data, addr);
120}
121
122u32 msm_readl(const void __iomem *addr)
123{
124 u32 val = readl(addr);
125 if (reglog)
126 printk(KERN_ERR "IO:R %08x %08x\n", (u32)addr, val);
127 return val;
128}
129
130/*
131 * DRM operations:
132 */
133
134static int msm_unload(struct drm_device *dev)
135{
136 struct msm_drm_private *priv = dev->dev_private;
137 struct msm_kms *kms = priv->kms;
138
139 drm_kms_helper_poll_fini(dev);
140 drm_mode_config_cleanup(dev);
141 drm_vblank_cleanup(dev);
142
143 pm_runtime_get_sync(dev->dev);
144 drm_irq_uninstall(dev);
145 pm_runtime_put_sync(dev->dev);
146
147 flush_workqueue(priv->wq);
148 destroy_workqueue(priv->wq);
149
150 if (kms) {
151 pm_runtime_disable(dev->dev);
152 kms->funcs->destroy(kms);
153 }
154
155
156 dev->dev_private = NULL;
157
158 kfree(priv);
159
160 return 0;
161}
162
163static int msm_load(struct drm_device *dev, unsigned long flags)
164{
165 struct platform_device *pdev = dev->platformdev;
166 struct msm_drm_private *priv;
167 struct msm_kms *kms;
168 int ret;
169
170 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
171 if (!priv) {
172 dev_err(dev->dev, "failed to allocate private data\n");
173 return -ENOMEM;
174 }
175
176 dev->dev_private = priv;
177
178 priv->wq = alloc_ordered_workqueue("msm", 0);
179
180 INIT_LIST_HEAD(&priv->inactive_list);
181
182 drm_mode_config_init(dev);
183
184 kms = mdp4_kms_init(dev);
185 if (IS_ERR(kms)) {
186 /*
187 * NOTE: once we have GPU support, having no kms should not
188 * be considered fatal.. ideally we would still support gpu
189 * and (for example) use dmabuf/prime to share buffers with
190 * imx drm driver on iMX5
191 */
192 dev_err(dev->dev, "failed to load kms\n");
193 ret = PTR_ERR(priv->kms);
194 goto fail;
195 }
196
197 priv->kms = kms;
198
199 if (kms) {
200 pm_runtime_enable(dev->dev);
201 ret = kms->funcs->hw_init(kms);
202 if (ret) {
203 dev_err(dev->dev, "kms hw init failed: %d\n", ret);
204 goto fail;
205 }
206 }
207
208 dev->mode_config.min_width = 0;
209 dev->mode_config.min_height = 0;
210 dev->mode_config.max_width = 2048;
211 dev->mode_config.max_height = 2048;
212 dev->mode_config.funcs = &mode_config_funcs;
213
214 ret = drm_vblank_init(dev, 1);
215 if (ret < 0) {
216 dev_err(dev->dev, "failed to initialize vblank\n");
217 goto fail;
218 }
219
220 pm_runtime_get_sync(dev->dev);
221 ret = drm_irq_install(dev);
222 pm_runtime_put_sync(dev->dev);
223 if (ret < 0) {
224 dev_err(dev->dev, "failed to install IRQ handler\n");
225 goto fail;
226 }
227
228 platform_set_drvdata(pdev, dev);
229
230#ifdef CONFIG_DRM_MSM_FBDEV
231 priv->fbdev = msm_fbdev_init(dev);
232#endif
233
234 drm_kms_helper_poll_init(dev);
235
236 return 0;
237
238fail:
239 msm_unload(dev);
240 return ret;
241}
242
243static void msm_preclose(struct drm_device *dev, struct drm_file *file)
244{
245 struct msm_drm_private *priv = dev->dev_private;
246 struct msm_kms *kms = priv->kms;
247 if (kms)
248 kms->funcs->preclose(kms, file);
249}
250
251static void msm_lastclose(struct drm_device *dev)
252{
253 struct msm_drm_private *priv = dev->dev_private;
254 if (priv->fbdev) {
255 drm_modeset_lock_all(dev);
256 drm_fb_helper_restore_fbdev_mode(priv->fbdev);
257 drm_modeset_unlock_all(dev);
258 }
259}
260
261static irqreturn_t msm_irq(DRM_IRQ_ARGS)
262{
263 struct drm_device *dev = arg;
264 struct msm_drm_private *priv = dev->dev_private;
265 struct msm_kms *kms = priv->kms;
266 BUG_ON(!kms);
267 return kms->funcs->irq(kms);
268}
269
270static void msm_irq_preinstall(struct drm_device *dev)
271{
272 struct msm_drm_private *priv = dev->dev_private;
273 struct msm_kms *kms = priv->kms;
274 BUG_ON(!kms);
275 kms->funcs->irq_preinstall(kms);
276}
277
278static int msm_irq_postinstall(struct drm_device *dev)
279{
280 struct msm_drm_private *priv = dev->dev_private;
281 struct msm_kms *kms = priv->kms;
282 BUG_ON(!kms);
283 return kms->funcs->irq_postinstall(kms);
284}
285
286static void msm_irq_uninstall(struct drm_device *dev)
287{
288 struct msm_drm_private *priv = dev->dev_private;
289 struct msm_kms *kms = priv->kms;
290 BUG_ON(!kms);
291 kms->funcs->irq_uninstall(kms);
292}
293
294static int msm_enable_vblank(struct drm_device *dev, int crtc_id)
295{
296 struct msm_drm_private *priv = dev->dev_private;
297 struct msm_kms *kms = priv->kms;
298 if (!kms)
299 return -ENXIO;
300 DBG("dev=%p, crtc=%d", dev, crtc_id);
301 return kms->funcs->enable_vblank(kms, priv->crtcs[crtc_id]);
302}
303
304static void msm_disable_vblank(struct drm_device *dev, int crtc_id)
305{
306 struct msm_drm_private *priv = dev->dev_private;
307 struct msm_kms *kms = priv->kms;
308 if (!kms)
309 return;
310 DBG("dev=%p, crtc=%d", dev, crtc_id);
311 kms->funcs->disable_vblank(kms, priv->crtcs[crtc_id]);
312}
313
314/*
315 * DRM debugfs:
316 */
317
318#ifdef CONFIG_DEBUG_FS
319static int msm_gem_show(struct drm_device *dev, struct seq_file *m)
320{
321 struct msm_drm_private *priv = dev->dev_private;
322
323 seq_printf(m, "All Objects:\n");
324 msm_gem_describe_objects(&priv->inactive_list, m);
325
326 return 0;
327}
328
329static int msm_mm_show(struct drm_device *dev, struct seq_file *m)
330{
331 return drm_mm_dump_table(m, dev->mm_private);
332}
333
334static int msm_fb_show(struct drm_device *dev, struct seq_file *m)
335{
336 struct msm_drm_private *priv = dev->dev_private;
337 struct drm_framebuffer *fb, *fbdev_fb = NULL;
338
339 if (priv->fbdev) {
340 seq_printf(m, "fbcon ");
341 fbdev_fb = priv->fbdev->fb;
342 msm_framebuffer_describe(fbdev_fb, m);
343 }
344
345 mutex_lock(&dev->mode_config.fb_lock);
346 list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
347 if (fb == fbdev_fb)
348 continue;
349
350 seq_printf(m, "user ");
351 msm_framebuffer_describe(fb, m);
352 }
353 mutex_unlock(&dev->mode_config.fb_lock);
354
355 return 0;
356}
357
358static int show_locked(struct seq_file *m, void *arg)
359{
360 struct drm_info_node *node = (struct drm_info_node *) m->private;
361 struct drm_device *dev = node->minor->dev;
362 int (*show)(struct drm_device *dev, struct seq_file *m) =
363 node->info_ent->data;
364 int ret;
365
366 ret = mutex_lock_interruptible(&dev->struct_mutex);
367 if (ret)
368 return ret;
369
370 ret = show(dev, m);
371
372 mutex_unlock(&dev->struct_mutex);
373
374 return ret;
375}
376
377static struct drm_info_list msm_debugfs_list[] = {
378 {"gem", show_locked, 0, msm_gem_show},
379 { "mm", show_locked, 0, msm_mm_show },
380 { "fb", show_locked, 0, msm_fb_show },
381};
382
383static int msm_debugfs_init(struct drm_minor *minor)
384{
385 struct drm_device *dev = minor->dev;
386 int ret;
387
388 ret = drm_debugfs_create_files(msm_debugfs_list,
389 ARRAY_SIZE(msm_debugfs_list),
390 minor->debugfs_root, minor);
391
392 if (ret) {
393 dev_err(dev->dev, "could not install msm_debugfs_list\n");
394 return ret;
395 }
396
397 return ret;
398}
399
400static void msm_debugfs_cleanup(struct drm_minor *minor)
401{
402 drm_debugfs_remove_files(msm_debugfs_list,
403 ARRAY_SIZE(msm_debugfs_list), minor);
404}
405#endif
406
407static const struct vm_operations_struct vm_ops = {
408 .fault = msm_gem_fault,
409 .open = drm_gem_vm_open,
410 .close = drm_gem_vm_close,
411};
412
413static const struct file_operations fops = {
414 .owner = THIS_MODULE,
415 .open = drm_open,
416 .release = drm_release,
417 .unlocked_ioctl = drm_ioctl,
418#ifdef CONFIG_COMPAT
419 .compat_ioctl = drm_compat_ioctl,
420#endif
421 .poll = drm_poll,
422 .read = drm_read,
423 .llseek = no_llseek,
424 .mmap = msm_gem_mmap,
425};
426
427static struct drm_driver msm_driver = {
428 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
429 .load = msm_load,
430 .unload = msm_unload,
431 .preclose = msm_preclose,
432 .lastclose = msm_lastclose,
433 .irq_handler = msm_irq,
434 .irq_preinstall = msm_irq_preinstall,
435 .irq_postinstall = msm_irq_postinstall,
436 .irq_uninstall = msm_irq_uninstall,
437 .get_vblank_counter = drm_vblank_count,
438 .enable_vblank = msm_enable_vblank,
439 .disable_vblank = msm_disable_vblank,
440 .gem_free_object = msm_gem_free_object,
441 .gem_vm_ops = &vm_ops,
442 .dumb_create = msm_gem_dumb_create,
443 .dumb_map_offset = msm_gem_dumb_map_offset,
444 .dumb_destroy = msm_gem_dumb_destroy,
445#ifdef CONFIG_DEBUG_FS
446 .debugfs_init = msm_debugfs_init,
447 .debugfs_cleanup = msm_debugfs_cleanup,
448#endif
449 .fops = &fops,
450 .name = "msm",
451 .desc = "MSM Snapdragon DRM",
452 .date = "20130625",
453 .major = 1,
454 .minor = 0,
455};
456
457#ifdef CONFIG_PM_SLEEP
458static int msm_pm_suspend(struct device *dev)
459{
460 struct drm_device *ddev = dev_get_drvdata(dev);
461
462 drm_kms_helper_poll_disable(ddev);
463
464 return 0;
465}
466
467static int msm_pm_resume(struct device *dev)
468{
469 struct drm_device *ddev = dev_get_drvdata(dev);
470
471 drm_kms_helper_poll_enable(ddev);
472
473 return 0;
474}
475#endif
476
477static const struct dev_pm_ops msm_pm_ops = {
478 SET_SYSTEM_SLEEP_PM_OPS(msm_pm_suspend, msm_pm_resume)
479};
480
481/*
482 * Platform driver:
483 */
484
485static int msm_pdev_probe(struct platform_device *pdev)
486{
487 return drm_platform_init(&msm_driver, pdev);
488}
489
490static int msm_pdev_remove(struct platform_device *pdev)
491{
492 drm_platform_exit(&msm_driver, pdev);
493
494 return 0;
495}
496
497static const struct platform_device_id msm_id[] = {
498 { "mdp", 0 },
499 { }
500};
501
502static struct platform_driver msm_platform_driver = {
503 .probe = msm_pdev_probe,
504 .remove = msm_pdev_remove,
505 .driver = {
506 .owner = THIS_MODULE,
507 .name = "msm",
508 .pm = &msm_pm_ops,
509 },
510 .id_table = msm_id,
511};
512
513static int __init msm_drm_register(void)
514{
515 DBG("init");
516 hdmi_register();
517 return platform_driver_register(&msm_platform_driver);
518}
519
520static void __exit msm_drm_unregister(void)
521{
522 DBG("fini");
523 platform_driver_unregister(&msm_platform_driver);
524 hdmi_unregister();
525}
526
527module_init(msm_drm_register);
528module_exit(msm_drm_unregister);
529
530MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
531MODULE_DESCRIPTION("MSM DRM Driver");
532MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
new file mode 100644
index 000000000000..36f8ba2f5c84
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -0,0 +1,187 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __MSM_DRV_H__
19#define __MSM_DRV_H__
20
21#include <linux/kernel.h>
22#include <linux/clk.h>
23#include <linux/cpufreq.h>
24#include <linux/module.h>
25#include <linux/platform_device.h>
26#include <linux/pm.h>
27#include <linux/pm_runtime.h>
28#include <linux/slab.h>
29#include <linux/list.h>
30#include <linux/iommu.h>
31#include <linux/types.h>
32#include <asm/sizes.h>
33
34#ifndef CONFIG_OF
35#include <mach/board.h>
36#include <mach/socinfo.h>
37#include <mach/iommu_domains.h>
38#endif
39
40#include <drm/drmP.h>
41#include <drm/drm_crtc_helper.h>
42#include <drm/drm_fb_helper.h>
43
44struct msm_kms;
45
46#define NUM_DOMAINS 1 /* one for KMS, then one per gpu core (?) */
47
48struct msm_drm_private {
49
50 struct msm_kms *kms;
51
52 struct drm_fb_helper *fbdev;
53
54 /* list of GEM objects: */
55 struct list_head inactive_list;
56
57 struct workqueue_struct *wq;
58
59 /* registered IOMMU domains: */
60 unsigned int num_iommus;
61 struct iommu_domain *iommus[NUM_DOMAINS];
62
63 unsigned int num_crtcs;
64 struct drm_crtc *crtcs[8];
65
66 unsigned int num_encoders;
67 struct drm_encoder *encoders[8];
68
69 unsigned int num_connectors;
70 struct drm_connector *connectors[8];
71};
72
73struct msm_format {
74 uint32_t pixel_format;
75};
76
77/* As there are different display controller blocks depending on the
78 * snapdragon version, the kms support is split out and the appropriate
79 * implementation is loaded at runtime. The kms module is responsible
80 * for constructing the appropriate planes/crtcs/encoders/connectors.
81 */
82struct msm_kms_funcs {
83 /* hw initialization: */
84 int (*hw_init)(struct msm_kms *kms);
85 /* irq handling: */
86 void (*irq_preinstall)(struct msm_kms *kms);
87 int (*irq_postinstall)(struct msm_kms *kms);
88 void (*irq_uninstall)(struct msm_kms *kms);
89 irqreturn_t (*irq)(struct msm_kms *kms);
90 int (*enable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
91 void (*disable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
92 /* misc: */
93 const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format);
94 long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
95 struct drm_encoder *encoder);
96 /* cleanup: */
97 void (*preclose)(struct msm_kms *kms, struct drm_file *file);
98 void (*destroy)(struct msm_kms *kms);
99};
100
101struct msm_kms {
102 const struct msm_kms_funcs *funcs;
103};
104
105struct msm_kms *mdp4_kms_init(struct drm_device *dev);
106
107int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu);
108int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
109 const char **names, int cnt);
110
111int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
112int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
113uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
114int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
115 uint32_t *iova);
116int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova);
117void msm_gem_put_iova(struct drm_gem_object *obj, int id);
118int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
119 struct drm_mode_create_dumb *args);
120int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
121 uint32_t handle);
122int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
123 uint32_t handle, uint64_t *offset);
124void *msm_gem_vaddr_locked(struct drm_gem_object *obj);
125void *msm_gem_vaddr(struct drm_gem_object *obj);
126int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
127 struct work_struct *work);
128void msm_gem_free_object(struct drm_gem_object *obj);
129int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
130 uint32_t size, uint32_t flags, uint32_t *handle);
131struct drm_gem_object *msm_gem_new(struct drm_device *dev,
132 uint32_t size, uint32_t flags);
133
134struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane);
135const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb);
136struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
137 struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
138struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
139 struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd);
140
141struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev);
142
143struct drm_connector *hdmi_connector_init(struct drm_device *dev,
144 struct drm_encoder *encoder);
145void __init hdmi_register(void);
146void __exit hdmi_unregister(void);
147
148#ifdef CONFIG_DEBUG_FS
149void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
150void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
151void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
152#endif
153
154void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
155 const char *dbgname);
156void msm_writel(u32 data, void __iomem *addr);
157u32 msm_readl(const void __iomem *addr);
158
159#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
160#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
161
162static inline int align_pitch(int width, int bpp)
163{
164 int bytespp = (bpp + 7) / 8;
165 /* adreno needs pitch aligned to 32 pixels: */
166 return bytespp * ALIGN(width, 32);
167}
168
169/* for the generated headers: */
170#define INVALID_IDX(idx) ({BUG(); 0;})
171
172#define FIELD(val, name) (((val) & name ## __MASK) >> name ## __SHIFT)
173
174/* for conditionally setting boolean flag(s): */
175#define COND(bool, val) ((bool) ? (val) : 0)
176
177/* just put these here until we start adding driver private ioctls: */
178// TODO might shuffle these around.. just need something for now..
179#define MSM_BO_CACHE_MASK 0x0000000f
180#define MSM_BO_SCANOUT 0x00010000 /* scanout capable */
181
182#define MSM_BO_CACHED 0x00000001 /* default */
183#define MSM_BO_WC 0x0000002
184#define MSM_BO_UNCACHED 0x00000004
185
186
187#endif /* __MSM_DRV_H__ */
diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
new file mode 100644
index 000000000000..0286c0eeb10c
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_fb.c
@@ -0,0 +1,202 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19
20#include "drm_crtc.h"
21#include "drm_crtc_helper.h"
22
23struct msm_framebuffer {
24 struct drm_framebuffer base;
25 const struct msm_format *format;
26 struct drm_gem_object *planes[2];
27};
28#define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
29
30
31static int msm_framebuffer_create_handle(struct drm_framebuffer *fb,
32 struct drm_file *file_priv,
33 unsigned int *handle)
34{
35 struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
36 return drm_gem_handle_create(file_priv,
37 msm_fb->planes[0], handle);
38}
39
40static void msm_framebuffer_destroy(struct drm_framebuffer *fb)
41{
42 struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
43 int i, n = drm_format_num_planes(fb->pixel_format);
44
45 DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
46
47 drm_framebuffer_cleanup(fb);
48
49 for (i = 0; i < n; i++) {
50 struct drm_gem_object *bo = msm_fb->planes[i];
51 if (bo)
52 drm_gem_object_unreference_unlocked(bo);
53 }
54
55 kfree(msm_fb);
56}
57
58static int msm_framebuffer_dirty(struct drm_framebuffer *fb,
59 struct drm_file *file_priv, unsigned flags, unsigned color,
60 struct drm_clip_rect *clips, unsigned num_clips)
61{
62 return 0;
63}
64
65static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
66 .create_handle = msm_framebuffer_create_handle,
67 .destroy = msm_framebuffer_destroy,
68 .dirty = msm_framebuffer_dirty,
69};
70
71#ifdef CONFIG_DEBUG_FS
72void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
73{
74 struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
75 int i, n = drm_format_num_planes(fb->pixel_format);
76
77 seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
78 fb->width, fb->height, (char *)&fb->pixel_format,
79 fb->refcount.refcount.counter, fb->base.id);
80
81 for (i = 0; i < n; i++) {
82 seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
83 i, fb->offsets[i], fb->pitches[i]);
84 msm_gem_describe(msm_fb->planes[i], m);
85 }
86}
87#endif
88
89struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
90{
91 struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
92 return msm_fb->planes[plane];
93}
94
95const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
96{
97 struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
98 return msm_fb->format;
99}
100
101struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
102 struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd)
103{
104 struct drm_gem_object *bos[4] = {0};
105 struct drm_framebuffer *fb;
106 int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
107
108 for (i = 0; i < n; i++) {
109 bos[i] = drm_gem_object_lookup(dev, file,
110 mode_cmd->handles[i]);
111 if (!bos[i]) {
112 ret = -ENXIO;
113 goto out_unref;
114 }
115 }
116
117 fb = msm_framebuffer_init(dev, mode_cmd, bos);
118 if (IS_ERR(fb)) {
119 ret = PTR_ERR(fb);
120 goto out_unref;
121 }
122
123 return fb;
124
125out_unref:
126 for (i = 0; i < n; i++)
127 drm_gem_object_unreference_unlocked(bos[i]);
128 return ERR_PTR(ret);
129}
130
131struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
132 struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
133{
134 struct msm_drm_private *priv = dev->dev_private;
135 struct msm_kms *kms = priv->kms;
136 struct msm_framebuffer *msm_fb;
137 struct drm_framebuffer *fb = NULL;
138 const struct msm_format *format;
139 int ret, i, n;
140 unsigned int hsub, vsub;
141
142 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
143 dev, mode_cmd, mode_cmd->width, mode_cmd->height,
144 (char *)&mode_cmd->pixel_format);
145
146 n = drm_format_num_planes(mode_cmd->pixel_format);
147 hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
148 vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
149
150 format = kms->funcs->get_format(kms, mode_cmd->pixel_format);
151 if (!format) {
152 dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
153 (char *)&mode_cmd->pixel_format);
154 ret = -EINVAL;
155 goto fail;
156 }
157
158 msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
159 if (!msm_fb) {
160 ret = -ENOMEM;
161 goto fail;
162 }
163
164 fb = &msm_fb->base;
165
166 msm_fb->format = format;
167
168 for (i = 0; i < n; i++) {
169 unsigned int width = mode_cmd->width / (i ? hsub : 1);
170 unsigned int height = mode_cmd->height / (i ? vsub : 1);
171 unsigned int min_size;
172
173 min_size = (height - 1) * mode_cmd->pitches[i]
174 + width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
175 + mode_cmd->offsets[i];
176
177 if (bos[i]->size < min_size) {
178 ret = -EINVAL;
179 goto fail;
180 }
181
182 msm_fb->planes[i] = bos[i];
183 }
184
185 drm_helper_mode_fill_fb_struct(fb, mode_cmd);
186
187 ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
188 if (ret) {
189 dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
190 goto fail;
191 }
192
193 DBG("create: FB ID: %d (%p)", fb->base.id, fb);
194
195 return fb;
196
197fail:
198 if (fb)
199 msm_framebuffer_destroy(fb);
200
201 return ERR_PTR(ret);
202}
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
new file mode 100644
index 000000000000..6c6d7d4c9b4e
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -0,0 +1,258 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19
20#include "drm_crtc.h"
21#include "drm_fb_helper.h"
22
23/*
24 * fbdev funcs, to implement legacy fbdev interface on top of drm driver
25 */
26
27#define to_msm_fbdev(x) container_of(x, struct msm_fbdev, base)
28
29struct msm_fbdev {
30 struct drm_fb_helper base;
31 struct drm_framebuffer *fb;
32 struct drm_gem_object *bo;
33};
34
35static struct fb_ops msm_fb_ops = {
36 .owner = THIS_MODULE,
37
38 /* Note: to properly handle manual update displays, we wrap the
39 * basic fbdev ops which write to the framebuffer
40 */
41 .fb_read = fb_sys_read,
42 .fb_write = fb_sys_write,
43 .fb_fillrect = sys_fillrect,
44 .fb_copyarea = sys_copyarea,
45 .fb_imageblit = sys_imageblit,
46
47 .fb_check_var = drm_fb_helper_check_var,
48 .fb_set_par = drm_fb_helper_set_par,
49 .fb_pan_display = drm_fb_helper_pan_display,
50 .fb_blank = drm_fb_helper_blank,
51 .fb_setcmap = drm_fb_helper_setcmap,
52};
53
54static int msm_fbdev_create(struct drm_fb_helper *helper,
55 struct drm_fb_helper_surface_size *sizes)
56{
57 struct msm_fbdev *fbdev = to_msm_fbdev(helper);
58 struct drm_device *dev = helper->dev;
59 struct drm_framebuffer *fb = NULL;
60 struct fb_info *fbi = NULL;
61 struct drm_mode_fb_cmd2 mode_cmd = {0};
62 dma_addr_t paddr;
63 int ret, size;
64
65 /* only doing ARGB32 since this is what is needed to alpha-blend
66 * with video overlays:
67 */
68 sizes->surface_bpp = 32;
69 sizes->surface_depth = 32;
70
71 DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,
72 sizes->surface_height, sizes->surface_bpp,
73 sizes->fb_width, sizes->fb_height);
74
75 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
76 sizes->surface_depth);
77
78 mode_cmd.width = sizes->surface_width;
79 mode_cmd.height = sizes->surface_height;
80
81 mode_cmd.pitches[0] = align_pitch(
82 mode_cmd.width, sizes->surface_bpp);
83
84 /* allocate backing bo */
85 size = mode_cmd.pitches[0] * mode_cmd.height;
86 DBG("allocating %d bytes for fb %d", size, dev->primary->index);
87 mutex_lock(&dev->struct_mutex);
88 fbdev->bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
89 mutex_unlock(&dev->struct_mutex);
90 if (IS_ERR(fbdev->bo)) {
91 ret = PTR_ERR(fbdev->bo);
92 fbdev->bo = NULL;
93 dev_err(dev->dev, "failed to allocate buffer object: %d\n", ret);
94 goto fail;
95 }
96
97 fb = msm_framebuffer_init(dev, &mode_cmd, &fbdev->bo);
98 if (IS_ERR(fb)) {
99 dev_err(dev->dev, "failed to allocate fb\n");
100 /* note: if fb creation failed, we can't rely on fb destroy
101 * to unref the bo:
102 */
103 drm_gem_object_unreference(fbdev->bo);
104 ret = PTR_ERR(fb);
105 goto fail;
106 }
107
108 mutex_lock(&dev->struct_mutex);
109
110 /* TODO implement our own fb_mmap so we don't need this: */
111 msm_gem_get_iova_locked(fbdev->bo, 0, &paddr);
112
113 fbi = framebuffer_alloc(0, dev->dev);
114 if (!fbi) {
115 dev_err(dev->dev, "failed to allocate fb info\n");
116 ret = -ENOMEM;
117 goto fail_unlock;
118 }
119
120 DBG("fbi=%p, dev=%p", fbi, dev);
121
122 fbdev->fb = fb;
123 helper->fb = fb;
124 helper->fbdev = fbi;
125
126 fbi->par = helper;
127 fbi->flags = FBINFO_DEFAULT;
128 fbi->fbops = &msm_fb_ops;
129
130 strcpy(fbi->fix.id, "msm");
131
132 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
133 if (ret) {
134 ret = -ENOMEM;
135 goto fail_unlock;
136 }
137
138 drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
139 drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
140
141 dev->mode_config.fb_base = paddr;
142
143 fbi->screen_base = msm_gem_vaddr_locked(fbdev->bo);
144 fbi->screen_size = fbdev->bo->size;
145 fbi->fix.smem_start = paddr;
146 fbi->fix.smem_len = fbdev->bo->size;
147
148 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
149 DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
150
151 mutex_unlock(&dev->struct_mutex);
152
153 return 0;
154
155fail_unlock:
156 mutex_unlock(&dev->struct_mutex);
157fail:
158
159 if (ret) {
160 if (fbi)
161 framebuffer_release(fbi);
162 if (fb) {
163 drm_framebuffer_unregister_private(fb);
164 drm_framebuffer_remove(fb);
165 }
166 }
167
168 return ret;
169}
170
171static void msm_crtc_fb_gamma_set(struct drm_crtc *crtc,
172 u16 red, u16 green, u16 blue, int regno)
173{
174 DBG("fbdev: set gamma");
175}
176
177static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
178 u16 *red, u16 *green, u16 *blue, int regno)
179{
180 DBG("fbdev: get gamma");
181}
182
183static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
184 .gamma_set = msm_crtc_fb_gamma_set,
185 .gamma_get = msm_crtc_fb_gamma_get,
186 .fb_probe = msm_fbdev_create,
187};
188
189/* initialize fbdev helper */
190struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
191{
192 struct msm_drm_private *priv = dev->dev_private;
193 struct msm_fbdev *fbdev = NULL;
194 struct drm_fb_helper *helper;
195 int ret = 0;
196
197 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
198 if (!fbdev)
199 goto fail;
200
201 helper = &fbdev->base;
202
203 helper->funcs = &msm_fb_helper_funcs;
204
205 ret = drm_fb_helper_init(dev, helper,
206 priv->num_crtcs, priv->num_connectors);
207 if (ret) {
208 dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
209 goto fail;
210 }
211
212 drm_fb_helper_single_add_all_connectors(helper);
213
214 /* disable all the possible outputs/crtcs before entering KMS mode */
215 drm_helper_disable_unused_functions(dev);
216
217 drm_fb_helper_initial_config(helper, 32);
218
219 priv->fbdev = helper;
220
221 return helper;
222
223fail:
224 kfree(fbdev);
225 return NULL;
226}
227
228void msm_fbdev_free(struct drm_device *dev)
229{
230 struct msm_drm_private *priv = dev->dev_private;
231 struct drm_fb_helper *helper = priv->fbdev;
232 struct msm_fbdev *fbdev;
233 struct fb_info *fbi;
234
235 DBG();
236
237 fbi = helper->fbdev;
238
239 /* only cleanup framebuffer if it is present */
240 if (fbi) {
241 unregister_framebuffer(fbi);
242 framebuffer_release(fbi);
243 }
244
245 drm_fb_helper_fini(helper);
246
247 fbdev = to_msm_fbdev(priv->fbdev);
248
249 /* this will free the backing object */
250 if (fbdev->fb) {
251 drm_framebuffer_unregister_private(fbdev->fb);
252 drm_framebuffer_remove(fbdev->fb);
253 }
254
255 kfree(fbdev);
256
257 priv->fbdev = NULL;
258}
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
new file mode 100644
index 000000000000..a52e6cca8403
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -0,0 +1,521 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/spinlock.h>
19#include <linux/shmem_fs.h>
20
21#include "msm_drv.h"
22#include "msm_gem.h"
23
24
25/* called with dev->struct_mutex held */
26static struct page **get_pages(struct drm_gem_object *obj)
27{
28 struct msm_gem_object *msm_obj = to_msm_bo(obj);
29
30 if (!msm_obj->pages) {
31 struct drm_device *dev = obj->dev;
32 struct page **p = drm_gem_get_pages(obj, 0);
33 int npages = obj->size >> PAGE_SHIFT;
34
35 if (IS_ERR(p)) {
36 dev_err(dev->dev, "could not get pages: %ld\n",
37 PTR_ERR(p));
38 return p;
39 }
40
41 msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
42 if (!msm_obj->sgt) {
43 dev_err(dev->dev, "failed to allocate sgt\n");
44 return ERR_PTR(-ENOMEM);
45 }
46
47 msm_obj->pages = p;
48
49 /* For non-cached buffers, ensure the new pages are clean
50 * because display controller, GPU, etc. are not coherent:
51 */
52 if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED))
53 dma_map_sg(dev->dev, msm_obj->sgt->sgl,
54 msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
55 }
56
57 return msm_obj->pages;
58}
59
60static void put_pages(struct drm_gem_object *obj)
61{
62 struct msm_gem_object *msm_obj = to_msm_bo(obj);
63
64 if (msm_obj->pages) {
65 /* For non-cached buffers, ensure the new pages are clean
66 * because display controller, GPU, etc. are not coherent:
67 */
68 if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED))
69 dma_unmap_sg(obj->dev->dev, msm_obj->sgt->sgl,
70 msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
71 sg_free_table(msm_obj->sgt);
72 kfree(msm_obj->sgt);
73
74 drm_gem_put_pages(obj, msm_obj->pages, true, false);
75 msm_obj->pages = NULL;
76 }
77}
78
79int msm_gem_mmap_obj(struct drm_gem_object *obj,
80 struct vm_area_struct *vma)
81{
82 struct msm_gem_object *msm_obj = to_msm_bo(obj);
83
84 vma->vm_flags &= ~VM_PFNMAP;
85 vma->vm_flags |= VM_MIXEDMAP;
86
87 if (msm_obj->flags & MSM_BO_WC) {
88 vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
89 } else if (msm_obj->flags & MSM_BO_UNCACHED) {
90 vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
91 } else {
92 /*
93 * Shunt off cached objs to shmem file so they have their own
94 * address_space (so unmap_mapping_range does what we want,
95 * in particular in the case of mmap'd dmabufs)
96 */
97 fput(vma->vm_file);
98 get_file(obj->filp);
99 vma->vm_pgoff = 0;
100 vma->vm_file = obj->filp;
101
102 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
103 }
104
105 return 0;
106}
107
108int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
109{
110 int ret;
111
112 ret = drm_gem_mmap(filp, vma);
113 if (ret) {
114 DBG("mmap failed: %d", ret);
115 return ret;
116 }
117
118 return msm_gem_mmap_obj(vma->vm_private_data, vma);
119}
120
121int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
122{
123 struct drm_gem_object *obj = vma->vm_private_data;
124 struct msm_gem_object *msm_obj = to_msm_bo(obj);
125 struct drm_device *dev = obj->dev;
126 struct page **pages;
127 unsigned long pfn;
128 pgoff_t pgoff;
129 int ret;
130
131 /* Make sure we don't parallel update on a fault, nor move or remove
132 * something from beneath our feet
133 */
134 ret = mutex_lock_interruptible(&dev->struct_mutex);
135 if (ret)
136 goto out;
137
138 /* make sure we have pages attached now */
139 pages = get_pages(obj);
140 if (IS_ERR(pages)) {
141 ret = PTR_ERR(pages);
142 goto out_unlock;
143 }
144
145 /* We don't use vmf->pgoff since that has the fake offset: */
146 pgoff = ((unsigned long)vmf->virtual_address -
147 vma->vm_start) >> PAGE_SHIFT;
148
149 pfn = page_to_pfn(msm_obj->pages[pgoff]);
150
151 VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
152 pfn, pfn << PAGE_SHIFT);
153
154 ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
155
156out_unlock:
157 mutex_unlock(&dev->struct_mutex);
158out:
159 switch (ret) {
160 case -EAGAIN:
161 set_need_resched();
162 case 0:
163 case -ERESTARTSYS:
164 case -EINTR:
165 return VM_FAULT_NOPAGE;
166 case -ENOMEM:
167 return VM_FAULT_OOM;
168 default:
169 return VM_FAULT_SIGBUS;
170 }
171}
172
173/** get mmap offset */
174static uint64_t mmap_offset(struct drm_gem_object *obj)
175{
176 struct drm_device *dev = obj->dev;
177 int ret;
178
179 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
180
181 /* Make it mmapable */
182 ret = drm_gem_create_mmap_offset(obj);
183
184 if (ret) {
185 dev_err(dev->dev, "could not allocate mmap offset\n");
186 return 0;
187 }
188
189 return drm_vma_node_offset_addr(&obj->vma_node);
190}
191
192uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj)
193{
194 uint64_t offset;
195 mutex_lock(&obj->dev->struct_mutex);
196 offset = mmap_offset(obj);
197 mutex_unlock(&obj->dev->struct_mutex);
198 return offset;
199}
200
201/* helpers for dealing w/ iommu: */
202static int map_range(struct iommu_domain *domain, unsigned int iova,
203 struct sg_table *sgt, unsigned int len, int prot)
204{
205 struct scatterlist *sg;
206 unsigned int da = iova;
207 unsigned int i, j;
208 int ret;
209
210 if (!domain || !sgt)
211 return -EINVAL;
212
213 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
214 u32 pa = sg_phys(sg) - sg->offset;
215 size_t bytes = sg->length + sg->offset;
216
217 VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes);
218
219 ret = iommu_map(domain, da, pa, bytes, prot);
220 if (ret)
221 goto fail;
222
223 da += bytes;
224 }
225
226 return 0;
227
228fail:
229 da = iova;
230
231 for_each_sg(sgt->sgl, sg, i, j) {
232 size_t bytes = sg->length + sg->offset;
233 iommu_unmap(domain, da, bytes);
234 da += bytes;
235 }
236 return ret;
237}
238
239static void unmap_range(struct iommu_domain *domain, unsigned int iova,
240 struct sg_table *sgt, unsigned int len)
241{
242 struct scatterlist *sg;
243 unsigned int da = iova;
244 int i;
245
246 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
247 size_t bytes = sg->length + sg->offset;
248 size_t unmapped;
249
250 unmapped = iommu_unmap(domain, da, bytes);
251 if (unmapped < bytes)
252 break;
253
254 VERB("unmap[%d]: %08x(%x)", i, iova, bytes);
255
256 BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE));
257
258 da += bytes;
259 }
260}
261
262/* should be called under struct_mutex.. although it can be called
263 * from atomic context without struct_mutex to acquire an extra
264 * iova ref if you know one is already held.
265 *
266 * That means when I do eventually need to add support for unpinning
267 * the refcnt counter needs to be atomic_t.
268 */
269int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
270 uint32_t *iova)
271{
272 struct msm_gem_object *msm_obj = to_msm_bo(obj);
273 int ret = 0;
274
275 if (!msm_obj->domain[id].iova) {
276 struct msm_drm_private *priv = obj->dev->dev_private;
277 uint32_t offset = (uint32_t)mmap_offset(obj);
278 struct page **pages;
279 pages = get_pages(obj);
280 if (IS_ERR(pages))
281 return PTR_ERR(pages);
282 // XXX ideally we would not map buffers writable when not needed...
283 ret = map_range(priv->iommus[id], offset, msm_obj->sgt,
284 obj->size, IOMMU_READ | IOMMU_WRITE);
285 msm_obj->domain[id].iova = offset;
286 }
287
288 if (!ret)
289 *iova = msm_obj->domain[id].iova;
290
291 return ret;
292}
293
294int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova)
295{
296 int ret;
297 mutex_lock(&obj->dev->struct_mutex);
298 ret = msm_gem_get_iova_locked(obj, id, iova);
299 mutex_unlock(&obj->dev->struct_mutex);
300 return ret;
301}
302
303void msm_gem_put_iova(struct drm_gem_object *obj, int id)
304{
305 // XXX TODO ..
306 // NOTE: probably don't need a _locked() version.. we wouldn't
307 // normally unmap here, but instead just mark that it could be
308 // unmapped (if the iova refcnt drops to zero), but then later
309 // if another _get_iova_locked() fails we can start unmapping
310 // things that are no longer needed..
311}
312
313int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
314 struct drm_mode_create_dumb *args)
315{
316 args->pitch = align_pitch(args->width, args->bpp);
317 args->size = PAGE_ALIGN(args->pitch * args->height);
318 return msm_gem_new_handle(dev, file, args->size,
319 MSM_BO_SCANOUT | MSM_BO_WC, &args->handle);
320}
321
322int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
323 uint32_t handle)
324{
325 /* No special work needed, drop the reference and see what falls out */
326 return drm_gem_handle_delete(file, handle);
327}
328
329int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
330 uint32_t handle, uint64_t *offset)
331{
332 struct drm_gem_object *obj;
333 int ret = 0;
334
335 /* GEM does all our handle to object mapping */
336 obj = drm_gem_object_lookup(dev, file, handle);
337 if (obj == NULL) {
338 ret = -ENOENT;
339 goto fail;
340 }
341
342 *offset = msm_gem_mmap_offset(obj);
343
344 drm_gem_object_unreference_unlocked(obj);
345
346fail:
347 return ret;
348}
349
350void *msm_gem_vaddr_locked(struct drm_gem_object *obj)
351{
352 struct msm_gem_object *msm_obj = to_msm_bo(obj);
353 WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
354 if (!msm_obj->vaddr) {
355 struct page **pages = get_pages(obj);
356 if (IS_ERR(pages))
357 return ERR_CAST(pages);
358 msm_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT,
359 VM_MAP, pgprot_writecombine(PAGE_KERNEL));
360 }
361 return msm_obj->vaddr;
362}
363
364void *msm_gem_vaddr(struct drm_gem_object *obj)
365{
366 void *ret;
367 mutex_lock(&obj->dev->struct_mutex);
368 ret = msm_gem_vaddr_locked(obj);
369 mutex_unlock(&obj->dev->struct_mutex);
370 return ret;
371}
372
373int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
374 struct work_struct *work)
375{
376 struct drm_device *dev = obj->dev;
377 struct msm_drm_private *priv = dev->dev_private;
378
379 /* just a place-holder until we have gpu.. */
380 queue_work(priv->wq, work);
381
382 return 0;
383}
384
385#ifdef CONFIG_DEBUG_FS
386void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
387{
388 struct drm_device *dev = obj->dev;
389 struct msm_gem_object *msm_obj = to_msm_bo(obj);
390 uint64_t off = drm_vma_node_start(&obj->vma_node);
391
392 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
393 seq_printf(m, "%08x: %2d (%2d) %08llx %p %d\n",
394 msm_obj->flags, obj->name, obj->refcount.refcount.counter,
395 off, msm_obj->vaddr, obj->size);
396}
397
398void msm_gem_describe_objects(struct list_head *list, struct seq_file *m)
399{
400 struct msm_gem_object *msm_obj;
401 int count = 0;
402 size_t size = 0;
403
404 list_for_each_entry(msm_obj, list, mm_list) {
405 struct drm_gem_object *obj = &msm_obj->base;
406 seq_printf(m, " ");
407 msm_gem_describe(obj, m);
408 count++;
409 size += obj->size;
410 }
411
412 seq_printf(m, "Total %d objects, %zu bytes\n", count, size);
413}
414#endif
415
416void msm_gem_free_object(struct drm_gem_object *obj)
417{
418 struct drm_device *dev = obj->dev;
419 struct msm_gem_object *msm_obj = to_msm_bo(obj);
420 int id;
421
422 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
423
424 list_del(&msm_obj->mm_list);
425
426 for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) {
427 if (msm_obj->domain[id].iova) {
428 struct msm_drm_private *priv = obj->dev->dev_private;
429 uint32_t offset = (uint32_t)mmap_offset(obj);
430 unmap_range(priv->iommus[id], offset,
431 msm_obj->sgt, obj->size);
432 }
433 }
434
435 drm_gem_free_mmap_offset(obj);
436
437 if (msm_obj->vaddr)
438 vunmap(msm_obj->vaddr);
439
440 put_pages(obj);
441
442 drm_gem_object_release(obj);
443
444 kfree(msm_obj);
445}
446
447/* convenience method to construct a GEM buffer object, and userspace handle */
448int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
449 uint32_t size, uint32_t flags, uint32_t *handle)
450{
451 struct drm_gem_object *obj;
452 int ret;
453
454 ret = mutex_lock_interruptible(&dev->struct_mutex);
455 if (ret)
456 return ret;
457
458 obj = msm_gem_new(dev, size, flags);
459
460 mutex_unlock(&dev->struct_mutex);
461
462 if (IS_ERR(obj))
463 return PTR_ERR(obj);
464
465 ret = drm_gem_handle_create(file, obj, handle);
466
467 /* drop reference from allocate - handle holds it now */
468 drm_gem_object_unreference_unlocked(obj);
469
470 return ret;
471}
472
473struct drm_gem_object *msm_gem_new(struct drm_device *dev,
474 uint32_t size, uint32_t flags)
475{
476 struct msm_drm_private *priv = dev->dev_private;
477 struct msm_gem_object *msm_obj;
478 struct drm_gem_object *obj = NULL;
479 int ret;
480
481 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
482
483 size = PAGE_ALIGN(size);
484
485 switch (flags & MSM_BO_CACHE_MASK) {
486 case MSM_BO_UNCACHED:
487 case MSM_BO_CACHED:
488 case MSM_BO_WC:
489 break;
490 default:
491 dev_err(dev->dev, "invalid cache flag: %x\n",
492 (flags & MSM_BO_CACHE_MASK));
493 ret = -EINVAL;
494 goto fail;
495 }
496
497 msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL);
498 if (!msm_obj) {
499 ret = -ENOMEM;
500 goto fail;
501 }
502
503 obj = &msm_obj->base;
504
505 ret = drm_gem_object_init(dev, obj, size);
506 if (ret)
507 goto fail;
508
509 msm_obj->flags = flags;
510
511
512 list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
513
514 return obj;
515
516fail:
517 if (obj)
518 drm_gem_object_unreference_unlocked(obj);
519
520 return ERR_PTR(ret);
521}
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
new file mode 100644
index 000000000000..fcafd1965151
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -0,0 +1,41 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __MSM_GEM_H__
19#define __MSM_GEM_H__
20
21#include "msm_drv.h"
22
23struct msm_gem_object {
24 struct drm_gem_object base;
25
26 uint32_t flags;
27
28 struct list_head mm_list;
29
30 struct page **pages;
31 struct sg_table *sgt;
32 void *vaddr;
33
34 struct {
35 // XXX
36 uint32_t iova;
37 } domain[NUM_DOMAINS];
38};
39#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
40
41#endif /* __MSM_GEM_H__ */