diff options
Diffstat (limited to 'drivers/gpu/drm/exynos')
20 files changed, 4154 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig new file mode 100644 index 000000000000..847466aab435 --- /dev/null +++ b/drivers/gpu/drm/exynos/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | config DRM_EXYNOS | ||
2 | tristate "DRM Support for Samsung SoC EXYNOS Series" | ||
3 | depends on DRM && PLAT_SAMSUNG | ||
4 | default n | ||
5 | select DRM_KMS_HELPER | ||
6 | select FB_CFB_FILLRECT | ||
7 | select FB_CFB_COPYAREA | ||
8 | select FB_CFB_IMAGEBLIT | ||
9 | select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE | ||
10 | help | ||
11 | Choose this option if you have a Samsung SoC EXYNOS chipset. | ||
12 | If M is selected the module will be called exynosdrm. | ||
13 | |||
14 | config DRM_EXYNOS_FIMD | ||
15 | tristate "Exynos DRM FIMD" | ||
16 | depends on DRM_EXYNOS | ||
17 | default n | ||
18 | help | ||
19 | Choose this option if you want to use Exynos FIMD for DRM. | ||
20 | If M is selected, the module will be called exynos_drm_fimd | ||
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile new file mode 100644 index 000000000000..0496d3ff2683 --- /dev/null +++ b/drivers/gpu/drm/exynos/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # Makefile for the drm device driver. This driver provides support for the | ||
3 | # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. | ||
4 | |||
5 | ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos | ||
6 | exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ | ||
7 | exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \ | ||
8 | exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o | ||
9 | |||
10 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o | ||
11 | obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c new file mode 100644 index 000000000000..6f8afea94fc9 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* exynos_drm_buf.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Author: Inki Dae <inki.dae@samsung.com> | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the next | ||
14 | * paragraph) shall be included in all copies or substantial portions of the | ||
15 | * Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
20 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
23 | * OTHER DEALINGS IN THE SOFTWARE. | ||
24 | */ | ||
25 | |||
26 | #include "drmP.h" | ||
27 | #include "drm.h" | ||
28 | |||
29 | #include "exynos_drm_drv.h" | ||
30 | #include "exynos_drm_buf.h" | ||
31 | |||
32 | static DEFINE_MUTEX(exynos_drm_buf_lock); | ||
33 | |||
34 | static int lowlevel_buffer_allocate(struct drm_device *dev, | ||
35 | struct exynos_drm_buf_entry *entry) | ||
36 | { | ||
37 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
38 | |||
39 | entry->vaddr = dma_alloc_writecombine(dev->dev, entry->size, | ||
40 | (dma_addr_t *)&entry->paddr, GFP_KERNEL); | ||
41 | if (!entry->paddr) { | ||
42 | DRM_ERROR("failed to allocate buffer.\n"); | ||
43 | return -ENOMEM; | ||
44 | } | ||
45 | |||
46 | DRM_DEBUG_KMS("allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n", | ||
47 | (unsigned int)entry->vaddr, entry->paddr, entry->size); | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static void lowlevel_buffer_deallocate(struct drm_device *dev, | ||
53 | struct exynos_drm_buf_entry *entry) | ||
54 | { | ||
55 | DRM_DEBUG_KMS("%s.\n", __FILE__); | ||
56 | |||
57 | if (entry->paddr && entry->vaddr && entry->size) | ||
58 | dma_free_writecombine(dev->dev, entry->size, entry->vaddr, | ||
59 | entry->paddr); | ||
60 | else | ||
61 | DRM_DEBUG_KMS("entry data is null.\n"); | ||
62 | } | ||
63 | |||
64 | struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev, | ||
65 | unsigned int size) | ||
66 | { | ||
67 | struct exynos_drm_buf_entry *entry; | ||
68 | |||
69 | DRM_DEBUG_KMS("%s.\n", __FILE__); | ||
70 | |||
71 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
72 | if (!entry) { | ||
73 | DRM_ERROR("failed to allocate exynos_drm_buf_entry.\n"); | ||
74 | return ERR_PTR(-ENOMEM); | ||
75 | } | ||
76 | |||
77 | entry->size = size; | ||
78 | |||
79 | /* | ||
80 | * allocate memory region with size and set the memory information | ||
81 | * to vaddr and paddr of a entry object. | ||
82 | */ | ||
83 | if (lowlevel_buffer_allocate(dev, entry) < 0) { | ||
84 | kfree(entry); | ||
85 | entry = NULL; | ||
86 | return ERR_PTR(-ENOMEM); | ||
87 | } | ||
88 | |||
89 | return entry; | ||
90 | } | ||
91 | |||
92 | void exynos_drm_buf_destroy(struct drm_device *dev, | ||
93 | struct exynos_drm_buf_entry *entry) | ||
94 | { | ||
95 | DRM_DEBUG_KMS("%s.\n", __FILE__); | ||
96 | |||
97 | if (!entry) { | ||
98 | DRM_DEBUG_KMS("entry is null.\n"); | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | lowlevel_buffer_deallocate(dev, entry); | ||
103 | |||
104 | kfree(entry); | ||
105 | entry = NULL; | ||
106 | } | ||
107 | |||
108 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
109 | MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module"); | ||
110 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h new file mode 100644 index 000000000000..045d59eab01a --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* exynos_drm_buf.h | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Author: Inki Dae <inki.dae@samsung.com> | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the next | ||
14 | * paragraph) shall be included in all copies or substantial portions of the | ||
15 | * Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
20 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
23 | * OTHER DEALINGS IN THE SOFTWARE. | ||
24 | */ | ||
25 | |||
26 | #ifndef _EXYNOS_DRM_BUF_H_ | ||
27 | #define _EXYNOS_DRM_BUF_H_ | ||
28 | |||
29 | /* | ||
30 | * exynos drm buffer entry structure. | ||
31 | * | ||
32 | * @paddr: physical address of allocated memory. | ||
33 | * @vaddr: kernel virtual address of allocated memory. | ||
34 | * @size: size of allocated memory. | ||
35 | */ | ||
36 | struct exynos_drm_buf_entry { | ||
37 | dma_addr_t paddr; | ||
38 | void __iomem *vaddr; | ||
39 | unsigned int size; | ||
40 | }; | ||
41 | |||
42 | /* allocate physical memory. */ | ||
43 | struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev, | ||
44 | unsigned int size); | ||
45 | |||
46 | /* get physical memory information of a drm framebuffer. */ | ||
47 | struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb); | ||
48 | |||
49 | /* remove allocated physical memory. */ | ||
50 | void exynos_drm_buf_destroy(struct drm_device *dev, | ||
51 | struct exynos_drm_buf_entry *entry); | ||
52 | |||
53 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c new file mode 100644 index 000000000000..985d9e768728 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
3 | * Authors: | ||
4 | * Inki Dae <inki.dae@samsung.com> | ||
5 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
6 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
7 | * | ||
8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
9 | * copy of this software and associated documentation files (the "Software"), | ||
10 | * to deal in the Software without restriction, including without limitation | ||
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
13 | * Software is furnished to do so, subject to the following conditions: | ||
14 | * | ||
15 | * The above copyright notice and this permission notice (including the next | ||
16 | * paragraph) shall be included in all copies or substantial portions of the | ||
17 | * Software. | ||
18 | * | ||
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
22 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
25 | * OTHER DEALINGS IN THE SOFTWARE. | ||
26 | */ | ||
27 | |||
28 | #include "drmP.h" | ||
29 | #include "drm_crtc_helper.h" | ||
30 | |||
31 | #include "exynos_drm_drv.h" | ||
32 | #include "exynos_drm_encoder.h" | ||
33 | |||
34 | #define MAX_EDID 256 | ||
35 | #define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\ | ||
36 | drm_connector) | ||
37 | |||
38 | struct exynos_drm_connector { | ||
39 | struct drm_connector drm_connector; | ||
40 | }; | ||
41 | |||
42 | /* convert exynos_video_timings to drm_display_mode */ | ||
43 | static inline void | ||
44 | convert_to_display_mode(struct drm_display_mode *mode, | ||
45 | struct fb_videomode *timing) | ||
46 | { | ||
47 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
48 | |||
49 | mode->clock = timing->pixclock / 1000; | ||
50 | |||
51 | mode->hdisplay = timing->xres; | ||
52 | mode->hsync_start = mode->hdisplay + timing->left_margin; | ||
53 | mode->hsync_end = mode->hsync_start + timing->hsync_len; | ||
54 | mode->htotal = mode->hsync_end + timing->right_margin; | ||
55 | |||
56 | mode->vdisplay = timing->yres; | ||
57 | mode->vsync_start = mode->vdisplay + timing->upper_margin; | ||
58 | mode->vsync_end = mode->vsync_start + timing->vsync_len; | ||
59 | mode->vtotal = mode->vsync_end + timing->lower_margin; | ||
60 | } | ||
61 | |||
62 | /* convert drm_display_mode to exynos_video_timings */ | ||
63 | static inline void | ||
64 | convert_to_video_timing(struct fb_videomode *timing, | ||
65 | struct drm_display_mode *mode) | ||
66 | { | ||
67 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
68 | |||
69 | memset(timing, 0, sizeof(*timing)); | ||
70 | |||
71 | timing->pixclock = mode->clock * 1000; | ||
72 | timing->refresh = mode->vrefresh; | ||
73 | |||
74 | timing->xres = mode->hdisplay; | ||
75 | timing->left_margin = mode->hsync_start - mode->hdisplay; | ||
76 | timing->hsync_len = mode->hsync_end - mode->hsync_start; | ||
77 | timing->right_margin = mode->htotal - mode->hsync_end; | ||
78 | |||
79 | timing->yres = mode->vdisplay; | ||
80 | timing->upper_margin = mode->vsync_start - mode->vdisplay; | ||
81 | timing->vsync_len = mode->vsync_end - mode->vsync_start; | ||
82 | timing->lower_margin = mode->vtotal - mode->vsync_end; | ||
83 | |||
84 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
85 | timing->vmode = FB_VMODE_INTERLACED; | ||
86 | else | ||
87 | timing->vmode = FB_VMODE_NONINTERLACED; | ||
88 | |||
89 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
90 | timing->vmode |= FB_VMODE_DOUBLE; | ||
91 | } | ||
92 | |||
93 | static int exynos_drm_connector_get_modes(struct drm_connector *connector) | ||
94 | { | ||
95 | struct exynos_drm_manager *manager = | ||
96 | exynos_drm_get_manager(connector->encoder); | ||
97 | struct exynos_drm_display *display = manager->display; | ||
98 | unsigned int count; | ||
99 | |||
100 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
101 | |||
102 | if (!display) { | ||
103 | DRM_DEBUG_KMS("display is null.\n"); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * if get_edid() exists then get_edid() callback of hdmi side | ||
109 | * is called to get edid data through i2c interface else | ||
110 | * get timing from the FIMD driver(display controller). | ||
111 | * | ||
112 | * P.S. in case of lcd panel, count is always 1 if success | ||
113 | * because lcd panel has only one mode. | ||
114 | */ | ||
115 | if (display->get_edid) { | ||
116 | int ret; | ||
117 | void *edid; | ||
118 | |||
119 | edid = kzalloc(MAX_EDID, GFP_KERNEL); | ||
120 | if (!edid) { | ||
121 | DRM_ERROR("failed to allocate edid\n"); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | ret = display->get_edid(manager->dev, connector, | ||
126 | edid, MAX_EDID); | ||
127 | if (ret < 0) { | ||
128 | DRM_ERROR("failed to get edid data.\n"); | ||
129 | kfree(edid); | ||
130 | edid = NULL; | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | drm_mode_connector_update_edid_property(connector, edid); | ||
135 | count = drm_add_edid_modes(connector, edid); | ||
136 | |||
137 | kfree(connector->display_info.raw_edid); | ||
138 | connector->display_info.raw_edid = edid; | ||
139 | } else { | ||
140 | struct drm_display_mode *mode = drm_mode_create(connector->dev); | ||
141 | struct fb_videomode *timing; | ||
142 | |||
143 | if (display->get_timing) | ||
144 | timing = display->get_timing(manager->dev); | ||
145 | else { | ||
146 | drm_mode_destroy(connector->dev, mode); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | convert_to_display_mode(mode, timing); | ||
151 | |||
152 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | ||
153 | drm_mode_set_name(mode); | ||
154 | drm_mode_probed_add(connector, mode); | ||
155 | |||
156 | count = 1; | ||
157 | } | ||
158 | |||
159 | return count; | ||
160 | } | ||
161 | |||
162 | static int exynos_drm_connector_mode_valid(struct drm_connector *connector, | ||
163 | struct drm_display_mode *mode) | ||
164 | { | ||
165 | struct exynos_drm_manager *manager = | ||
166 | exynos_drm_get_manager(connector->encoder); | ||
167 | struct exynos_drm_display *display = manager->display; | ||
168 | struct fb_videomode timing; | ||
169 | int ret = MODE_BAD; | ||
170 | |||
171 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
172 | |||
173 | convert_to_video_timing(&timing, mode); | ||
174 | |||
175 | if (display && display->check_timing) | ||
176 | if (!display->check_timing(manager->dev, (void *)&timing)) | ||
177 | ret = MODE_OK; | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector) | ||
183 | { | ||
184 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
185 | |||
186 | return connector->encoder; | ||
187 | } | ||
188 | |||
189 | static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { | ||
190 | .get_modes = exynos_drm_connector_get_modes, | ||
191 | .mode_valid = exynos_drm_connector_mode_valid, | ||
192 | .best_encoder = exynos_drm_best_encoder, | ||
193 | }; | ||
194 | |||
195 | /* get detection status of display device. */ | ||
196 | static enum drm_connector_status | ||
197 | exynos_drm_connector_detect(struct drm_connector *connector, bool force) | ||
198 | { | ||
199 | struct exynos_drm_manager *manager = | ||
200 | exynos_drm_get_manager(connector->encoder); | ||
201 | struct exynos_drm_display *display = manager->display; | ||
202 | enum drm_connector_status status = connector_status_disconnected; | ||
203 | |||
204 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
205 | |||
206 | if (display && display->is_connected) { | ||
207 | if (display->is_connected(manager->dev)) | ||
208 | status = connector_status_connected; | ||
209 | else | ||
210 | status = connector_status_disconnected; | ||
211 | } | ||
212 | |||
213 | return status; | ||
214 | } | ||
215 | |||
216 | static void exynos_drm_connector_destroy(struct drm_connector *connector) | ||
217 | { | ||
218 | struct exynos_drm_connector *exynos_connector = | ||
219 | to_exynos_connector(connector); | ||
220 | |||
221 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
222 | |||
223 | drm_sysfs_connector_remove(connector); | ||
224 | drm_connector_cleanup(connector); | ||
225 | kfree(exynos_connector); | ||
226 | } | ||
227 | |||
228 | static struct drm_connector_funcs exynos_connector_funcs = { | ||
229 | .dpms = drm_helper_connector_dpms, | ||
230 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
231 | .detect = exynos_drm_connector_detect, | ||
232 | .destroy = exynos_drm_connector_destroy, | ||
233 | }; | ||
234 | |||
235 | struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, | ||
236 | struct drm_encoder *encoder) | ||
237 | { | ||
238 | struct exynos_drm_connector *exynos_connector; | ||
239 | struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | ||
240 | struct drm_connector *connector; | ||
241 | int type; | ||
242 | int err; | ||
243 | |||
244 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
245 | |||
246 | exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL); | ||
247 | if (!exynos_connector) { | ||
248 | DRM_ERROR("failed to allocate connector\n"); | ||
249 | return NULL; | ||
250 | } | ||
251 | |||
252 | connector = &exynos_connector->drm_connector; | ||
253 | |||
254 | switch (manager->display->type) { | ||
255 | case EXYNOS_DISPLAY_TYPE_HDMI: | ||
256 | type = DRM_MODE_CONNECTOR_HDMIA; | ||
257 | break; | ||
258 | default: | ||
259 | type = DRM_MODE_CONNECTOR_Unknown; | ||
260 | break; | ||
261 | } | ||
262 | |||
263 | drm_connector_init(dev, connector, &exynos_connector_funcs, type); | ||
264 | drm_connector_helper_add(connector, &exynos_connector_helper_funcs); | ||
265 | |||
266 | err = drm_sysfs_connector_add(connector); | ||
267 | if (err) | ||
268 | goto err_connector; | ||
269 | |||
270 | connector->encoder = encoder; | ||
271 | err = drm_mode_connector_attach_encoder(connector, encoder); | ||
272 | if (err) { | ||
273 | DRM_ERROR("failed to attach a connector to a encoder\n"); | ||
274 | goto err_sysfs; | ||
275 | } | ||
276 | |||
277 | DRM_DEBUG_KMS("connector has been created\n"); | ||
278 | |||
279 | return connector; | ||
280 | |||
281 | err_sysfs: | ||
282 | drm_sysfs_connector_remove(connector); | ||
283 | err_connector: | ||
284 | drm_connector_cleanup(connector); | ||
285 | kfree(exynos_connector); | ||
286 | return NULL; | ||
287 | } | ||
288 | |||
289 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
290 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
291 | MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); | ||
292 | MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver"); | ||
293 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.h b/drivers/gpu/drm/exynos/exynos_drm_connector.h new file mode 100644 index 000000000000..1c7b2b5b579c --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
3 | * Authors: | ||
4 | * Inki Dae <inki.dae@samsung.com> | ||
5 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
6 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
7 | * | ||
8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
9 | * copy of this software and associated documentation files (the "Software"), | ||
10 | * to deal in the Software without restriction, including without limitation | ||
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
13 | * Software is furnished to do so, subject to the following conditions: | ||
14 | * | ||
15 | * The above copyright notice and this permission notice (including the next | ||
16 | * paragraph) shall be included in all copies or substantial portions of the | ||
17 | * Software. | ||
18 | * | ||
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
22 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
25 | * OTHER DEALINGS IN THE SOFTWARE. | ||
26 | */ | ||
27 | |||
28 | #ifndef _EXYNOS_DRM_CONNECTOR_H_ | ||
29 | #define _EXYNOS_DRM_CONNECTOR_H_ | ||
30 | |||
31 | struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, | ||
32 | struct drm_encoder *encoder); | ||
33 | |||
34 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c new file mode 100644 index 000000000000..661a03571d0c --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* exynos_drm_core.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Author: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #include "drmP.h" | ||
30 | #include "exynos_drm_drv.h" | ||
31 | #include "exynos_drm_encoder.h" | ||
32 | #include "exynos_drm_connector.h" | ||
33 | #include "exynos_drm_fbdev.h" | ||
34 | |||
35 | static DEFINE_MUTEX(exynos_drm_mutex); | ||
36 | static LIST_HEAD(exynos_drm_subdrv_list); | ||
37 | static struct drm_device *drm_dev; | ||
38 | |||
39 | static int exynos_drm_subdrv_probe(struct drm_device *dev, | ||
40 | struct exynos_drm_subdrv *subdrv) | ||
41 | { | ||
42 | struct drm_encoder *encoder; | ||
43 | struct drm_connector *connector; | ||
44 | |||
45 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
46 | |||
47 | if (subdrv->probe) { | ||
48 | int ret; | ||
49 | |||
50 | /* | ||
51 | * this probe callback would be called by sub driver | ||
52 | * after setting of all resources to this sub driver, | ||
53 | * such as clock, irq and register map are done or by load() | ||
54 | * of exynos drm driver. | ||
55 | * | ||
56 | * P.S. note that this driver is considered for modularization. | ||
57 | */ | ||
58 | ret = subdrv->probe(dev, subdrv->manager.dev); | ||
59 | if (ret) | ||
60 | return ret; | ||
61 | } | ||
62 | |||
63 | /* create and initialize a encoder for this sub driver. */ | ||
64 | encoder = exynos_drm_encoder_create(dev, &subdrv->manager, | ||
65 | (1 << MAX_CRTC) - 1); | ||
66 | if (!encoder) { | ||
67 | DRM_ERROR("failed to create encoder\n"); | ||
68 | return -EFAULT; | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * create and initialize a connector for this sub driver and | ||
73 | * attach the encoder created above to the connector. | ||
74 | */ | ||
75 | connector = exynos_drm_connector_create(dev, encoder); | ||
76 | if (!connector) { | ||
77 | DRM_ERROR("failed to create connector\n"); | ||
78 | encoder->funcs->destroy(encoder); | ||
79 | return -EFAULT; | ||
80 | } | ||
81 | |||
82 | subdrv->encoder = encoder; | ||
83 | subdrv->connector = connector; | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static void exynos_drm_subdrv_remove(struct drm_device *dev, | ||
89 | struct exynos_drm_subdrv *subdrv) | ||
90 | { | ||
91 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
92 | |||
93 | if (subdrv->remove) | ||
94 | subdrv->remove(dev); | ||
95 | |||
96 | if (subdrv->encoder) { | ||
97 | struct drm_encoder *encoder = subdrv->encoder; | ||
98 | encoder->funcs->destroy(encoder); | ||
99 | subdrv->encoder = NULL; | ||
100 | } | ||
101 | |||
102 | if (subdrv->connector) { | ||
103 | struct drm_connector *connector = subdrv->connector; | ||
104 | connector->funcs->destroy(connector); | ||
105 | subdrv->connector = NULL; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | int exynos_drm_device_register(struct drm_device *dev) | ||
110 | { | ||
111 | struct exynos_drm_subdrv *subdrv, *n; | ||
112 | int err; | ||
113 | |||
114 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
115 | |||
116 | if (!dev) | ||
117 | return -EINVAL; | ||
118 | |||
119 | if (drm_dev) { | ||
120 | DRM_ERROR("Already drm device were registered\n"); | ||
121 | return -EBUSY; | ||
122 | } | ||
123 | |||
124 | mutex_lock(&exynos_drm_mutex); | ||
125 | list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { | ||
126 | err = exynos_drm_subdrv_probe(dev, subdrv); | ||
127 | if (err) { | ||
128 | DRM_DEBUG("exynos drm subdrv probe failed.\n"); | ||
129 | list_del(&subdrv->list); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | drm_dev = dev; | ||
134 | mutex_unlock(&exynos_drm_mutex); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | EXPORT_SYMBOL_GPL(exynos_drm_device_register); | ||
139 | |||
140 | int exynos_drm_device_unregister(struct drm_device *dev) | ||
141 | { | ||
142 | struct exynos_drm_subdrv *subdrv; | ||
143 | |||
144 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
145 | |||
146 | if (!dev || dev != drm_dev) { | ||
147 | WARN(1, "Unexpected drm device unregister!\n"); | ||
148 | return -EINVAL; | ||
149 | } | ||
150 | |||
151 | mutex_lock(&exynos_drm_mutex); | ||
152 | list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) | ||
153 | exynos_drm_subdrv_remove(dev, subdrv); | ||
154 | |||
155 | drm_dev = NULL; | ||
156 | mutex_unlock(&exynos_drm_mutex); | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); | ||
161 | |||
162 | static int exynos_drm_mode_group_reinit(struct drm_device *dev) | ||
163 | { | ||
164 | struct drm_mode_group *group = &dev->primary->mode_group; | ||
165 | uint32_t *id_list = group->id_list; | ||
166 | int ret; | ||
167 | |||
168 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
169 | |||
170 | ret = drm_mode_group_init_legacy_group(dev, group); | ||
171 | if (ret < 0) | ||
172 | return ret; | ||
173 | |||
174 | kfree(id_list); | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) | ||
179 | { | ||
180 | int err; | ||
181 | |||
182 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
183 | |||
184 | if (!subdrv) | ||
185 | return -EINVAL; | ||
186 | |||
187 | mutex_lock(&exynos_drm_mutex); | ||
188 | if (drm_dev) { | ||
189 | err = exynos_drm_subdrv_probe(drm_dev, subdrv); | ||
190 | if (err) { | ||
191 | DRM_ERROR("failed to probe exynos drm subdrv\n"); | ||
192 | mutex_unlock(&exynos_drm_mutex); | ||
193 | return err; | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * if any specific driver such as fimd or hdmi driver called | ||
198 | * exynos_drm_subdrv_register() later than drm_load(), | ||
199 | * the fb helper should be re-initialized and re-configured. | ||
200 | */ | ||
201 | err = exynos_drm_fbdev_reinit(drm_dev); | ||
202 | if (err) { | ||
203 | DRM_ERROR("failed to reinitialize exynos drm fbdev\n"); | ||
204 | exynos_drm_subdrv_remove(drm_dev, subdrv); | ||
205 | mutex_unlock(&exynos_drm_mutex); | ||
206 | return err; | ||
207 | } | ||
208 | |||
209 | err = exynos_drm_mode_group_reinit(drm_dev); | ||
210 | if (err) { | ||
211 | DRM_ERROR("failed to reinitialize mode group\n"); | ||
212 | exynos_drm_fbdev_fini(drm_dev); | ||
213 | exynos_drm_subdrv_remove(drm_dev, subdrv); | ||
214 | mutex_unlock(&exynos_drm_mutex); | ||
215 | return err; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | subdrv->drm_dev = drm_dev; | ||
220 | |||
221 | list_add_tail(&subdrv->list, &exynos_drm_subdrv_list); | ||
222 | mutex_unlock(&exynos_drm_mutex); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register); | ||
227 | |||
228 | int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) | ||
229 | { | ||
230 | int ret = -EFAULT; | ||
231 | |||
232 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
233 | |||
234 | if (!subdrv) { | ||
235 | DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n"); | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | mutex_lock(&exynos_drm_mutex); | ||
240 | if (drm_dev) { | ||
241 | exynos_drm_subdrv_remove(drm_dev, subdrv); | ||
242 | list_del(&subdrv->list); | ||
243 | |||
244 | /* | ||
245 | * fb helper should be updated once a sub driver is released | ||
246 | * to re-configure crtc and connector and also to re-setup | ||
247 | * drm framebuffer. | ||
248 | */ | ||
249 | ret = exynos_drm_fbdev_reinit(drm_dev); | ||
250 | if (ret < 0) { | ||
251 | DRM_ERROR("failed fb helper reinit.\n"); | ||
252 | goto fail; | ||
253 | } | ||
254 | |||
255 | ret = exynos_drm_mode_group_reinit(drm_dev); | ||
256 | if (ret < 0) { | ||
257 | DRM_ERROR("failed drm mode group reinit.\n"); | ||
258 | goto fail; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | fail: | ||
263 | mutex_unlock(&exynos_drm_mutex); | ||
264 | return ret; | ||
265 | } | ||
266 | EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); | ||
267 | |||
268 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
269 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
270 | MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); | ||
271 | MODULE_DESCRIPTION("Samsung SoC DRM Core Driver"); | ||
272 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c new file mode 100644 index 000000000000..9337e5e2dbb6 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* exynos_drm_crtc.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #include "drmP.h" | ||
30 | #include "drm_crtc_helper.h" | ||
31 | |||
32 | #include "exynos_drm_drv.h" | ||
33 | #include "exynos_drm_fb.h" | ||
34 | #include "exynos_drm_encoder.h" | ||
35 | #include "exynos_drm_buf.h" | ||
36 | |||
37 | #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ | ||
38 | drm_crtc) | ||
39 | |||
40 | /* | ||
41 | * Exynos specific crtc postion structure. | ||
42 | * | ||
43 | * @fb_x: offset x on a framebuffer to be displyed | ||
44 | * - the unit is screen coordinates. | ||
45 | * @fb_y: offset y on a framebuffer to be displayed | ||
46 | * - the unit is screen coordinates. | ||
47 | * @crtc_x: offset x on hardware screen. | ||
48 | * @crtc_y: offset y on hardware screen. | ||
49 | * @crtc_w: width of hardware screen. | ||
50 | * @crtc_h: height of hardware screen. | ||
51 | */ | ||
52 | struct exynos_drm_crtc_pos { | ||
53 | unsigned int fb_x; | ||
54 | unsigned int fb_y; | ||
55 | unsigned int crtc_x; | ||
56 | unsigned int crtc_y; | ||
57 | unsigned int crtc_w; | ||
58 | unsigned int crtc_h; | ||
59 | }; | ||
60 | |||
61 | /* | ||
62 | * Exynos specific crtc structure. | ||
63 | * | ||
64 | * @drm_crtc: crtc object. | ||
65 | * @overlay: contain information common to display controller and hdmi and | ||
66 | * contents of this overlay object would be copied to sub driver size. | ||
67 | * @pipe: a crtc index created at load() with a new crtc object creation | ||
68 | * and the crtc object would be set to private->crtc array | ||
69 | * to get a crtc object corresponding to this pipe from private->crtc | ||
70 | * array when irq interrupt occured. the reason of using this pipe is that | ||
71 | * drm framework doesn't support multiple irq yet. | ||
72 | * we can refer to the crtc to current hardware interrupt occured through | ||
73 | * this pipe value. | ||
74 | */ | ||
75 | struct exynos_drm_crtc { | ||
76 | struct drm_crtc drm_crtc; | ||
77 | struct exynos_drm_overlay overlay; | ||
78 | unsigned int pipe; | ||
79 | }; | ||
80 | |||
81 | static void exynos_drm_crtc_apply(struct drm_crtc *crtc) | ||
82 | { | ||
83 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | ||
84 | struct exynos_drm_overlay *overlay = &exynos_crtc->overlay; | ||
85 | |||
86 | exynos_drm_fn_encoder(crtc, overlay, | ||
87 | exynos_drm_encoder_crtc_mode_set); | ||
88 | exynos_drm_fn_encoder(crtc, NULL, exynos_drm_encoder_crtc_commit); | ||
89 | } | ||
90 | |||
91 | static int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, | ||
92 | struct drm_framebuffer *fb, | ||
93 | struct drm_display_mode *mode, | ||
94 | struct exynos_drm_crtc_pos *pos) | ||
95 | { | ||
96 | struct exynos_drm_buf_entry *entry; | ||
97 | unsigned int actual_w; | ||
98 | unsigned int actual_h; | ||
99 | |||
100 | entry = exynos_drm_fb_get_buf(fb); | ||
101 | if (!entry) { | ||
102 | DRM_LOG_KMS("entry is null.\n"); | ||
103 | return -EFAULT; | ||
104 | } | ||
105 | |||
106 | overlay->paddr = entry->paddr; | ||
107 | overlay->vaddr = entry->vaddr; | ||
108 | |||
109 | DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n", | ||
110 | (unsigned long)overlay->vaddr, | ||
111 | (unsigned long)overlay->paddr); | ||
112 | |||
113 | actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); | ||
114 | actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); | ||
115 | |||
116 | /* set drm framebuffer data. */ | ||
117 | overlay->fb_x = pos->fb_x; | ||
118 | overlay->fb_y = pos->fb_y; | ||
119 | overlay->fb_width = fb->width; | ||
120 | overlay->fb_height = fb->height; | ||
121 | overlay->bpp = fb->bits_per_pixel; | ||
122 | overlay->pitch = fb->pitch; | ||
123 | |||
124 | /* set overlay range to be displayed. */ | ||
125 | overlay->crtc_x = pos->crtc_x; | ||
126 | overlay->crtc_y = pos->crtc_y; | ||
127 | overlay->crtc_width = actual_w; | ||
128 | overlay->crtc_height = actual_h; | ||
129 | |||
130 | /* set drm mode data. */ | ||
131 | overlay->mode_width = mode->hdisplay; | ||
132 | overlay->mode_height = mode->vdisplay; | ||
133 | overlay->refresh = mode->vrefresh; | ||
134 | overlay->scan_flag = mode->flags; | ||
135 | |||
136 | DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", | ||
137 | overlay->crtc_x, overlay->crtc_y, | ||
138 | overlay->crtc_width, overlay->crtc_height); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static int exynos_drm_crtc_update(struct drm_crtc *crtc) | ||
144 | { | ||
145 | struct exynos_drm_crtc *exynos_crtc; | ||
146 | struct exynos_drm_overlay *overlay; | ||
147 | struct exynos_drm_crtc_pos pos; | ||
148 | struct drm_display_mode *mode = &crtc->mode; | ||
149 | struct drm_framebuffer *fb = crtc->fb; | ||
150 | |||
151 | if (!mode || !fb) | ||
152 | return -EINVAL; | ||
153 | |||
154 | exynos_crtc = to_exynos_crtc(crtc); | ||
155 | overlay = &exynos_crtc->overlay; | ||
156 | |||
157 | memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); | ||
158 | |||
159 | /* it means the offset of framebuffer to be displayed. */ | ||
160 | pos.fb_x = crtc->x; | ||
161 | pos.fb_y = crtc->y; | ||
162 | |||
163 | /* OSD position to be displayed. */ | ||
164 | pos.crtc_x = 0; | ||
165 | pos.crtc_y = 0; | ||
166 | pos.crtc_w = fb->width - crtc->x; | ||
167 | pos.crtc_h = fb->height - crtc->y; | ||
168 | |||
169 | return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); | ||
170 | } | ||
171 | |||
172 | static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
173 | { | ||
174 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
175 | |||
176 | /* TODO */ | ||
177 | } | ||
178 | |||
179 | static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) | ||
180 | { | ||
181 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
182 | |||
183 | /* drm framework doesn't check NULL. */ | ||
184 | } | ||
185 | |||
186 | static void exynos_drm_crtc_commit(struct drm_crtc *crtc) | ||
187 | { | ||
188 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
189 | |||
190 | /* drm framework doesn't check NULL. */ | ||
191 | } | ||
192 | |||
193 | static bool | ||
194 | exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc, | ||
195 | struct drm_display_mode *mode, | ||
196 | struct drm_display_mode *adjusted_mode) | ||
197 | { | ||
198 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
199 | |||
200 | /* drm framework doesn't check NULL */ | ||
201 | return true; | ||
202 | } | ||
203 | |||
204 | static int | ||
205 | exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, | ||
206 | struct drm_display_mode *adjusted_mode, int x, int y, | ||
207 | struct drm_framebuffer *old_fb) | ||
208 | { | ||
209 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
210 | |||
211 | mode = adjusted_mode; | ||
212 | |||
213 | return exynos_drm_crtc_update(crtc); | ||
214 | } | ||
215 | |||
216 | static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
217 | struct drm_framebuffer *old_fb) | ||
218 | { | ||
219 | int ret; | ||
220 | |||
221 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
222 | |||
223 | ret = exynos_drm_crtc_update(crtc); | ||
224 | if (ret) | ||
225 | return ret; | ||
226 | |||
227 | exynos_drm_crtc_apply(crtc); | ||
228 | |||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) | ||
233 | { | ||
234 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
235 | /* drm framework doesn't check NULL */ | ||
236 | } | ||
237 | |||
238 | static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { | ||
239 | .dpms = exynos_drm_crtc_dpms, | ||
240 | .prepare = exynos_drm_crtc_prepare, | ||
241 | .commit = exynos_drm_crtc_commit, | ||
242 | .mode_fixup = exynos_drm_crtc_mode_fixup, | ||
243 | .mode_set = exynos_drm_crtc_mode_set, | ||
244 | .mode_set_base = exynos_drm_crtc_mode_set_base, | ||
245 | .load_lut = exynos_drm_crtc_load_lut, | ||
246 | }; | ||
247 | |||
248 | static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, | ||
249 | struct drm_framebuffer *fb, | ||
250 | struct drm_pending_vblank_event *event) | ||
251 | { | ||
252 | struct drm_device *dev = crtc->dev; | ||
253 | struct exynos_drm_private *dev_priv = dev->dev_private; | ||
254 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | ||
255 | struct drm_framebuffer *old_fb = crtc->fb; | ||
256 | int ret = -EINVAL; | ||
257 | |||
258 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
259 | |||
260 | mutex_lock(&dev->struct_mutex); | ||
261 | |||
262 | if (event) { | ||
263 | /* | ||
264 | * the pipe from user always is 0 so we can set pipe number | ||
265 | * of current owner to event. | ||
266 | */ | ||
267 | event->pipe = exynos_crtc->pipe; | ||
268 | |||
269 | list_add_tail(&event->base.link, | ||
270 | &dev_priv->pageflip_event_list); | ||
271 | |||
272 | ret = drm_vblank_get(dev, exynos_crtc->pipe); | ||
273 | if (ret) { | ||
274 | DRM_DEBUG("failed to acquire vblank counter\n"); | ||
275 | list_del(&event->base.link); | ||
276 | |||
277 | goto out; | ||
278 | } | ||
279 | |||
280 | crtc->fb = fb; | ||
281 | ret = exynos_drm_crtc_update(crtc); | ||
282 | if (ret) { | ||
283 | crtc->fb = old_fb; | ||
284 | drm_vblank_put(dev, exynos_crtc->pipe); | ||
285 | list_del(&event->base.link); | ||
286 | |||
287 | goto out; | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * the values related to a buffer of the drm framebuffer | ||
292 | * to be applied should be set at here. because these values | ||
293 | * first, are set to shadow registers and then to | ||
294 | * real registers at vsync front porch period. | ||
295 | */ | ||
296 | exynos_drm_crtc_apply(crtc); | ||
297 | } | ||
298 | out: | ||
299 | mutex_unlock(&dev->struct_mutex); | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) | ||
304 | { | ||
305 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | ||
306 | struct exynos_drm_private *private = crtc->dev->dev_private; | ||
307 | |||
308 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
309 | |||
310 | private->crtc[exynos_crtc->pipe] = NULL; | ||
311 | |||
312 | drm_crtc_cleanup(crtc); | ||
313 | kfree(exynos_crtc); | ||
314 | } | ||
315 | |||
316 | static struct drm_crtc_funcs exynos_crtc_funcs = { | ||
317 | .set_config = drm_crtc_helper_set_config, | ||
318 | .page_flip = exynos_drm_crtc_page_flip, | ||
319 | .destroy = exynos_drm_crtc_destroy, | ||
320 | }; | ||
321 | |||
322 | struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, | ||
323 | struct drm_crtc *crtc) | ||
324 | { | ||
325 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | ||
326 | |||
327 | return &exynos_crtc->overlay; | ||
328 | } | ||
329 | |||
330 | int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) | ||
331 | { | ||
332 | struct exynos_drm_crtc *exynos_crtc; | ||
333 | struct exynos_drm_private *private = dev->dev_private; | ||
334 | struct drm_crtc *crtc; | ||
335 | |||
336 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
337 | |||
338 | exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); | ||
339 | if (!exynos_crtc) { | ||
340 | DRM_ERROR("failed to allocate exynos crtc\n"); | ||
341 | return -ENOMEM; | ||
342 | } | ||
343 | |||
344 | exynos_crtc->pipe = nr; | ||
345 | crtc = &exynos_crtc->drm_crtc; | ||
346 | |||
347 | private->crtc[nr] = crtc; | ||
348 | |||
349 | drm_crtc_init(dev, crtc, &exynos_crtc_funcs); | ||
350 | drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) | ||
356 | { | ||
357 | struct exynos_drm_private *private = dev->dev_private; | ||
358 | |||
359 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
360 | |||
361 | exynos_drm_fn_encoder(private->crtc[crtc], &crtc, | ||
362 | exynos_drm_enable_vblank); | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) | ||
368 | { | ||
369 | struct exynos_drm_private *private = dev->dev_private; | ||
370 | |||
371 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
372 | |||
373 | exynos_drm_fn_encoder(private->crtc[crtc], &crtc, | ||
374 | exynos_drm_disable_vblank); | ||
375 | } | ||
376 | |||
377 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
378 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
379 | MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); | ||
380 | MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver"); | ||
381 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h new file mode 100644 index 000000000000..c584042d6d2c --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* exynos_drm_crtc.h | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #ifndef _EXYNOS_DRM_CRTC_H_ | ||
30 | #define _EXYNOS_DRM_CRTC_H_ | ||
31 | |||
32 | struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, | ||
33 | struct drm_crtc *crtc); | ||
34 | int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr); | ||
35 | int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); | ||
36 | void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); | ||
37 | |||
38 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c new file mode 100644 index 000000000000..83810cbe3c17 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
3 | * Authors: | ||
4 | * Inki Dae <inki.dae@samsung.com> | ||
5 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
6 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
7 | * | ||
8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
9 | * copy of this software and associated documentation files (the "Software"), | ||
10 | * to deal in the Software without restriction, including without limitation | ||
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
13 | * Software is furnished to do so, subject to the following conditions: | ||
14 | * | ||
15 | * The above copyright notice and this permission notice (including the next | ||
16 | * paragraph) shall be included in all copies or substantial portions of the | ||
17 | * Software. | ||
18 | * | ||
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
22 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
25 | * OTHER DEALINGS IN THE SOFTWARE. | ||
26 | */ | ||
27 | |||
28 | #include "drmP.h" | ||
29 | #include "drm.h" | ||
30 | |||
31 | #include <drm/exynos_drm.h> | ||
32 | |||
33 | #include "exynos_drm_drv.h" | ||
34 | #include "exynos_drm_crtc.h" | ||
35 | #include "exynos_drm_fbdev.h" | ||
36 | #include "exynos_drm_fb.h" | ||
37 | #include "exynos_drm_gem.h" | ||
38 | |||
39 | #define DRIVER_NAME "exynos-drm" | ||
40 | #define DRIVER_DESC "Samsung SoC DRM" | ||
41 | #define DRIVER_DATE "20110530" | ||
42 | #define DRIVER_MAJOR 1 | ||
43 | #define DRIVER_MINOR 0 | ||
44 | |||
45 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) | ||
46 | { | ||
47 | struct exynos_drm_private *private; | ||
48 | int ret; | ||
49 | int nr; | ||
50 | |||
51 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
52 | |||
53 | private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); | ||
54 | if (!private) { | ||
55 | DRM_ERROR("failed to allocate private\n"); | ||
56 | return -ENOMEM; | ||
57 | } | ||
58 | |||
59 | INIT_LIST_HEAD(&private->pageflip_event_list); | ||
60 | dev->dev_private = (void *)private; | ||
61 | |||
62 | drm_mode_config_init(dev); | ||
63 | |||
64 | exynos_drm_mode_config_init(dev); | ||
65 | |||
66 | /* | ||
67 | * EXYNOS4 is enough to have two CRTCs and each crtc would be used | ||
68 | * without dependency of hardware. | ||
69 | */ | ||
70 | for (nr = 0; nr < MAX_CRTC; nr++) { | ||
71 | ret = exynos_drm_crtc_create(dev, nr); | ||
72 | if (ret) | ||
73 | goto err_crtc; | ||
74 | } | ||
75 | |||
76 | ret = drm_vblank_init(dev, MAX_CRTC); | ||
77 | if (ret) | ||
78 | goto err_crtc; | ||
79 | |||
80 | /* | ||
81 | * probe sub drivers such as display controller and hdmi driver, | ||
82 | * that were registered at probe() of platform driver | ||
83 | * to the sub driver and create encoder and connector for them. | ||
84 | */ | ||
85 | ret = exynos_drm_device_register(dev); | ||
86 | if (ret) | ||
87 | goto err_vblank; | ||
88 | |||
89 | /* | ||
90 | * create and configure fb helper and also exynos specific | ||
91 | * fbdev object. | ||
92 | */ | ||
93 | ret = exynos_drm_fbdev_init(dev); | ||
94 | if (ret) { | ||
95 | DRM_ERROR("failed to initialize drm fbdev\n"); | ||
96 | goto err_drm_device; | ||
97 | } | ||
98 | |||
99 | return 0; | ||
100 | |||
101 | err_drm_device: | ||
102 | exynos_drm_device_unregister(dev); | ||
103 | err_vblank: | ||
104 | drm_vblank_cleanup(dev); | ||
105 | err_crtc: | ||
106 | drm_mode_config_cleanup(dev); | ||
107 | kfree(private); | ||
108 | |||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | static int exynos_drm_unload(struct drm_device *dev) | ||
113 | { | ||
114 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
115 | |||
116 | exynos_drm_fbdev_fini(dev); | ||
117 | exynos_drm_device_unregister(dev); | ||
118 | drm_vblank_cleanup(dev); | ||
119 | drm_mode_config_cleanup(dev); | ||
120 | kfree(dev->dev_private); | ||
121 | |||
122 | dev->dev_private = NULL; | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static void exynos_drm_preclose(struct drm_device *dev, | ||
128 | struct drm_file *file_priv) | ||
129 | { | ||
130 | struct exynos_drm_private *dev_priv = dev->dev_private; | ||
131 | |||
132 | /* | ||
133 | * drm framework frees all events at release time, | ||
134 | * so private event list should be cleared. | ||
135 | */ | ||
136 | if (!list_empty(&dev_priv->pageflip_event_list)) | ||
137 | INIT_LIST_HEAD(&dev_priv->pageflip_event_list); | ||
138 | } | ||
139 | |||
140 | static void exynos_drm_lastclose(struct drm_device *dev) | ||
141 | { | ||
142 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
143 | |||
144 | exynos_drm_fbdev_restore_mode(dev); | ||
145 | } | ||
146 | |||
147 | static struct vm_operations_struct exynos_drm_gem_vm_ops = { | ||
148 | .fault = exynos_drm_gem_fault, | ||
149 | .open = drm_gem_vm_open, | ||
150 | .close = drm_gem_vm_close, | ||
151 | }; | ||
152 | |||
153 | static struct drm_ioctl_desc exynos_ioctls[] = { | ||
154 | DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, | ||
155 | DRM_UNLOCKED | DRM_AUTH), | ||
156 | DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET, | ||
157 | exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED | | ||
158 | DRM_AUTH), | ||
159 | DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, | ||
160 | exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), | ||
161 | }; | ||
162 | |||
163 | static struct drm_driver exynos_drm_driver = { | ||
164 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM | | ||
165 | DRIVER_MODESET | DRIVER_GEM, | ||
166 | .load = exynos_drm_load, | ||
167 | .unload = exynos_drm_unload, | ||
168 | .preclose = exynos_drm_preclose, | ||
169 | .lastclose = exynos_drm_lastclose, | ||
170 | .get_vblank_counter = drm_vblank_count, | ||
171 | .enable_vblank = exynos_drm_crtc_enable_vblank, | ||
172 | .disable_vblank = exynos_drm_crtc_disable_vblank, | ||
173 | .gem_init_object = exynos_drm_gem_init_object, | ||
174 | .gem_free_object = exynos_drm_gem_free_object, | ||
175 | .gem_vm_ops = &exynos_drm_gem_vm_ops, | ||
176 | .dumb_create = exynos_drm_gem_dumb_create, | ||
177 | .dumb_map_offset = exynos_drm_gem_dumb_map_offset, | ||
178 | .dumb_destroy = exynos_drm_gem_dumb_destroy, | ||
179 | .ioctls = exynos_ioctls, | ||
180 | .fops = { | ||
181 | .owner = THIS_MODULE, | ||
182 | .open = drm_open, | ||
183 | .mmap = exynos_drm_gem_mmap, | ||
184 | .poll = drm_poll, | ||
185 | .read = drm_read, | ||
186 | .unlocked_ioctl = drm_ioctl, | ||
187 | .release = drm_release, | ||
188 | }, | ||
189 | .name = DRIVER_NAME, | ||
190 | .desc = DRIVER_DESC, | ||
191 | .date = DRIVER_DATE, | ||
192 | .major = DRIVER_MAJOR, | ||
193 | .minor = DRIVER_MINOR, | ||
194 | }; | ||
195 | |||
196 | static int exynos_drm_platform_probe(struct platform_device *pdev) | ||
197 | { | ||
198 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
199 | |||
200 | exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); | ||
201 | |||
202 | return drm_platform_init(&exynos_drm_driver, pdev); | ||
203 | } | ||
204 | |||
205 | static int exynos_drm_platform_remove(struct platform_device *pdev) | ||
206 | { | ||
207 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
208 | |||
209 | drm_platform_exit(&exynos_drm_driver, pdev); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static struct platform_driver exynos_drm_platform_driver = { | ||
215 | .probe = exynos_drm_platform_probe, | ||
216 | .remove = __devexit_p(exynos_drm_platform_remove), | ||
217 | .driver = { | ||
218 | .owner = THIS_MODULE, | ||
219 | .name = DRIVER_NAME, | ||
220 | }, | ||
221 | }; | ||
222 | |||
223 | static int __init exynos_drm_init(void) | ||
224 | { | ||
225 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
226 | |||
227 | return platform_driver_register(&exynos_drm_platform_driver); | ||
228 | } | ||
229 | |||
230 | static void __exit exynos_drm_exit(void) | ||
231 | { | ||
232 | DRM_DEBUG_DRIVER("%s\n", __FILE__); | ||
233 | |||
234 | platform_driver_unregister(&exynos_drm_platform_driver); | ||
235 | } | ||
236 | |||
237 | module_init(exynos_drm_init); | ||
238 | module_exit(exynos_drm_exit); | ||
239 | |||
240 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
241 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
242 | MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); | ||
243 | MODULE_DESCRIPTION("Samsung SoC DRM Driver"); | ||
244 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h new file mode 100644 index 000000000000..c03683f2ae72 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h | |||
@@ -0,0 +1,254 @@ | |||
1 | /* exynos_drm_drv.h | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #ifndef _EXYNOS_DRM_DRV_H_ | ||
30 | #define _EXYNOS_DRM_DRV_H_ | ||
31 | |||
32 | #include "drm.h" | ||
33 | |||
34 | #define MAX_CRTC 2 | ||
35 | |||
36 | struct drm_device; | ||
37 | struct exynos_drm_overlay; | ||
38 | struct drm_connector; | ||
39 | |||
40 | /* this enumerates display type. */ | ||
41 | enum exynos_drm_output_type { | ||
42 | EXYNOS_DISPLAY_TYPE_NONE, | ||
43 | /* RGB or CPU Interface. */ | ||
44 | EXYNOS_DISPLAY_TYPE_LCD, | ||
45 | /* HDMI Interface. */ | ||
46 | EXYNOS_DISPLAY_TYPE_HDMI, | ||
47 | }; | ||
48 | |||
49 | /* | ||
50 | * Exynos drm overlay ops structure. | ||
51 | * | ||
52 | * @mode_set: copy drm overlay info to hw specific overlay info. | ||
53 | * @commit: apply hardware specific overlay data to registers. | ||
54 | * @disable: disable hardware specific overlay. | ||
55 | */ | ||
56 | struct exynos_drm_overlay_ops { | ||
57 | void (*mode_set)(struct device *subdrv_dev, | ||
58 | struct exynos_drm_overlay *overlay); | ||
59 | void (*commit)(struct device *subdrv_dev); | ||
60 | void (*disable)(struct device *subdrv_dev); | ||
61 | }; | ||
62 | |||
63 | /* | ||
64 | * Exynos drm common overlay structure. | ||
65 | * | ||
66 | * @fb_x: offset x on a framebuffer to be displayed. | ||
67 | * - the unit is screen coordinates. | ||
68 | * @fb_y: offset y on a framebuffer to be displayed. | ||
69 | * - the unit is screen coordinates. | ||
70 | * @fb_width: width of a framebuffer. | ||
71 | * @fb_height: height of a framebuffer. | ||
72 | * @crtc_x: offset x on hardware screen. | ||
73 | * @crtc_y: offset y on hardware screen. | ||
74 | * @crtc_width: window width to be displayed (hardware screen). | ||
75 | * @crtc_height: window height to be displayed (hardware screen). | ||
76 | * @mode_width: width of screen mode. | ||
77 | * @mode_height: height of screen mode. | ||
78 | * @refresh: refresh rate. | ||
79 | * @scan_flag: interlace or progressive way. | ||
80 | * (it could be DRM_MODE_FLAG_*) | ||
81 | * @bpp: pixel size.(in bit) | ||
82 | * @paddr: bus(accessed by dma) physical memory address to this overlay | ||
83 | * and this is physically continuous. | ||
84 | * @vaddr: virtual memory addresss to this overlay. | ||
85 | * @default_win: a window to be enabled. | ||
86 | * @color_key: color key on or off. | ||
87 | * @index_color: if using color key feature then this value would be used | ||
88 | * as index color. | ||
89 | * @local_path: in case of lcd type, local path mode on or off. | ||
90 | * @transparency: transparency on or off. | ||
91 | * @activated: activated or not. | ||
92 | * | ||
93 | * this structure is common to exynos SoC and its contents would be copied | ||
94 | * to hardware specific overlay info. | ||
95 | */ | ||
96 | struct exynos_drm_overlay { | ||
97 | unsigned int fb_x; | ||
98 | unsigned int fb_y; | ||
99 | unsigned int fb_width; | ||
100 | unsigned int fb_height; | ||
101 | unsigned int crtc_x; | ||
102 | unsigned int crtc_y; | ||
103 | unsigned int crtc_width; | ||
104 | unsigned int crtc_height; | ||
105 | unsigned int mode_width; | ||
106 | unsigned int mode_height; | ||
107 | unsigned int refresh; | ||
108 | unsigned int scan_flag; | ||
109 | unsigned int bpp; | ||
110 | unsigned int pitch; | ||
111 | dma_addr_t paddr; | ||
112 | void __iomem *vaddr; | ||
113 | |||
114 | bool default_win; | ||
115 | bool color_key; | ||
116 | unsigned int index_color; | ||
117 | bool local_path; | ||
118 | bool transparency; | ||
119 | bool activated; | ||
120 | }; | ||
121 | |||
122 | /* | ||
123 | * Exynos DRM Display Structure. | ||
124 | * - this structure is common to analog tv, digital tv and lcd panel. | ||
125 | * | ||
126 | * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. | ||
127 | * @is_connected: check for that display is connected or not. | ||
128 | * @get_edid: get edid modes from display driver. | ||
129 | * @get_timing: get timing object from display driver. | ||
130 | * @check_timing: check if timing is valid or not. | ||
131 | * @power_on: display device on or off. | ||
132 | */ | ||
133 | struct exynos_drm_display { | ||
134 | enum exynos_drm_output_type type; | ||
135 | bool (*is_connected)(struct device *dev); | ||
136 | int (*get_edid)(struct device *dev, struct drm_connector *connector, | ||
137 | u8 *edid, int len); | ||
138 | void *(*get_timing)(struct device *dev); | ||
139 | int (*check_timing)(struct device *dev, void *timing); | ||
140 | int (*power_on)(struct device *dev, int mode); | ||
141 | }; | ||
142 | |||
143 | /* | ||
144 | * Exynos drm manager ops | ||
145 | * | ||
146 | * @mode_set: convert drm_display_mode to hw specific display mode and | ||
147 | * would be called by encoder->mode_set(). | ||
148 | * @commit: set current hw specific display mode to hw. | ||
149 | * @enable_vblank: specific driver callback for enabling vblank interrupt. | ||
150 | * @disable_vblank: specific driver callback for disabling vblank interrupt. | ||
151 | */ | ||
152 | struct exynos_drm_manager_ops { | ||
153 | void (*mode_set)(struct device *subdrv_dev, void *mode); | ||
154 | void (*commit)(struct device *subdrv_dev); | ||
155 | int (*enable_vblank)(struct device *subdrv_dev); | ||
156 | void (*disable_vblank)(struct device *subdrv_dev); | ||
157 | }; | ||
158 | |||
159 | /* | ||
160 | * Exynos drm common manager structure. | ||
161 | * | ||
162 | * @dev: pointer to device object for subdrv device driver. | ||
163 | * sub drivers such as display controller or hdmi driver, | ||
164 | * have their own device object. | ||
165 | * @ops: pointer to callbacks for exynos drm specific framebuffer. | ||
166 | * these callbacks should be set by specific drivers such fimd | ||
167 | * or hdmi driver and are used to control hardware global registers. | ||
168 | * @overlay_ops: pointer to callbacks for exynos drm specific framebuffer. | ||
169 | * these callbacks should be set by specific drivers such fimd | ||
170 | * or hdmi driver and are used to control hardware overlay reigsters. | ||
171 | * @display: pointer to callbacks for exynos drm specific framebuffer. | ||
172 | * these callbacks should be set by specific drivers such fimd | ||
173 | * or hdmi driver and are used to control display devices such as | ||
174 | * analog tv, digital tv and lcd panel and also get timing data for them. | ||
175 | */ | ||
176 | struct exynos_drm_manager { | ||
177 | struct device *dev; | ||
178 | int pipe; | ||
179 | struct exynos_drm_manager_ops *ops; | ||
180 | struct exynos_drm_overlay_ops *overlay_ops; | ||
181 | struct exynos_drm_display *display; | ||
182 | }; | ||
183 | |||
184 | /* | ||
185 | * Exynos drm private structure. | ||
186 | */ | ||
187 | struct exynos_drm_private { | ||
188 | struct drm_fb_helper *fb_helper; | ||
189 | |||
190 | /* list head for new event to be added. */ | ||
191 | struct list_head pageflip_event_list; | ||
192 | |||
193 | /* | ||
194 | * created crtc object would be contained at this array and | ||
195 | * this array is used to be aware of which crtc did it request vblank. | ||
196 | */ | ||
197 | struct drm_crtc *crtc[MAX_CRTC]; | ||
198 | }; | ||
199 | |||
200 | /* | ||
201 | * Exynos drm sub driver structure. | ||
202 | * | ||
203 | * @list: sub driver has its own list object to register to exynos drm driver. | ||
204 | * @drm_dev: pointer to drm_device and this pointer would be set | ||
205 | * when sub driver calls exynos_drm_subdrv_register(). | ||
206 | * @probe: this callback would be called by exynos drm driver after | ||
207 | * subdrv is registered to it. | ||
208 | * @remove: this callback is used to release resources created | ||
209 | * by probe callback. | ||
210 | * @manager: subdrv has its own manager to control a hardware appropriately | ||
211 | * and we can access a hardware drawing on this manager. | ||
212 | * @encoder: encoder object owned by this sub driver. | ||
213 | * @connector: connector object owned by this sub driver. | ||
214 | */ | ||
215 | struct exynos_drm_subdrv { | ||
216 | struct list_head list; | ||
217 | struct drm_device *drm_dev; | ||
218 | |||
219 | int (*probe)(struct drm_device *drm_dev, struct device *dev); | ||
220 | void (*remove)(struct drm_device *dev); | ||
221 | |||
222 | struct exynos_drm_manager manager; | ||
223 | struct drm_encoder *encoder; | ||
224 | struct drm_connector *connector; | ||
225 | }; | ||
226 | |||
227 | /* | ||
228 | * this function calls a probe callback registered to sub driver list and | ||
229 | * create its own encoder and connector and then set drm_device object | ||
230 | * to global one. | ||
231 | */ | ||
232 | int exynos_drm_device_register(struct drm_device *dev); | ||
233 | /* | ||
234 | * this function calls a remove callback registered to sub driver list and | ||
235 | * destroy its own encoder and connetor. | ||
236 | */ | ||
237 | int exynos_drm_device_unregister(struct drm_device *dev); | ||
238 | |||
239 | /* | ||
240 | * this function would be called by sub drivers such as display controller | ||
241 | * or hdmi driver to register this sub driver object to exynos drm driver | ||
242 | * and when a sub driver is registered to exynos drm driver a probe callback | ||
243 | * of the sub driver is called and creates its own encoder and connector | ||
244 | * and then fb helper and drm mode group would be re-initialized. | ||
245 | */ | ||
246 | int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv); | ||
247 | |||
248 | /* | ||
249 | * this function removes subdrv list from exynos drm driver and fb helper | ||
250 | * and drm mode group would be re-initialized. | ||
251 | */ | ||
252 | int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv); | ||
253 | |||
254 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c new file mode 100644 index 000000000000..7cf6fa86a67e --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* exynos_drm_encoder.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #include "drmP.h" | ||
30 | #include "drm_crtc_helper.h" | ||
31 | |||
32 | #include "exynos_drm_drv.h" | ||
33 | #include "exynos_drm_crtc.h" | ||
34 | #include "exynos_drm_encoder.h" | ||
35 | |||
36 | #define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ | ||
37 | drm_encoder) | ||
38 | |||
39 | /* | ||
40 | * exynos specific encoder structure. | ||
41 | * | ||
42 | * @drm_encoder: encoder object. | ||
43 | * @manager: specific encoder has its own manager to control a hardware | ||
44 | * appropriately and we can access a hardware drawing on this manager. | ||
45 | */ | ||
46 | struct exynos_drm_encoder { | ||
47 | struct drm_encoder drm_encoder; | ||
48 | struct exynos_drm_manager *manager; | ||
49 | }; | ||
50 | |||
51 | static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
52 | { | ||
53 | struct drm_device *dev = encoder->dev; | ||
54 | struct drm_connector *connector; | ||
55 | struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | ||
56 | |||
57 | DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode); | ||
58 | |||
59 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
60 | if (connector->encoder == encoder) { | ||
61 | struct exynos_drm_display *display = manager->display; | ||
62 | |||
63 | if (display && display->power_on) | ||
64 | display->power_on(manager->dev, mode); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | static bool | ||
70 | exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, | ||
71 | struct drm_display_mode *mode, | ||
72 | struct drm_display_mode *adjusted_mode) | ||
73 | { | ||
74 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
75 | |||
76 | /* drm framework doesn't check NULL. */ | ||
77 | |||
78 | return true; | ||
79 | } | ||
80 | |||
81 | static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, | ||
82 | struct drm_display_mode *mode, | ||
83 | struct drm_display_mode *adjusted_mode) | ||
84 | { | ||
85 | struct drm_device *dev = encoder->dev; | ||
86 | struct drm_connector *connector; | ||
87 | struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | ||
88 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | ||
89 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | ||
90 | struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev, | ||
91 | encoder->crtc); | ||
92 | |||
93 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
94 | |||
95 | mode = adjusted_mode; | ||
96 | |||
97 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
98 | if (connector->encoder == encoder) { | ||
99 | if (manager_ops && manager_ops->mode_set) | ||
100 | manager_ops->mode_set(manager->dev, mode); | ||
101 | |||
102 | if (overlay_ops && overlay_ops->mode_set) | ||
103 | overlay_ops->mode_set(manager->dev, overlay); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) | ||
109 | { | ||
110 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
111 | |||
112 | /* drm framework doesn't check NULL. */ | ||
113 | } | ||
114 | |||
115 | static void exynos_drm_encoder_commit(struct drm_encoder *encoder) | ||
116 | { | ||
117 | struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | ||
118 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | ||
119 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | ||
120 | |||
121 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
122 | |||
123 | if (manager_ops && manager_ops->commit) | ||
124 | manager_ops->commit(manager->dev); | ||
125 | |||
126 | if (overlay_ops && overlay_ops->commit) | ||
127 | overlay_ops->commit(manager->dev); | ||
128 | } | ||
129 | |||
130 | static struct drm_crtc * | ||
131 | exynos_drm_encoder_get_crtc(struct drm_encoder *encoder) | ||
132 | { | ||
133 | return encoder->crtc; | ||
134 | } | ||
135 | |||
136 | static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { | ||
137 | .dpms = exynos_drm_encoder_dpms, | ||
138 | .mode_fixup = exynos_drm_encoder_mode_fixup, | ||
139 | .mode_set = exynos_drm_encoder_mode_set, | ||
140 | .prepare = exynos_drm_encoder_prepare, | ||
141 | .commit = exynos_drm_encoder_commit, | ||
142 | .get_crtc = exynos_drm_encoder_get_crtc, | ||
143 | }; | ||
144 | |||
145 | static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) | ||
146 | { | ||
147 | struct exynos_drm_encoder *exynos_encoder = | ||
148 | to_exynos_encoder(encoder); | ||
149 | |||
150 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
151 | |||
152 | exynos_encoder->manager->pipe = -1; | ||
153 | |||
154 | drm_encoder_cleanup(encoder); | ||
155 | encoder->dev->mode_config.num_encoder--; | ||
156 | kfree(exynos_encoder); | ||
157 | } | ||
158 | |||
159 | static struct drm_encoder_funcs exynos_encoder_funcs = { | ||
160 | .destroy = exynos_drm_encoder_destroy, | ||
161 | }; | ||
162 | |||
163 | struct drm_encoder * | ||
164 | exynos_drm_encoder_create(struct drm_device *dev, | ||
165 | struct exynos_drm_manager *manager, | ||
166 | unsigned int possible_crtcs) | ||
167 | { | ||
168 | struct drm_encoder *encoder; | ||
169 | struct exynos_drm_encoder *exynos_encoder; | ||
170 | |||
171 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
172 | |||
173 | if (!manager || !possible_crtcs) | ||
174 | return NULL; | ||
175 | |||
176 | if (!manager->dev) | ||
177 | return NULL; | ||
178 | |||
179 | exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); | ||
180 | if (!exynos_encoder) { | ||
181 | DRM_ERROR("failed to allocate encoder\n"); | ||
182 | return NULL; | ||
183 | } | ||
184 | |||
185 | exynos_encoder->manager = manager; | ||
186 | encoder = &exynos_encoder->drm_encoder; | ||
187 | encoder->possible_crtcs = possible_crtcs; | ||
188 | |||
189 | DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); | ||
190 | |||
191 | drm_encoder_init(dev, encoder, &exynos_encoder_funcs, | ||
192 | DRM_MODE_ENCODER_TMDS); | ||
193 | |||
194 | drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); | ||
195 | |||
196 | DRM_DEBUG_KMS("encoder has been created\n"); | ||
197 | |||
198 | return encoder; | ||
199 | } | ||
200 | |||
201 | struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder) | ||
202 | { | ||
203 | return to_exynos_encoder(encoder)->manager; | ||
204 | } | ||
205 | |||
206 | void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, | ||
207 | void (*fn)(struct drm_encoder *, void *)) | ||
208 | { | ||
209 | struct drm_device *dev = crtc->dev; | ||
210 | struct drm_encoder *encoder; | ||
211 | |||
212 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
213 | if (encoder->crtc != crtc) | ||
214 | continue; | ||
215 | |||
216 | fn(encoder, data); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) | ||
221 | { | ||
222 | struct exynos_drm_manager *manager = | ||
223 | to_exynos_encoder(encoder)->manager; | ||
224 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | ||
225 | int crtc = *(int *)data; | ||
226 | |||
227 | if (manager->pipe == -1) | ||
228 | manager->pipe = crtc; | ||
229 | |||
230 | if (manager_ops->enable_vblank) | ||
231 | manager_ops->enable_vblank(manager->dev); | ||
232 | } | ||
233 | |||
234 | void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) | ||
235 | { | ||
236 | struct exynos_drm_manager *manager = | ||
237 | to_exynos_encoder(encoder)->manager; | ||
238 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | ||
239 | int crtc = *(int *)data; | ||
240 | |||
241 | if (manager->pipe == -1) | ||
242 | manager->pipe = crtc; | ||
243 | |||
244 | if (manager_ops->disable_vblank) | ||
245 | manager_ops->disable_vblank(manager->dev); | ||
246 | } | ||
247 | |||
248 | void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) | ||
249 | { | ||
250 | struct exynos_drm_manager *manager = | ||
251 | to_exynos_encoder(encoder)->manager; | ||
252 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | ||
253 | |||
254 | overlay_ops->commit(manager->dev); | ||
255 | } | ||
256 | |||
257 | void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) | ||
258 | { | ||
259 | struct exynos_drm_manager *manager = | ||
260 | to_exynos_encoder(encoder)->manager; | ||
261 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | ||
262 | struct exynos_drm_overlay *overlay = data; | ||
263 | |||
264 | overlay_ops->mode_set(manager->dev, overlay); | ||
265 | } | ||
266 | |||
267 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
268 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
269 | MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); | ||
270 | MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver"); | ||
271 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h new file mode 100644 index 000000000000..5ecd645d06a9 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
3 | * Authors: | ||
4 | * Inki Dae <inki.dae@samsung.com> | ||
5 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
6 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
7 | * | ||
8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
9 | * copy of this software and associated documentation files (the "Software"), | ||
10 | * to deal in the Software without restriction, including without limitation | ||
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
13 | * Software is furnished to do so, subject to the following conditions: | ||
14 | * | ||
15 | * The above copyright notice and this permission notice (including the next | ||
16 | * paragraph) shall be included in all copies or substantial portions of the | ||
17 | * Software. | ||
18 | * | ||
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
22 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
25 | * OTHER DEALINGS IN THE SOFTWARE. | ||
26 | */ | ||
27 | |||
28 | #ifndef _EXYNOS_DRM_ENCODER_H_ | ||
29 | #define _EXYNOS_DRM_ENCODER_H_ | ||
30 | |||
31 | struct exynos_drm_manager; | ||
32 | |||
33 | struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev, | ||
34 | struct exynos_drm_manager *mgr, | ||
35 | unsigned int possible_crtcs); | ||
36 | struct exynos_drm_manager * | ||
37 | exynos_drm_get_manager(struct drm_encoder *encoder); | ||
38 | void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, | ||
39 | void (*fn)(struct drm_encoder *, void *)); | ||
40 | void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data); | ||
41 | void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data); | ||
42 | void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data); | ||
43 | void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data); | ||
44 | |||
45 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c new file mode 100644 index 000000000000..48d29cfd5240 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* exynos_drm_fb.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #include "drmP.h" | ||
30 | #include "drm_crtc.h" | ||
31 | #include "drm_crtc_helper.h" | ||
32 | |||
33 | #include "exynos_drm_fb.h" | ||
34 | #include "exynos_drm_buf.h" | ||
35 | #include "exynos_drm_gem.h" | ||
36 | |||
37 | #define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) | ||
38 | |||
39 | /* | ||
40 | * exynos specific framebuffer structure. | ||
41 | * | ||
42 | * @fb: drm framebuffer obejct. | ||
43 | * @exynos_gem_obj: exynos specific gem object containing a gem object. | ||
44 | * @entry: pointer to exynos drm buffer entry object. | ||
45 | * - containing only the information to physically continuous memory | ||
46 | * region allocated at default framebuffer creation. | ||
47 | */ | ||
48 | struct exynos_drm_fb { | ||
49 | struct drm_framebuffer fb; | ||
50 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
51 | struct exynos_drm_buf_entry *entry; | ||
52 | }; | ||
53 | |||
54 | static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) | ||
55 | { | ||
56 | struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); | ||
57 | |||
58 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
59 | |||
60 | drm_framebuffer_cleanup(fb); | ||
61 | |||
62 | /* | ||
63 | * default framebuffer has no gem object so | ||
64 | * a buffer of the default framebuffer should be released at here. | ||
65 | */ | ||
66 | if (!exynos_fb->exynos_gem_obj && exynos_fb->entry) | ||
67 | exynos_drm_buf_destroy(fb->dev, exynos_fb->entry); | ||
68 | |||
69 | kfree(exynos_fb); | ||
70 | exynos_fb = NULL; | ||
71 | } | ||
72 | |||
73 | static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, | ||
74 | struct drm_file *file_priv, | ||
75 | unsigned int *handle) | ||
76 | { | ||
77 | struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); | ||
78 | |||
79 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
80 | |||
81 | return drm_gem_handle_create(file_priv, | ||
82 | &exynos_fb->exynos_gem_obj->base, handle); | ||
83 | } | ||
84 | |||
85 | static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, | ||
86 | struct drm_file *file_priv, unsigned flags, | ||
87 | unsigned color, struct drm_clip_rect *clips, | ||
88 | unsigned num_clips) | ||
89 | { | ||
90 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
91 | |||
92 | /* TODO */ | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { | ||
98 | .destroy = exynos_drm_fb_destroy, | ||
99 | .create_handle = exynos_drm_fb_create_handle, | ||
100 | .dirty = exynos_drm_fb_dirty, | ||
101 | }; | ||
102 | |||
103 | static struct drm_framebuffer * | ||
104 | exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, | ||
105 | struct drm_mode_fb_cmd *mode_cmd) | ||
106 | { | ||
107 | struct exynos_drm_fb *exynos_fb; | ||
108 | struct drm_framebuffer *fb; | ||
109 | struct exynos_drm_gem_obj *exynos_gem_obj = NULL; | ||
110 | struct drm_gem_object *obj; | ||
111 | unsigned int size; | ||
112 | int ret; | ||
113 | |||
114 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
115 | |||
116 | mode_cmd->pitch = max(mode_cmd->pitch, | ||
117 | mode_cmd->width * (mode_cmd->bpp >> 3)); | ||
118 | |||
119 | DRM_LOG_KMS("drm fb create(%dx%d)\n", | ||
120 | mode_cmd->width, mode_cmd->height); | ||
121 | |||
122 | exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); | ||
123 | if (!exynos_fb) { | ||
124 | DRM_ERROR("failed to allocate exynos drm framebuffer.\n"); | ||
125 | return ERR_PTR(-ENOMEM); | ||
126 | } | ||
127 | |||
128 | fb = &exynos_fb->fb; | ||
129 | ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs); | ||
130 | if (ret) { | ||
131 | DRM_ERROR("failed to initialize framebuffer.\n"); | ||
132 | goto err_init; | ||
133 | } | ||
134 | |||
135 | DRM_LOG_KMS("create: fb id: %d\n", fb->base.id); | ||
136 | |||
137 | size = mode_cmd->pitch * mode_cmd->height; | ||
138 | |||
139 | /* | ||
140 | * mode_cmd->handle could be NULL at booting time or | ||
141 | * with user request. if NULL, a new buffer or a gem object | ||
142 | * would be allocated. | ||
143 | */ | ||
144 | if (!mode_cmd->handle) { | ||
145 | if (!file_priv) { | ||
146 | struct exynos_drm_buf_entry *entry; | ||
147 | |||
148 | /* | ||
149 | * in case that file_priv is NULL, it allocates | ||
150 | * only buffer and this buffer would be used | ||
151 | * for default framebuffer. | ||
152 | */ | ||
153 | entry = exynos_drm_buf_create(dev, size); | ||
154 | if (IS_ERR(entry)) { | ||
155 | ret = PTR_ERR(entry); | ||
156 | goto err_buffer; | ||
157 | } | ||
158 | |||
159 | exynos_fb->entry = entry; | ||
160 | |||
161 | DRM_LOG_KMS("default fb: paddr = 0x%lx, size = 0x%x\n", | ||
162 | (unsigned long)entry->paddr, size); | ||
163 | |||
164 | goto out; | ||
165 | } else { | ||
166 | exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, | ||
167 | size, | ||
168 | &mode_cmd->handle); | ||
169 | if (IS_ERR(exynos_gem_obj)) { | ||
170 | ret = PTR_ERR(exynos_gem_obj); | ||
171 | goto err_buffer; | ||
172 | } | ||
173 | } | ||
174 | } else { | ||
175 | obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); | ||
176 | if (!obj) { | ||
177 | DRM_ERROR("failed to lookup gem object.\n"); | ||
178 | goto err_buffer; | ||
179 | } | ||
180 | |||
181 | exynos_gem_obj = to_exynos_gem_obj(obj); | ||
182 | |||
183 | drm_gem_object_unreference_unlocked(obj); | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * if got a exynos_gem_obj from either a handle or | ||
188 | * a new creation then exynos_fb->exynos_gem_obj is NULL | ||
189 | * so that default framebuffer has no its own gem object, | ||
190 | * only its own buffer object. | ||
191 | */ | ||
192 | exynos_fb->entry = exynos_gem_obj->entry; | ||
193 | |||
194 | DRM_LOG_KMS("paddr = 0x%lx, size = 0x%x, gem object = 0x%x\n", | ||
195 | (unsigned long)exynos_fb->entry->paddr, size, | ||
196 | (unsigned int)&exynos_gem_obj->base); | ||
197 | |||
198 | out: | ||
199 | exynos_fb->exynos_gem_obj = exynos_gem_obj; | ||
200 | |||
201 | drm_helper_mode_fill_fb_struct(fb, mode_cmd); | ||
202 | |||
203 | return fb; | ||
204 | |||
205 | err_buffer: | ||
206 | drm_framebuffer_cleanup(fb); | ||
207 | |||
208 | err_init: | ||
209 | kfree(exynos_fb); | ||
210 | |||
211 | return ERR_PTR(ret); | ||
212 | } | ||
213 | |||
214 | struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, | ||
215 | struct drm_file *file_priv, | ||
216 | struct drm_mode_fb_cmd *mode_cmd) | ||
217 | { | ||
218 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
219 | |||
220 | return exynos_drm_fb_init(file_priv, dev, mode_cmd); | ||
221 | } | ||
222 | |||
223 | struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb) | ||
224 | { | ||
225 | struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); | ||
226 | struct exynos_drm_buf_entry *entry; | ||
227 | |||
228 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
229 | |||
230 | entry = exynos_fb->entry; | ||
231 | if (!entry) | ||
232 | return NULL; | ||
233 | |||
234 | DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n", | ||
235 | (unsigned long)entry->vaddr, | ||
236 | (unsigned long)entry->paddr); | ||
237 | |||
238 | return entry; | ||
239 | } | ||
240 | |||
241 | static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { | ||
242 | .fb_create = exynos_drm_fb_create, | ||
243 | }; | ||
244 | |||
245 | void exynos_drm_mode_config_init(struct drm_device *dev) | ||
246 | { | ||
247 | dev->mode_config.min_width = 0; | ||
248 | dev->mode_config.min_height = 0; | ||
249 | |||
250 | /* | ||
251 | * set max width and height as default value(4096x4096). | ||
252 | * this value would be used to check framebuffer size limitation | ||
253 | * at drm_mode_addfb(). | ||
254 | */ | ||
255 | dev->mode_config.max_width = 4096; | ||
256 | dev->mode_config.max_height = 4096; | ||
257 | |||
258 | dev->mode_config.funcs = &exynos_drm_mode_config_funcs; | ||
259 | } | ||
260 | |||
261 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
262 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
263 | MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); | ||
264 | MODULE_DESCRIPTION("Samsung SoC DRM FB Driver"); | ||
265 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h new file mode 100644 index 000000000000..eb35931d302c --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
3 | * Authors: | ||
4 | * Inki Dae <inki.dae@samsung.com> | ||
5 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
6 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
7 | * | ||
8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
9 | * copy of this software and associated documentation files (the "Software"), | ||
10 | * to deal in the Software without restriction, including without limitation | ||
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
13 | * Software is furnished to do so, subject to the following conditions: | ||
14 | * | ||
15 | * The above copyright notice and this permission notice (including the next | ||
16 | * paragraph) shall be included in all copies or substantial portions of the | ||
17 | * Software. | ||
18 | * | ||
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
22 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
25 | * OTHER DEALINGS IN THE SOFTWARE. | ||
26 | */ | ||
27 | |||
28 | #ifndef _EXYNOS_DRM_FB_H_ | ||
29 | #define _EXYNOS_DRM_FB_H | ||
30 | |||
31 | struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, | ||
32 | struct drm_file *filp, | ||
33 | struct drm_mode_fb_cmd *mode_cmd); | ||
34 | |||
35 | void exynos_drm_mode_config_init(struct drm_device *dev); | ||
36 | |||
37 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c new file mode 100644 index 000000000000..1f4b3d1a7713 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c | |||
@@ -0,0 +1,456 @@ | |||
1 | /* exynos_drm_fbdev.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #include "drmP.h" | ||
30 | #include "drm_crtc.h" | ||
31 | #include "drm_fb_helper.h" | ||
32 | #include "drm_crtc_helper.h" | ||
33 | |||
34 | #include "exynos_drm_drv.h" | ||
35 | #include "exynos_drm_fb.h" | ||
36 | #include "exynos_drm_buf.h" | ||
37 | |||
38 | #define MAX_CONNECTOR 4 | ||
39 | #define PREFERRED_BPP 32 | ||
40 | |||
41 | #define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\ | ||
42 | drm_fb_helper) | ||
43 | |||
44 | struct exynos_drm_fbdev { | ||
45 | struct drm_fb_helper drm_fb_helper; | ||
46 | struct drm_framebuffer *fb; | ||
47 | }; | ||
48 | |||
49 | static int exynos_drm_fbdev_set_par(struct fb_info *info) | ||
50 | { | ||
51 | struct fb_var_screeninfo *var = &info->var; | ||
52 | |||
53 | switch (var->bits_per_pixel) { | ||
54 | case 32: | ||
55 | case 24: | ||
56 | case 18: | ||
57 | case 16: | ||
58 | case 12: | ||
59 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
60 | break; | ||
61 | case 1: | ||
62 | info->fix.visual = FB_VISUAL_MONO01; | ||
63 | break; | ||
64 | default: | ||
65 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
66 | break; | ||
67 | } | ||
68 | |||
69 | info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; | ||
70 | |||
71 | return drm_fb_helper_set_par(info); | ||
72 | } | ||
73 | |||
74 | |||
75 | static struct fb_ops exynos_drm_fb_ops = { | ||
76 | .owner = THIS_MODULE, | ||
77 | .fb_fillrect = cfb_fillrect, | ||
78 | .fb_copyarea = cfb_copyarea, | ||
79 | .fb_imageblit = cfb_imageblit, | ||
80 | .fb_check_var = drm_fb_helper_check_var, | ||
81 | .fb_set_par = exynos_drm_fbdev_set_par, | ||
82 | .fb_blank = drm_fb_helper_blank, | ||
83 | .fb_pan_display = drm_fb_helper_pan_display, | ||
84 | .fb_setcmap = drm_fb_helper_setcmap, | ||
85 | }; | ||
86 | |||
87 | static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, | ||
88 | struct drm_framebuffer *fb, | ||
89 | unsigned int fb_width, | ||
90 | unsigned int fb_height) | ||
91 | { | ||
92 | struct fb_info *fbi = helper->fbdev; | ||
93 | struct drm_device *dev = helper->dev; | ||
94 | struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper); | ||
95 | struct exynos_drm_buf_entry *entry; | ||
96 | unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3); | ||
97 | unsigned long offset; | ||
98 | |||
99 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
100 | |||
101 | exynos_fb->fb = fb; | ||
102 | |||
103 | drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth); | ||
104 | drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height); | ||
105 | |||
106 | entry = exynos_drm_fb_get_buf(fb); | ||
107 | if (!entry) { | ||
108 | DRM_LOG_KMS("entry is null.\n"); | ||
109 | return -EFAULT; | ||
110 | } | ||
111 | |||
112 | offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); | ||
113 | offset += fbi->var.yoffset * fb->pitch; | ||
114 | |||
115 | dev->mode_config.fb_base = entry->paddr; | ||
116 | fbi->screen_base = entry->vaddr + offset; | ||
117 | fbi->fix.smem_start = entry->paddr + offset; | ||
118 | fbi->screen_size = size; | ||
119 | fbi->fix.smem_len = size; | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | ||
125 | struct drm_fb_helper_surface_size *sizes) | ||
126 | { | ||
127 | struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); | ||
128 | struct drm_device *dev = helper->dev; | ||
129 | struct fb_info *fbi; | ||
130 | struct drm_mode_fb_cmd mode_cmd = { 0 }; | ||
131 | struct platform_device *pdev = dev->platformdev; | ||
132 | int ret; | ||
133 | |||
134 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
135 | |||
136 | DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", | ||
137 | sizes->surface_width, sizes->surface_height, | ||
138 | sizes->surface_bpp); | ||
139 | |||
140 | mode_cmd.width = sizes->surface_width; | ||
141 | mode_cmd.height = sizes->surface_height; | ||
142 | mode_cmd.bpp = sizes->surface_bpp; | ||
143 | mode_cmd.depth = sizes->surface_depth; | ||
144 | |||
145 | mutex_lock(&dev->struct_mutex); | ||
146 | |||
147 | fbi = framebuffer_alloc(0, &pdev->dev); | ||
148 | if (!fbi) { | ||
149 | DRM_ERROR("failed to allocate fb info.\n"); | ||
150 | ret = -ENOMEM; | ||
151 | goto out; | ||
152 | } | ||
153 | |||
154 | exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd); | ||
155 | if (IS_ERR_OR_NULL(exynos_fbdev->fb)) { | ||
156 | DRM_ERROR("failed to create drm framebuffer.\n"); | ||
157 | ret = PTR_ERR(exynos_fbdev->fb); | ||
158 | goto out; | ||
159 | } | ||
160 | |||
161 | helper->fb = exynos_fbdev->fb; | ||
162 | helper->fbdev = fbi; | ||
163 | |||
164 | fbi->par = helper; | ||
165 | fbi->flags = FBINFO_FLAG_DEFAULT; | ||
166 | fbi->fbops = &exynos_drm_fb_ops; | ||
167 | |||
168 | ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | ||
169 | if (ret) { | ||
170 | DRM_ERROR("failed to allocate cmap.\n"); | ||
171 | goto out; | ||
172 | } | ||
173 | |||
174 | ret = exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width, | ||
175 | sizes->fb_height); | ||
176 | if (ret < 0) | ||
177 | fb_dealloc_cmap(&fbi->cmap); | ||
178 | |||
179 | /* | ||
180 | * if failed, all resources allocated above would be released by | ||
181 | * drm_mode_config_cleanup() when drm_load() had been called prior | ||
182 | * to any specific driver such as fimd or hdmi driver. | ||
183 | */ | ||
184 | out: | ||
185 | mutex_unlock(&dev->struct_mutex); | ||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | static bool | ||
190 | exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb, | ||
191 | struct drm_fb_helper_surface_size *sizes) | ||
192 | { | ||
193 | if (fb->width != sizes->surface_width) | ||
194 | return false; | ||
195 | if (fb->height != sizes->surface_height) | ||
196 | return false; | ||
197 | if (fb->bits_per_pixel != sizes->surface_bpp) | ||
198 | return false; | ||
199 | if (fb->depth != sizes->surface_depth) | ||
200 | return false; | ||
201 | |||
202 | return true; | ||
203 | } | ||
204 | |||
205 | static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, | ||
206 | struct drm_fb_helper_surface_size *sizes) | ||
207 | { | ||
208 | struct drm_device *dev = helper->dev; | ||
209 | struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); | ||
210 | struct drm_framebuffer *fb = exynos_fbdev->fb; | ||
211 | struct drm_mode_fb_cmd mode_cmd = { 0 }; | ||
212 | |||
213 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
214 | |||
215 | if (helper->fb != fb) { | ||
216 | DRM_ERROR("drm framebuffer is different\n"); | ||
217 | return -EINVAL; | ||
218 | } | ||
219 | |||
220 | if (exynos_drm_fbdev_is_samefb(fb, sizes)) | ||
221 | return 0; | ||
222 | |||
223 | mode_cmd.width = sizes->surface_width; | ||
224 | mode_cmd.height = sizes->surface_height; | ||
225 | mode_cmd.bpp = sizes->surface_bpp; | ||
226 | mode_cmd.depth = sizes->surface_depth; | ||
227 | |||
228 | if (fb->funcs->destroy) | ||
229 | fb->funcs->destroy(fb); | ||
230 | |||
231 | exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd); | ||
232 | if (IS_ERR(exynos_fbdev->fb)) { | ||
233 | DRM_ERROR("failed to allocate fb.\n"); | ||
234 | return PTR_ERR(exynos_fbdev->fb); | ||
235 | } | ||
236 | |||
237 | helper->fb = exynos_fbdev->fb; | ||
238 | return exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width, | ||
239 | sizes->fb_height); | ||
240 | } | ||
241 | |||
242 | static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, | ||
243 | struct drm_fb_helper_surface_size *sizes) | ||
244 | { | ||
245 | int ret = 0; | ||
246 | |||
247 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
248 | |||
249 | if (!helper->fb) { | ||
250 | ret = exynos_drm_fbdev_create(helper, sizes); | ||
251 | if (ret < 0) { | ||
252 | DRM_ERROR("failed to create fbdev.\n"); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * fb_helper expects a value more than 1 if succeed | ||
258 | * because register_framebuffer() should be called. | ||
259 | */ | ||
260 | ret = 1; | ||
261 | } else { | ||
262 | ret = exynos_drm_fbdev_recreate(helper, sizes); | ||
263 | if (ret < 0) { | ||
264 | DRM_ERROR("failed to reconfigure fbdev\n"); | ||
265 | return ret; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | return ret; | ||
270 | } | ||
271 | |||
272 | static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { | ||
273 | .fb_probe = exynos_drm_fbdev_probe, | ||
274 | }; | ||
275 | |||
276 | int exynos_drm_fbdev_init(struct drm_device *dev) | ||
277 | { | ||
278 | struct exynos_drm_fbdev *fbdev; | ||
279 | struct exynos_drm_private *private = dev->dev_private; | ||
280 | struct drm_fb_helper *helper; | ||
281 | unsigned int num_crtc; | ||
282 | int ret; | ||
283 | |||
284 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
285 | |||
286 | if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) | ||
287 | return 0; | ||
288 | |||
289 | fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); | ||
290 | if (!fbdev) { | ||
291 | DRM_ERROR("failed to allocate drm fbdev.\n"); | ||
292 | return -ENOMEM; | ||
293 | } | ||
294 | |||
295 | private->fb_helper = helper = &fbdev->drm_fb_helper; | ||
296 | helper->funcs = &exynos_drm_fb_helper_funcs; | ||
297 | |||
298 | num_crtc = dev->mode_config.num_crtc; | ||
299 | |||
300 | ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); | ||
301 | if (ret < 0) { | ||
302 | DRM_ERROR("failed to initialize drm fb helper.\n"); | ||
303 | goto err_init; | ||
304 | } | ||
305 | |||
306 | ret = drm_fb_helper_single_add_all_connectors(helper); | ||
307 | if (ret < 0) { | ||
308 | DRM_ERROR("failed to register drm_fb_helper_connector.\n"); | ||
309 | goto err_setup; | ||
310 | |||
311 | } | ||
312 | |||
313 | ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); | ||
314 | if (ret < 0) { | ||
315 | DRM_ERROR("failed to set up hw configuration.\n"); | ||
316 | goto err_setup; | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | |||
321 | err_setup: | ||
322 | drm_fb_helper_fini(helper); | ||
323 | |||
324 | err_init: | ||
325 | private->fb_helper = NULL; | ||
326 | kfree(fbdev); | ||
327 | |||
328 | return ret; | ||
329 | } | ||
330 | |||
331 | static void exynos_drm_fbdev_destroy(struct drm_device *dev, | ||
332 | struct drm_fb_helper *fb_helper) | ||
333 | { | ||
334 | struct drm_framebuffer *fb; | ||
335 | |||
336 | /* release drm framebuffer and real buffer */ | ||
337 | if (fb_helper->fb && fb_helper->fb->funcs) { | ||
338 | fb = fb_helper->fb; | ||
339 | if (fb && fb->funcs->destroy) | ||
340 | fb->funcs->destroy(fb); | ||
341 | } | ||
342 | |||
343 | /* release linux framebuffer */ | ||
344 | if (fb_helper->fbdev) { | ||
345 | struct fb_info *info; | ||
346 | int ret; | ||
347 | |||
348 | info = fb_helper->fbdev; | ||
349 | ret = unregister_framebuffer(info); | ||
350 | if (ret < 0) | ||
351 | DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); | ||
352 | |||
353 | if (info->cmap.len) | ||
354 | fb_dealloc_cmap(&info->cmap); | ||
355 | |||
356 | framebuffer_release(info); | ||
357 | } | ||
358 | |||
359 | drm_fb_helper_fini(fb_helper); | ||
360 | } | ||
361 | |||
362 | void exynos_drm_fbdev_fini(struct drm_device *dev) | ||
363 | { | ||
364 | struct exynos_drm_private *private = dev->dev_private; | ||
365 | struct exynos_drm_fbdev *fbdev; | ||
366 | |||
367 | if (!private || !private->fb_helper) | ||
368 | return; | ||
369 | |||
370 | fbdev = to_exynos_fbdev(private->fb_helper); | ||
371 | |||
372 | exynos_drm_fbdev_destroy(dev, private->fb_helper); | ||
373 | kfree(fbdev); | ||
374 | private->fb_helper = NULL; | ||
375 | } | ||
376 | |||
377 | void exynos_drm_fbdev_restore_mode(struct drm_device *dev) | ||
378 | { | ||
379 | struct exynos_drm_private *private = dev->dev_private; | ||
380 | |||
381 | if (!private || !private->fb_helper) | ||
382 | return; | ||
383 | |||
384 | drm_fb_helper_restore_fbdev_mode(private->fb_helper); | ||
385 | } | ||
386 | |||
387 | int exynos_drm_fbdev_reinit(struct drm_device *dev) | ||
388 | { | ||
389 | struct exynos_drm_private *private = dev->dev_private; | ||
390 | struct drm_fb_helper *fb_helper; | ||
391 | int ret; | ||
392 | |||
393 | if (!private) | ||
394 | return -EINVAL; | ||
395 | |||
396 | /* | ||
397 | * if all sub drivers were unloaded then num_connector is 0 | ||
398 | * so at this time, the framebuffers also should be destroyed. | ||
399 | */ | ||
400 | if (!dev->mode_config.num_connector) { | ||
401 | exynos_drm_fbdev_fini(dev); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | fb_helper = private->fb_helper; | ||
406 | |||
407 | if (fb_helper) { | ||
408 | drm_fb_helper_fini(fb_helper); | ||
409 | |||
410 | ret = drm_fb_helper_init(dev, fb_helper, | ||
411 | dev->mode_config.num_crtc, MAX_CONNECTOR); | ||
412 | if (ret < 0) { | ||
413 | DRM_ERROR("failed to initialize drm fb helper\n"); | ||
414 | return ret; | ||
415 | } | ||
416 | |||
417 | ret = drm_fb_helper_single_add_all_connectors(fb_helper); | ||
418 | if (ret < 0) { | ||
419 | DRM_ERROR("failed to add fb helper to connectors\n"); | ||
420 | goto err; | ||
421 | } | ||
422 | |||
423 | ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP); | ||
424 | if (ret < 0) { | ||
425 | DRM_ERROR("failed to set up hw configuration.\n"); | ||
426 | goto err; | ||
427 | } | ||
428 | } else { | ||
429 | /* | ||
430 | * if drm_load() failed whem drm load() was called prior | ||
431 | * to specific drivers, fb_helper must be NULL and so | ||
432 | * this fuction should be called again to re-initialize and | ||
433 | * re-configure the fb helper. it means that this function | ||
434 | * has been called by the specific drivers. | ||
435 | */ | ||
436 | ret = exynos_drm_fbdev_init(dev); | ||
437 | } | ||
438 | |||
439 | return ret; | ||
440 | |||
441 | err: | ||
442 | /* | ||
443 | * if drm_load() failed when drm load() was called prior | ||
444 | * to specific drivers, the fb_helper must be NULL and so check it. | ||
445 | */ | ||
446 | if (fb_helper) | ||
447 | drm_fb_helper_fini(fb_helper); | ||
448 | |||
449 | return ret; | ||
450 | } | ||
451 | |||
452 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
453 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
454 | MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); | ||
455 | MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver"); | ||
456 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.h b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h new file mode 100644 index 000000000000..ccfce8a1a451 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
26 | * OTHER DEALINGS IN THE SOFTWARE. | ||
27 | */ | ||
28 | |||
29 | #ifndef _EXYNOS_DRM_FBDEV_H_ | ||
30 | #define _EXYNOS_DRM_FBDEV_H_ | ||
31 | |||
32 | int exynos_drm_fbdev_init(struct drm_device *dev); | ||
33 | int exynos_drm_fbdev_reinit(struct drm_device *dev); | ||
34 | void exynos_drm_fbdev_fini(struct drm_device *dev); | ||
35 | void exynos_drm_fbdev_restore_mode(struct drm_device *dev); | ||
36 | |||
37 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c new file mode 100644 index 000000000000..4659c88cdd9b --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c | |||
@@ -0,0 +1,811 @@ | |||
1 | /* exynos_drm_fimd.c | ||
2 | * | ||
3 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
4 | * Authors: | ||
5 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
6 | * Inki Dae <inki.dae@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | #include "drmP.h" | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/clk.h> | ||
20 | |||
21 | #include <drm/exynos_drm.h> | ||
22 | #include <plat/regs-fb-v4.h> | ||
23 | |||
24 | #include "exynos_drm_drv.h" | ||
25 | #include "exynos_drm_fbdev.h" | ||
26 | #include "exynos_drm_crtc.h" | ||
27 | |||
28 | /* | ||
29 | * FIMD is stand for Fully Interactive Mobile Display and | ||
30 | * as a display controller, it transfers contents drawn on memory | ||
31 | * to a LCD Panel through Display Interfaces such as RGB or | ||
32 | * CPU Interface. | ||
33 | */ | ||
34 | |||
35 | /* position control register for hardware window 0, 2 ~ 4.*/ | ||
36 | #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) | ||
37 | #define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) | ||
38 | /* size control register for hardware window 0. */ | ||
39 | #define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08) | ||
40 | /* alpha control register for hardware window 1 ~ 4. */ | ||
41 | #define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16) | ||
42 | /* size control register for hardware window 1 ~ 4. */ | ||
43 | #define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) | ||
44 | |||
45 | #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) | ||
46 | #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) | ||
47 | #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) | ||
48 | |||
49 | /* color key control register for hardware window 1 ~ 4. */ | ||
50 | #define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8)) | ||
51 | /* color key value register for hardware window 1 ~ 4. */ | ||
52 | #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8)) | ||
53 | |||
54 | /* FIMD has totally five hardware windows. */ | ||
55 | #define WINDOWS_NR 5 | ||
56 | |||
57 | #define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
58 | |||
59 | struct fimd_win_data { | ||
60 | unsigned int offset_x; | ||
61 | unsigned int offset_y; | ||
62 | unsigned int ovl_width; | ||
63 | unsigned int ovl_height; | ||
64 | unsigned int fb_width; | ||
65 | unsigned int fb_height; | ||
66 | unsigned int bpp; | ||
67 | dma_addr_t paddr; | ||
68 | void __iomem *vaddr; | ||
69 | unsigned int buf_offsize; | ||
70 | unsigned int line_size; /* bytes */ | ||
71 | }; | ||
72 | |||
73 | struct fimd_context { | ||
74 | struct exynos_drm_subdrv subdrv; | ||
75 | int irq; | ||
76 | struct drm_crtc *crtc; | ||
77 | struct clk *bus_clk; | ||
78 | struct clk *lcd_clk; | ||
79 | struct resource *regs_res; | ||
80 | void __iomem *regs; | ||
81 | struct fimd_win_data win_data[WINDOWS_NR]; | ||
82 | unsigned int clkdiv; | ||
83 | unsigned int default_win; | ||
84 | unsigned long irq_flags; | ||
85 | u32 vidcon0; | ||
86 | u32 vidcon1; | ||
87 | |||
88 | struct fb_videomode *timing; | ||
89 | }; | ||
90 | |||
91 | static bool fimd_display_is_connected(struct device *dev) | ||
92 | { | ||
93 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
94 | |||
95 | /* TODO. */ | ||
96 | |||
97 | return true; | ||
98 | } | ||
99 | |||
100 | static void *fimd_get_timing(struct device *dev) | ||
101 | { | ||
102 | struct fimd_context *ctx = get_fimd_context(dev); | ||
103 | |||
104 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
105 | |||
106 | return ctx->timing; | ||
107 | } | ||
108 | |||
109 | static int fimd_check_timing(struct device *dev, void *timing) | ||
110 | { | ||
111 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
112 | |||
113 | /* TODO. */ | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int fimd_display_power_on(struct device *dev, int mode) | ||
119 | { | ||
120 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
121 | |||
122 | /* TODO. */ | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static struct exynos_drm_display fimd_display = { | ||
128 | .type = EXYNOS_DISPLAY_TYPE_LCD, | ||
129 | .is_connected = fimd_display_is_connected, | ||
130 | .get_timing = fimd_get_timing, | ||
131 | .check_timing = fimd_check_timing, | ||
132 | .power_on = fimd_display_power_on, | ||
133 | }; | ||
134 | |||
135 | static void fimd_commit(struct device *dev) | ||
136 | { | ||
137 | struct fimd_context *ctx = get_fimd_context(dev); | ||
138 | struct fb_videomode *timing = ctx->timing; | ||
139 | u32 val; | ||
140 | |||
141 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
142 | |||
143 | /* setup polarity values from machine code. */ | ||
144 | writel(ctx->vidcon1, ctx->regs + VIDCON1); | ||
145 | |||
146 | /* setup vertical timing values. */ | ||
147 | val = VIDTCON0_VBPD(timing->upper_margin - 1) | | ||
148 | VIDTCON0_VFPD(timing->lower_margin - 1) | | ||
149 | VIDTCON0_VSPW(timing->vsync_len - 1); | ||
150 | writel(val, ctx->regs + VIDTCON0); | ||
151 | |||
152 | /* setup horizontal timing values. */ | ||
153 | val = VIDTCON1_HBPD(timing->left_margin - 1) | | ||
154 | VIDTCON1_HFPD(timing->right_margin - 1) | | ||
155 | VIDTCON1_HSPW(timing->hsync_len - 1); | ||
156 | writel(val, ctx->regs + VIDTCON1); | ||
157 | |||
158 | /* setup horizontal and vertical display size. */ | ||
159 | val = VIDTCON2_LINEVAL(timing->yres - 1) | | ||
160 | VIDTCON2_HOZVAL(timing->xres - 1); | ||
161 | writel(val, ctx->regs + VIDTCON2); | ||
162 | |||
163 | /* setup clock source, clock divider, enable dma. */ | ||
164 | val = ctx->vidcon0; | ||
165 | val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); | ||
166 | |||
167 | if (ctx->clkdiv > 1) | ||
168 | val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; | ||
169 | else | ||
170 | val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ | ||
171 | |||
172 | /* | ||
173 | * fields of register with prefix '_F' would be updated | ||
174 | * at vsync(same as dma start) | ||
175 | */ | ||
176 | val |= VIDCON0_ENVID | VIDCON0_ENVID_F; | ||
177 | writel(val, ctx->regs + VIDCON0); | ||
178 | } | ||
179 | |||
180 | static int fimd_enable_vblank(struct device *dev) | ||
181 | { | ||
182 | struct fimd_context *ctx = get_fimd_context(dev); | ||
183 | u32 val; | ||
184 | |||
185 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
186 | |||
187 | if (!test_and_set_bit(0, &ctx->irq_flags)) { | ||
188 | val = readl(ctx->regs + VIDINTCON0); | ||
189 | |||
190 | val |= VIDINTCON0_INT_ENABLE; | ||
191 | val |= VIDINTCON0_INT_FRAME; | ||
192 | |||
193 | val &= ~VIDINTCON0_FRAMESEL0_MASK; | ||
194 | val |= VIDINTCON0_FRAMESEL0_VSYNC; | ||
195 | val &= ~VIDINTCON0_FRAMESEL1_MASK; | ||
196 | val |= VIDINTCON0_FRAMESEL1_NONE; | ||
197 | |||
198 | writel(val, ctx->regs + VIDINTCON0); | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static void fimd_disable_vblank(struct device *dev) | ||
205 | { | ||
206 | struct fimd_context *ctx = get_fimd_context(dev); | ||
207 | u32 val; | ||
208 | |||
209 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
210 | |||
211 | if (test_and_clear_bit(0, &ctx->irq_flags)) { | ||
212 | val = readl(ctx->regs + VIDINTCON0); | ||
213 | |||
214 | val &= ~VIDINTCON0_INT_FRAME; | ||
215 | val &= ~VIDINTCON0_INT_ENABLE; | ||
216 | |||
217 | writel(val, ctx->regs + VIDINTCON0); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static struct exynos_drm_manager_ops fimd_manager_ops = { | ||
222 | .commit = fimd_commit, | ||
223 | .enable_vblank = fimd_enable_vblank, | ||
224 | .disable_vblank = fimd_disable_vblank, | ||
225 | }; | ||
226 | |||
227 | static void fimd_win_mode_set(struct device *dev, | ||
228 | struct exynos_drm_overlay *overlay) | ||
229 | { | ||
230 | struct fimd_context *ctx = get_fimd_context(dev); | ||
231 | struct fimd_win_data *win_data; | ||
232 | unsigned long offset; | ||
233 | |||
234 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
235 | |||
236 | if (!overlay) { | ||
237 | dev_err(dev, "overlay is NULL\n"); | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | offset = overlay->fb_x * (overlay->bpp >> 3); | ||
242 | offset += overlay->fb_y * overlay->pitch; | ||
243 | |||
244 | DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); | ||
245 | |||
246 | win_data = &ctx->win_data[ctx->default_win]; | ||
247 | |||
248 | win_data->offset_x = overlay->crtc_x; | ||
249 | win_data->offset_y = overlay->crtc_y; | ||
250 | win_data->ovl_width = overlay->crtc_width; | ||
251 | win_data->ovl_height = overlay->crtc_height; | ||
252 | win_data->fb_width = overlay->fb_width; | ||
253 | win_data->fb_height = overlay->fb_height; | ||
254 | win_data->paddr = overlay->paddr + offset; | ||
255 | win_data->vaddr = overlay->vaddr + offset; | ||
256 | win_data->bpp = overlay->bpp; | ||
257 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * | ||
258 | (overlay->bpp >> 3); | ||
259 | win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); | ||
260 | |||
261 | DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", | ||
262 | win_data->offset_x, win_data->offset_y); | ||
263 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", | ||
264 | win_data->ovl_width, win_data->ovl_height); | ||
265 | DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", | ||
266 | (unsigned long)win_data->paddr, | ||
267 | (unsigned long)win_data->vaddr); | ||
268 | DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", | ||
269 | overlay->fb_width, overlay->crtc_width); | ||
270 | } | ||
271 | |||
272 | static void fimd_win_set_pixfmt(struct device *dev, unsigned int win) | ||
273 | { | ||
274 | struct fimd_context *ctx = get_fimd_context(dev); | ||
275 | struct fimd_win_data *win_data = &ctx->win_data[win]; | ||
276 | unsigned long val; | ||
277 | |||
278 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
279 | |||
280 | val = WINCONx_ENWIN; | ||
281 | |||
282 | switch (win_data->bpp) { | ||
283 | case 1: | ||
284 | val |= WINCON0_BPPMODE_1BPP; | ||
285 | val |= WINCONx_BITSWP; | ||
286 | val |= WINCONx_BURSTLEN_4WORD; | ||
287 | break; | ||
288 | case 2: | ||
289 | val |= WINCON0_BPPMODE_2BPP; | ||
290 | val |= WINCONx_BITSWP; | ||
291 | val |= WINCONx_BURSTLEN_8WORD; | ||
292 | break; | ||
293 | case 4: | ||
294 | val |= WINCON0_BPPMODE_4BPP; | ||
295 | val |= WINCONx_BITSWP; | ||
296 | val |= WINCONx_BURSTLEN_8WORD; | ||
297 | break; | ||
298 | case 8: | ||
299 | val |= WINCON0_BPPMODE_8BPP_PALETTE; | ||
300 | val |= WINCONx_BURSTLEN_8WORD; | ||
301 | val |= WINCONx_BYTSWP; | ||
302 | break; | ||
303 | case 16: | ||
304 | val |= WINCON0_BPPMODE_16BPP_565; | ||
305 | val |= WINCONx_HAWSWP; | ||
306 | val |= WINCONx_BURSTLEN_16WORD; | ||
307 | break; | ||
308 | case 24: | ||
309 | val |= WINCON0_BPPMODE_24BPP_888; | ||
310 | val |= WINCONx_WSWP; | ||
311 | val |= WINCONx_BURSTLEN_16WORD; | ||
312 | break; | ||
313 | case 32: | ||
314 | val |= WINCON1_BPPMODE_28BPP_A4888 | ||
315 | | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; | ||
316 | val |= WINCONx_WSWP; | ||
317 | val |= WINCONx_BURSTLEN_16WORD; | ||
318 | break; | ||
319 | default: | ||
320 | DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); | ||
321 | |||
322 | val |= WINCON0_BPPMODE_24BPP_888; | ||
323 | val |= WINCONx_WSWP; | ||
324 | val |= WINCONx_BURSTLEN_16WORD; | ||
325 | break; | ||
326 | } | ||
327 | |||
328 | DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); | ||
329 | |||
330 | writel(val, ctx->regs + WINCON(win)); | ||
331 | } | ||
332 | |||
333 | static void fimd_win_set_colkey(struct device *dev, unsigned int win) | ||
334 | { | ||
335 | struct fimd_context *ctx = get_fimd_context(dev); | ||
336 | unsigned int keycon0 = 0, keycon1 = 0; | ||
337 | |||
338 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
339 | |||
340 | keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | | ||
341 | WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); | ||
342 | |||
343 | keycon1 = WxKEYCON1_COLVAL(0xffffffff); | ||
344 | |||
345 | writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); | ||
346 | writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); | ||
347 | } | ||
348 | |||
349 | static void fimd_win_commit(struct device *dev) | ||
350 | { | ||
351 | struct fimd_context *ctx = get_fimd_context(dev); | ||
352 | struct fimd_win_data *win_data; | ||
353 | int win = ctx->default_win; | ||
354 | unsigned long val, alpha, size; | ||
355 | |||
356 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
357 | |||
358 | if (win < 0 || win > WINDOWS_NR) | ||
359 | return; | ||
360 | |||
361 | win_data = &ctx->win_data[win]; | ||
362 | |||
363 | /* | ||
364 | * SHADOWCON register is used for enabling timing. | ||
365 | * | ||
366 | * for example, once only width value of a register is set, | ||
367 | * if the dma is started then fimd hardware could malfunction so | ||
368 | * with protect window setting, the register fields with prefix '_F' | ||
369 | * wouldn't be updated at vsync also but updated once unprotect window | ||
370 | * is set. | ||
371 | */ | ||
372 | |||
373 | /* protect windows */ | ||
374 | val = readl(ctx->regs + SHADOWCON); | ||
375 | val |= SHADOWCON_WINx_PROTECT(win); | ||
376 | writel(val, ctx->regs + SHADOWCON); | ||
377 | |||
378 | /* buffer start address */ | ||
379 | val = win_data->paddr; | ||
380 | writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); | ||
381 | |||
382 | /* buffer end address */ | ||
383 | size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); | ||
384 | val = win_data->paddr + size; | ||
385 | writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); | ||
386 | |||
387 | DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", | ||
388 | (unsigned long)win_data->paddr, val, size); | ||
389 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", | ||
390 | win_data->ovl_width, win_data->ovl_height); | ||
391 | |||
392 | /* buffer size */ | ||
393 | val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | | ||
394 | VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size); | ||
395 | writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); | ||
396 | |||
397 | /* OSD position */ | ||
398 | val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | | ||
399 | VIDOSDxA_TOPLEFT_Y(win_data->offset_y); | ||
400 | writel(val, ctx->regs + VIDOSD_A(win)); | ||
401 | |||
402 | val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x + | ||
403 | win_data->ovl_width - 1) | | ||
404 | VIDOSDxB_BOTRIGHT_Y(win_data->offset_y + | ||
405 | win_data->ovl_height - 1); | ||
406 | writel(val, ctx->regs + VIDOSD_B(win)); | ||
407 | |||
408 | DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", | ||
409 | win_data->offset_x, win_data->offset_y, | ||
410 | win_data->offset_x + win_data->ovl_width - 1, | ||
411 | win_data->offset_y + win_data->ovl_height - 1); | ||
412 | |||
413 | /* hardware window 0 doesn't support alpha channel. */ | ||
414 | if (win != 0) { | ||
415 | /* OSD alpha */ | ||
416 | alpha = VIDISD14C_ALPHA1_R(0xf) | | ||
417 | VIDISD14C_ALPHA1_G(0xf) | | ||
418 | VIDISD14C_ALPHA1_B(0xf); | ||
419 | |||
420 | writel(alpha, ctx->regs + VIDOSD_C(win)); | ||
421 | } | ||
422 | |||
423 | /* OSD size */ | ||
424 | if (win != 3 && win != 4) { | ||
425 | u32 offset = VIDOSD_D(win); | ||
426 | if (win == 0) | ||
427 | offset = VIDOSD_C_SIZE_W0; | ||
428 | val = win_data->ovl_width * win_data->ovl_height; | ||
429 | writel(val, ctx->regs + offset); | ||
430 | |||
431 | DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); | ||
432 | } | ||
433 | |||
434 | fimd_win_set_pixfmt(dev, win); | ||
435 | |||
436 | /* hardware window 0 doesn't support color key. */ | ||
437 | if (win != 0) | ||
438 | fimd_win_set_colkey(dev, win); | ||
439 | |||
440 | /* Enable DMA channel and unprotect windows */ | ||
441 | val = readl(ctx->regs + SHADOWCON); | ||
442 | val |= SHADOWCON_CHx_ENABLE(win); | ||
443 | val &= ~SHADOWCON_WINx_PROTECT(win); | ||
444 | writel(val, ctx->regs + SHADOWCON); | ||
445 | } | ||
446 | |||
447 | static void fimd_win_disable(struct device *dev) | ||
448 | { | ||
449 | struct fimd_context *ctx = get_fimd_context(dev); | ||
450 | struct fimd_win_data *win_data; | ||
451 | int win = ctx->default_win; | ||
452 | u32 val; | ||
453 | |||
454 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
455 | |||
456 | if (win < 0 || win > WINDOWS_NR) | ||
457 | return; | ||
458 | |||
459 | win_data = &ctx->win_data[win]; | ||
460 | |||
461 | /* protect windows */ | ||
462 | val = readl(ctx->regs + SHADOWCON); | ||
463 | val |= SHADOWCON_WINx_PROTECT(win); | ||
464 | writel(val, ctx->regs + SHADOWCON); | ||
465 | |||
466 | /* wincon */ | ||
467 | val = readl(ctx->regs + WINCON(win)); | ||
468 | val &= ~WINCONx_ENWIN; | ||
469 | writel(val, ctx->regs + WINCON(win)); | ||
470 | |||
471 | /* unprotect windows */ | ||
472 | val = readl(ctx->regs + SHADOWCON); | ||
473 | val &= ~SHADOWCON_CHx_ENABLE(win); | ||
474 | val &= ~SHADOWCON_WINx_PROTECT(win); | ||
475 | writel(val, ctx->regs + SHADOWCON); | ||
476 | } | ||
477 | |||
478 | static struct exynos_drm_overlay_ops fimd_overlay_ops = { | ||
479 | .mode_set = fimd_win_mode_set, | ||
480 | .commit = fimd_win_commit, | ||
481 | .disable = fimd_win_disable, | ||
482 | }; | ||
483 | |||
484 | static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) | ||
485 | { | ||
486 | struct exynos_drm_private *dev_priv = drm_dev->dev_private; | ||
487 | struct drm_pending_vblank_event *e, *t; | ||
488 | struct timeval now; | ||
489 | unsigned long flags; | ||
490 | bool is_checked = false; | ||
491 | |||
492 | spin_lock_irqsave(&drm_dev->event_lock, flags); | ||
493 | |||
494 | list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, | ||
495 | base.link) { | ||
496 | /* if event's pipe isn't same as crtc then ignore it. */ | ||
497 | if (crtc != e->pipe) | ||
498 | continue; | ||
499 | |||
500 | is_checked = true; | ||
501 | |||
502 | do_gettimeofday(&now); | ||
503 | e->event.sequence = 0; | ||
504 | e->event.tv_sec = now.tv_sec; | ||
505 | e->event.tv_usec = now.tv_usec; | ||
506 | |||
507 | list_move_tail(&e->base.link, &e->base.file_priv->event_list); | ||
508 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
509 | } | ||
510 | |||
511 | if (is_checked) | ||
512 | drm_vblank_put(drm_dev, crtc); | ||
513 | |||
514 | spin_unlock_irqrestore(&drm_dev->event_lock, flags); | ||
515 | } | ||
516 | |||
517 | static irqreturn_t fimd_irq_handler(int irq, void *dev_id) | ||
518 | { | ||
519 | struct fimd_context *ctx = (struct fimd_context *)dev_id; | ||
520 | struct exynos_drm_subdrv *subdrv = &ctx->subdrv; | ||
521 | struct drm_device *drm_dev = subdrv->drm_dev; | ||
522 | struct exynos_drm_manager *manager = &subdrv->manager; | ||
523 | u32 val; | ||
524 | |||
525 | val = readl(ctx->regs + VIDINTCON1); | ||
526 | |||
527 | if (val & VIDINTCON1_INT_FRAME) | ||
528 | /* VSYNC interrupt */ | ||
529 | writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); | ||
530 | |||
531 | drm_handle_vblank(drm_dev, manager->pipe); | ||
532 | fimd_finish_pageflip(drm_dev, manager->pipe); | ||
533 | |||
534 | return IRQ_HANDLED; | ||
535 | } | ||
536 | |||
537 | static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) | ||
538 | { | ||
539 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
540 | |||
541 | /* | ||
542 | * enable drm irq mode. | ||
543 | * - with irq_enabled = 1, we can use the vblank feature. | ||
544 | * | ||
545 | * P.S. note that we wouldn't use drm irq handler but | ||
546 | * just specific driver own one instead because | ||
547 | * drm framework supports only one irq handler. | ||
548 | */ | ||
549 | drm_dev->irq_enabled = 1; | ||
550 | |||
551 | /* | ||
552 | * with vblank_disable_allowed = 1, vblank interrupt will be disabled | ||
553 | * by drm timer once a current process gives up ownership of | ||
554 | * vblank event.(drm_vblank_put function was called) | ||
555 | */ | ||
556 | drm_dev->vblank_disable_allowed = 1; | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | static void fimd_subdrv_remove(struct drm_device *drm_dev) | ||
562 | { | ||
563 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
564 | |||
565 | /* TODO. */ | ||
566 | } | ||
567 | |||
568 | static int fimd_calc_clkdiv(struct fimd_context *ctx, | ||
569 | struct fb_videomode *timing) | ||
570 | { | ||
571 | unsigned long clk = clk_get_rate(ctx->lcd_clk); | ||
572 | u32 retrace; | ||
573 | u32 clkdiv; | ||
574 | u32 best_framerate = 0; | ||
575 | u32 framerate; | ||
576 | |||
577 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
578 | |||
579 | retrace = timing->left_margin + timing->hsync_len + | ||
580 | timing->right_margin + timing->xres; | ||
581 | retrace *= timing->upper_margin + timing->vsync_len + | ||
582 | timing->lower_margin + timing->yres; | ||
583 | |||
584 | /* default framerate is 60Hz */ | ||
585 | if (!timing->refresh) | ||
586 | timing->refresh = 60; | ||
587 | |||
588 | clk /= retrace; | ||
589 | |||
590 | for (clkdiv = 1; clkdiv < 0x100; clkdiv++) { | ||
591 | int tmp; | ||
592 | |||
593 | /* get best framerate */ | ||
594 | framerate = clk / clkdiv; | ||
595 | tmp = timing->refresh - framerate; | ||
596 | if (tmp < 0) { | ||
597 | best_framerate = framerate; | ||
598 | continue; | ||
599 | } else { | ||
600 | if (!best_framerate) | ||
601 | best_framerate = framerate; | ||
602 | else if (tmp < (best_framerate - framerate)) | ||
603 | best_framerate = framerate; | ||
604 | break; | ||
605 | } | ||
606 | } | ||
607 | |||
608 | return clkdiv; | ||
609 | } | ||
610 | |||
611 | static void fimd_clear_win(struct fimd_context *ctx, int win) | ||
612 | { | ||
613 | u32 val; | ||
614 | |||
615 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
616 | |||
617 | writel(0, ctx->regs + WINCON(win)); | ||
618 | writel(0, ctx->regs + VIDOSD_A(win)); | ||
619 | writel(0, ctx->regs + VIDOSD_B(win)); | ||
620 | writel(0, ctx->regs + VIDOSD_C(win)); | ||
621 | |||
622 | if (win == 1 || win == 2) | ||
623 | writel(0, ctx->regs + VIDOSD_D(win)); | ||
624 | |||
625 | val = readl(ctx->regs + SHADOWCON); | ||
626 | val &= ~SHADOWCON_WINx_PROTECT(win); | ||
627 | writel(val, ctx->regs + SHADOWCON); | ||
628 | } | ||
629 | |||
630 | static int __devinit fimd_probe(struct platform_device *pdev) | ||
631 | { | ||
632 | struct device *dev = &pdev->dev; | ||
633 | struct fimd_context *ctx; | ||
634 | struct exynos_drm_subdrv *subdrv; | ||
635 | struct exynos_drm_fimd_pdata *pdata; | ||
636 | struct fb_videomode *timing; | ||
637 | struct resource *res; | ||
638 | int win; | ||
639 | int ret = -EINVAL; | ||
640 | |||
641 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
642 | |||
643 | pdata = pdev->dev.platform_data; | ||
644 | if (!pdata) { | ||
645 | dev_err(dev, "no platform data specified\n"); | ||
646 | return -EINVAL; | ||
647 | } | ||
648 | |||
649 | timing = &pdata->timing; | ||
650 | if (!timing) { | ||
651 | dev_err(dev, "timing is null.\n"); | ||
652 | return -EINVAL; | ||
653 | } | ||
654 | |||
655 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
656 | if (!ctx) | ||
657 | return -ENOMEM; | ||
658 | |||
659 | ctx->bus_clk = clk_get(dev, "fimd"); | ||
660 | if (IS_ERR(ctx->bus_clk)) { | ||
661 | dev_err(dev, "failed to get bus clock\n"); | ||
662 | ret = PTR_ERR(ctx->bus_clk); | ||
663 | goto err_clk_get; | ||
664 | } | ||
665 | |||
666 | clk_enable(ctx->bus_clk); | ||
667 | |||
668 | ctx->lcd_clk = clk_get(dev, "sclk_fimd"); | ||
669 | if (IS_ERR(ctx->lcd_clk)) { | ||
670 | dev_err(dev, "failed to get lcd clock\n"); | ||
671 | ret = PTR_ERR(ctx->lcd_clk); | ||
672 | goto err_bus_clk; | ||
673 | } | ||
674 | |||
675 | clk_enable(ctx->lcd_clk); | ||
676 | |||
677 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
678 | if (!res) { | ||
679 | dev_err(dev, "failed to find registers\n"); | ||
680 | ret = -ENOENT; | ||
681 | goto err_clk; | ||
682 | } | ||
683 | |||
684 | ctx->regs_res = request_mem_region(res->start, resource_size(res), | ||
685 | dev_name(dev)); | ||
686 | if (!ctx->regs_res) { | ||
687 | dev_err(dev, "failed to claim register region\n"); | ||
688 | ret = -ENOENT; | ||
689 | goto err_clk; | ||
690 | } | ||
691 | |||
692 | ctx->regs = ioremap(res->start, resource_size(res)); | ||
693 | if (!ctx->regs) { | ||
694 | dev_err(dev, "failed to map registers\n"); | ||
695 | ret = -ENXIO; | ||
696 | goto err_req_region_io; | ||
697 | } | ||
698 | |||
699 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
700 | if (!res) { | ||
701 | dev_err(dev, "irq request failed.\n"); | ||
702 | goto err_req_region_irq; | ||
703 | } | ||
704 | |||
705 | ctx->irq = res->start; | ||
706 | |||
707 | for (win = 0; win < WINDOWS_NR; win++) | ||
708 | fimd_clear_win(ctx, win); | ||
709 | |||
710 | ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx); | ||
711 | if (ret < 0) { | ||
712 | dev_err(dev, "irq request failed.\n"); | ||
713 | goto err_req_irq; | ||
714 | } | ||
715 | |||
716 | ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); | ||
717 | ctx->vidcon0 = pdata->vidcon0; | ||
718 | ctx->vidcon1 = pdata->vidcon1; | ||
719 | ctx->default_win = pdata->default_win; | ||
720 | ctx->timing = timing; | ||
721 | |||
722 | timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv; | ||
723 | |||
724 | DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n", | ||
725 | timing->pixclock, ctx->clkdiv); | ||
726 | |||
727 | subdrv = &ctx->subdrv; | ||
728 | |||
729 | subdrv->probe = fimd_subdrv_probe; | ||
730 | subdrv->remove = fimd_subdrv_remove; | ||
731 | subdrv->manager.pipe = -1; | ||
732 | subdrv->manager.ops = &fimd_manager_ops; | ||
733 | subdrv->manager.overlay_ops = &fimd_overlay_ops; | ||
734 | subdrv->manager.display = &fimd_display; | ||
735 | subdrv->manager.dev = dev; | ||
736 | |||
737 | platform_set_drvdata(pdev, ctx); | ||
738 | exynos_drm_subdrv_register(subdrv); | ||
739 | |||
740 | return 0; | ||
741 | |||
742 | err_req_irq: | ||
743 | err_req_region_irq: | ||
744 | iounmap(ctx->regs); | ||
745 | |||
746 | err_req_region_io: | ||
747 | release_resource(ctx->regs_res); | ||
748 | kfree(ctx->regs_res); | ||
749 | |||
750 | err_clk: | ||
751 | clk_disable(ctx->lcd_clk); | ||
752 | clk_put(ctx->lcd_clk); | ||
753 | |||
754 | err_bus_clk: | ||
755 | clk_disable(ctx->bus_clk); | ||
756 | clk_put(ctx->bus_clk); | ||
757 | |||
758 | err_clk_get: | ||
759 | kfree(ctx); | ||
760 | return ret; | ||
761 | } | ||
762 | |||
763 | static int __devexit fimd_remove(struct platform_device *pdev) | ||
764 | { | ||
765 | struct fimd_context *ctx = platform_get_drvdata(pdev); | ||
766 | |||
767 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
768 | |||
769 | exynos_drm_subdrv_unregister(&ctx->subdrv); | ||
770 | |||
771 | clk_disable(ctx->lcd_clk); | ||
772 | clk_disable(ctx->bus_clk); | ||
773 | clk_put(ctx->lcd_clk); | ||
774 | clk_put(ctx->bus_clk); | ||
775 | |||
776 | iounmap(ctx->regs); | ||
777 | release_resource(ctx->regs_res); | ||
778 | kfree(ctx->regs_res); | ||
779 | free_irq(ctx->irq, ctx); | ||
780 | |||
781 | kfree(ctx); | ||
782 | |||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | static struct platform_driver fimd_driver = { | ||
787 | .probe = fimd_probe, | ||
788 | .remove = __devexit_p(fimd_remove), | ||
789 | .driver = { | ||
790 | .name = "exynos4-fb", | ||
791 | .owner = THIS_MODULE, | ||
792 | }, | ||
793 | }; | ||
794 | |||
795 | static int __init fimd_init(void) | ||
796 | { | ||
797 | return platform_driver_register(&fimd_driver); | ||
798 | } | ||
799 | |||
800 | static void __exit fimd_exit(void) | ||
801 | { | ||
802 | platform_driver_unregister(&fimd_driver); | ||
803 | } | ||
804 | |||
805 | module_init(fimd_init); | ||
806 | module_exit(fimd_exit); | ||
807 | |||
808 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
809 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
810 | MODULE_DESCRIPTION("Samsung DRM FIMD Driver"); | ||
811 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c new file mode 100644 index 000000000000..a8e7a88906ed --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c | |||
@@ -0,0 +1,415 @@ | |||
1 | /* exynos_drm_gem.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Author: Inki Dae <inki.dae@samsung.com> | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the next | ||
14 | * paragraph) shall be included in all copies or substantial portions of the | ||
15 | * Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
20 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
23 | * OTHER DEALINGS IN THE SOFTWARE. | ||
24 | */ | ||
25 | |||
26 | #include "drmP.h" | ||
27 | #include "drm.h" | ||
28 | |||
29 | #include <drm/exynos_drm.h> | ||
30 | |||
31 | #include "exynos_drm_drv.h" | ||
32 | #include "exynos_drm_gem.h" | ||
33 | #include "exynos_drm_buf.h" | ||
34 | |||
35 | static unsigned int convert_to_vm_err_msg(int msg) | ||
36 | { | ||
37 | unsigned int out_msg; | ||
38 | |||
39 | switch (msg) { | ||
40 | case 0: | ||
41 | case -ERESTARTSYS: | ||
42 | case -EINTR: | ||
43 | out_msg = VM_FAULT_NOPAGE; | ||
44 | break; | ||
45 | |||
46 | case -ENOMEM: | ||
47 | out_msg = VM_FAULT_OOM; | ||
48 | break; | ||
49 | |||
50 | default: | ||
51 | out_msg = VM_FAULT_SIGBUS; | ||
52 | break; | ||
53 | } | ||
54 | |||
55 | return out_msg; | ||
56 | } | ||
57 | |||
58 | static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj) | ||
59 | { | ||
60 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
61 | |||
62 | return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT; | ||
63 | } | ||
64 | |||
65 | struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv, | ||
66 | struct drm_device *dev, unsigned int size, | ||
67 | unsigned int *handle) | ||
68 | { | ||
69 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
70 | struct exynos_drm_buf_entry *entry; | ||
71 | struct drm_gem_object *obj; | ||
72 | int ret; | ||
73 | |||
74 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
75 | |||
76 | size = roundup(size, PAGE_SIZE); | ||
77 | |||
78 | exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); | ||
79 | if (!exynos_gem_obj) { | ||
80 | DRM_ERROR("failed to allocate exynos gem object.\n"); | ||
81 | return ERR_PTR(-ENOMEM); | ||
82 | } | ||
83 | |||
84 | /* allocate the new buffer object and memory region. */ | ||
85 | entry = exynos_drm_buf_create(dev, size); | ||
86 | if (!entry) { | ||
87 | kfree(exynos_gem_obj); | ||
88 | return ERR_PTR(-ENOMEM); | ||
89 | } | ||
90 | |||
91 | exynos_gem_obj->entry = entry; | ||
92 | |||
93 | obj = &exynos_gem_obj->base; | ||
94 | |||
95 | ret = drm_gem_object_init(dev, obj, size); | ||
96 | if (ret < 0) { | ||
97 | DRM_ERROR("failed to initailize gem object.\n"); | ||
98 | goto err_obj_init; | ||
99 | } | ||
100 | |||
101 | DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); | ||
102 | |||
103 | ret = drm_gem_create_mmap_offset(obj); | ||
104 | if (ret < 0) { | ||
105 | DRM_ERROR("failed to allocate mmap offset.\n"); | ||
106 | goto err_create_mmap_offset; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * allocate a id of idr table where the obj is registered | ||
111 | * and handle has the id what user can see. | ||
112 | */ | ||
113 | ret = drm_gem_handle_create(file_priv, obj, handle); | ||
114 | if (ret) | ||
115 | goto err_handle_create; | ||
116 | |||
117 | DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); | ||
118 | |||
119 | /* drop reference from allocate - handle holds it now. */ | ||
120 | drm_gem_object_unreference_unlocked(obj); | ||
121 | |||
122 | return exynos_gem_obj; | ||
123 | |||
124 | err_handle_create: | ||
125 | drm_gem_free_mmap_offset(obj); | ||
126 | |||
127 | err_create_mmap_offset: | ||
128 | drm_gem_object_release(obj); | ||
129 | |||
130 | err_obj_init: | ||
131 | exynos_drm_buf_destroy(dev, exynos_gem_obj->entry); | ||
132 | |||
133 | kfree(exynos_gem_obj); | ||
134 | |||
135 | return ERR_PTR(ret); | ||
136 | } | ||
137 | |||
138 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, | ||
139 | struct drm_file *file_priv) | ||
140 | { | ||
141 | struct drm_exynos_gem_create *args = data; | ||
142 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
143 | |||
144 | DRM_DEBUG_KMS("%s : size = 0x%x\n", __FILE__, args->size); | ||
145 | |||
146 | exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size, | ||
147 | &args->handle); | ||
148 | if (IS_ERR(exynos_gem_obj)) | ||
149 | return PTR_ERR(exynos_gem_obj); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, | ||
155 | struct drm_file *file_priv) | ||
156 | { | ||
157 | struct drm_exynos_gem_map_off *args = data; | ||
158 | |||
159 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
160 | |||
161 | DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n", | ||
162 | args->handle, (unsigned long)args->offset); | ||
163 | |||
164 | if (!(dev->driver->driver_features & DRIVER_GEM)) { | ||
165 | DRM_ERROR("does not support GEM.\n"); | ||
166 | return -ENODEV; | ||
167 | } | ||
168 | |||
169 | return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle, | ||
170 | &args->offset); | ||
171 | } | ||
172 | |||
173 | static int exynos_drm_gem_mmap_buffer(struct file *filp, | ||
174 | struct vm_area_struct *vma) | ||
175 | { | ||
176 | struct drm_gem_object *obj = filp->private_data; | ||
177 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | ||
178 | struct exynos_drm_buf_entry *entry; | ||
179 | unsigned long pfn, vm_size; | ||
180 | |||
181 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
182 | |||
183 | vma->vm_flags |= (VM_IO | VM_RESERVED); | ||
184 | |||
185 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
186 | vma->vm_file = filp; | ||
187 | |||
188 | vm_size = vma->vm_end - vma->vm_start; | ||
189 | /* | ||
190 | * a entry contains information to physically continuous memory | ||
191 | * allocated by user request or at framebuffer creation. | ||
192 | */ | ||
193 | entry = exynos_gem_obj->entry; | ||
194 | |||
195 | /* check if user-requested size is valid. */ | ||
196 | if (vm_size > entry->size) | ||
197 | return -EINVAL; | ||
198 | |||
199 | /* | ||
200 | * get page frame number to physical memory to be mapped | ||
201 | * to user space. | ||
202 | */ | ||
203 | pfn = exynos_gem_obj->entry->paddr >> PAGE_SHIFT; | ||
204 | |||
205 | DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); | ||
206 | |||
207 | if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size, | ||
208 | vma->vm_page_prot)) { | ||
209 | DRM_ERROR("failed to remap pfn range.\n"); | ||
210 | return -EAGAIN; | ||
211 | } | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static const struct file_operations exynos_drm_gem_fops = { | ||
217 | .mmap = exynos_drm_gem_mmap_buffer, | ||
218 | }; | ||
219 | |||
220 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | ||
221 | struct drm_file *file_priv) | ||
222 | { | ||
223 | struct drm_exynos_gem_mmap *args = data; | ||
224 | struct drm_gem_object *obj; | ||
225 | unsigned int addr; | ||
226 | |||
227 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
228 | |||
229 | if (!(dev->driver->driver_features & DRIVER_GEM)) { | ||
230 | DRM_ERROR("does not support GEM.\n"); | ||
231 | return -ENODEV; | ||
232 | } | ||
233 | |||
234 | obj = drm_gem_object_lookup(dev, file_priv, args->handle); | ||
235 | if (!obj) { | ||
236 | DRM_ERROR("failed to lookup gem object.\n"); | ||
237 | return -EINVAL; | ||
238 | } | ||
239 | |||
240 | obj->filp->f_op = &exynos_drm_gem_fops; | ||
241 | obj->filp->private_data = obj; | ||
242 | |||
243 | down_write(¤t->mm->mmap_sem); | ||
244 | addr = do_mmap(obj->filp, 0, args->size, | ||
245 | PROT_READ | PROT_WRITE, MAP_SHARED, 0); | ||
246 | up_write(¤t->mm->mmap_sem); | ||
247 | |||
248 | drm_gem_object_unreference_unlocked(obj); | ||
249 | |||
250 | if (IS_ERR((void *)addr)) | ||
251 | return PTR_ERR((void *)addr); | ||
252 | |||
253 | args->mapped = addr; | ||
254 | |||
255 | DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | int exynos_drm_gem_init_object(struct drm_gem_object *obj) | ||
261 | { | ||
262 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj) | ||
268 | { | ||
269 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
270 | |||
271 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
272 | |||
273 | DRM_DEBUG_KMS("handle count = %d\n", | ||
274 | atomic_read(&gem_obj->handle_count)); | ||
275 | |||
276 | if (gem_obj->map_list.map) | ||
277 | drm_gem_free_mmap_offset(gem_obj); | ||
278 | |||
279 | /* release file pointer to gem object. */ | ||
280 | drm_gem_object_release(gem_obj); | ||
281 | |||
282 | exynos_gem_obj = to_exynos_gem_obj(gem_obj); | ||
283 | |||
284 | exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->entry); | ||
285 | |||
286 | kfree(exynos_gem_obj); | ||
287 | } | ||
288 | |||
289 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | ||
290 | struct drm_device *dev, struct drm_mode_create_dumb *args) | ||
291 | { | ||
292 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
293 | |||
294 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
295 | |||
296 | /* | ||
297 | * alocate memory to be used for framebuffer. | ||
298 | * - this callback would be called by user application | ||
299 | * with DRM_IOCTL_MODE_CREATE_DUMB command. | ||
300 | */ | ||
301 | |||
302 | args->pitch = args->width * args->bpp >> 3; | ||
303 | args->size = args->pitch * args->height; | ||
304 | |||
305 | exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size, | ||
306 | &args->handle); | ||
307 | if (IS_ERR(exynos_gem_obj)) | ||
308 | return PTR_ERR(exynos_gem_obj); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, | ||
314 | struct drm_device *dev, uint32_t handle, uint64_t *offset) | ||
315 | { | ||
316 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
317 | struct drm_gem_object *obj; | ||
318 | |||
319 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
320 | |||
321 | mutex_lock(&dev->struct_mutex); | ||
322 | |||
323 | /* | ||
324 | * get offset of memory allocated for drm framebuffer. | ||
325 | * - this callback would be called by user application | ||
326 | * with DRM_IOCTL_MODE_MAP_DUMB command. | ||
327 | */ | ||
328 | |||
329 | obj = drm_gem_object_lookup(dev, file_priv, handle); | ||
330 | if (!obj) { | ||
331 | DRM_ERROR("failed to lookup gem object.\n"); | ||
332 | mutex_unlock(&dev->struct_mutex); | ||
333 | return -EINVAL; | ||
334 | } | ||
335 | |||
336 | exynos_gem_obj = to_exynos_gem_obj(obj); | ||
337 | |||
338 | *offset = get_gem_mmap_offset(&exynos_gem_obj->base); | ||
339 | |||
340 | drm_gem_object_unreference(obj); | ||
341 | |||
342 | DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); | ||
343 | |||
344 | mutex_unlock(&dev->struct_mutex); | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
350 | { | ||
351 | struct drm_gem_object *obj = vma->vm_private_data; | ||
352 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | ||
353 | struct drm_device *dev = obj->dev; | ||
354 | unsigned long pfn; | ||
355 | pgoff_t page_offset; | ||
356 | int ret; | ||
357 | |||
358 | page_offset = ((unsigned long)vmf->virtual_address - | ||
359 | vma->vm_start) >> PAGE_SHIFT; | ||
360 | |||
361 | mutex_lock(&dev->struct_mutex); | ||
362 | |||
363 | pfn = (exynos_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset; | ||
364 | |||
365 | ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); | ||
366 | |||
367 | mutex_unlock(&dev->struct_mutex); | ||
368 | |||
369 | return convert_to_vm_err_msg(ret); | ||
370 | } | ||
371 | |||
372 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | ||
373 | { | ||
374 | int ret; | ||
375 | |||
376 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
377 | |||
378 | /* set vm_area_struct. */ | ||
379 | ret = drm_gem_mmap(filp, vma); | ||
380 | if (ret < 0) { | ||
381 | DRM_ERROR("failed to mmap.\n"); | ||
382 | return ret; | ||
383 | } | ||
384 | |||
385 | vma->vm_flags &= ~VM_PFNMAP; | ||
386 | vma->vm_flags |= VM_MIXEDMAP; | ||
387 | |||
388 | return ret; | ||
389 | } | ||
390 | |||
391 | |||
392 | int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, | ||
393 | struct drm_device *dev, unsigned int handle) | ||
394 | { | ||
395 | int ret; | ||
396 | |||
397 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
398 | |||
399 | /* | ||
400 | * obj->refcount and obj->handle_count are decreased and | ||
401 | * if both them are 0 then exynos_drm_gem_free_object() | ||
402 | * would be called by callback to release resources. | ||
403 | */ | ||
404 | ret = drm_gem_handle_delete(file_priv, handle); | ||
405 | if (ret < 0) { | ||
406 | DRM_ERROR("failed to delete drm_gem_handle.\n"); | ||
407 | return ret; | ||
408 | } | ||
409 | |||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
414 | MODULE_DESCRIPTION("Samsung SoC DRM GEM Module"); | ||
415 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h new file mode 100644 index 000000000000..e5fc0148277b --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /* exynos_drm_gem.h | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authoer: Inki Dae <inki.dae@samsung.com> | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the next | ||
14 | * paragraph) shall be included in all copies or substantial portions of the | ||
15 | * Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
20 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
23 | * OTHER DEALINGS IN THE SOFTWARE. | ||
24 | */ | ||
25 | |||
26 | #ifndef _EXYNOS_DRM_GEM_H_ | ||
27 | #define _EXYNOS_DRM_GEM_H_ | ||
28 | |||
29 | #define to_exynos_gem_obj(x) container_of(x,\ | ||
30 | struct exynos_drm_gem_obj, base) | ||
31 | |||
32 | /* | ||
33 | * exynos drm buffer structure. | ||
34 | * | ||
35 | * @base: a gem object. | ||
36 | * - a new handle to this gem object would be created | ||
37 | * by drm_gem_handle_create(). | ||
38 | * @entry: pointer to exynos drm buffer entry object. | ||
39 | * - containing the information to physically | ||
40 | * continuous memory region allocated by user request | ||
41 | * or at framebuffer creation. | ||
42 | * | ||
43 | * P.S. this object would be transfered to user as kms_bo.handle so | ||
44 | * user can access the buffer through kms_bo.handle. | ||
45 | */ | ||
46 | struct exynos_drm_gem_obj { | ||
47 | struct drm_gem_object base; | ||
48 | struct exynos_drm_buf_entry *entry; | ||
49 | }; | ||
50 | |||
51 | /* create a new buffer and get a new gem handle. */ | ||
52 | struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv, | ||
53 | struct drm_device *dev, unsigned int size, | ||
54 | unsigned int *handle); | ||
55 | |||
56 | /* | ||
57 | * request gem object creation and buffer allocation as the size | ||
58 | * that it is calculated with framebuffer information such as width, | ||
59 | * height and bpp. | ||
60 | */ | ||
61 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, | ||
62 | struct drm_file *file_priv); | ||
63 | |||
64 | /* get buffer offset to map to user space. */ | ||
65 | int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, | ||
66 | struct drm_file *file_priv); | ||
67 | |||
68 | /* unmap a buffer from user space. */ | ||
69 | int exynos_drm_gem_munmap_ioctl(struct drm_device *dev, void *data, | ||
70 | struct drm_file *file_priv); | ||
71 | |||
72 | /* initialize gem object. */ | ||
73 | int exynos_drm_gem_init_object(struct drm_gem_object *obj); | ||
74 | |||
75 | /* free gem object. */ | ||
76 | void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj); | ||
77 | |||
78 | /* create memory region for drm framebuffer. */ | ||
79 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | ||
80 | struct drm_device *dev, struct drm_mode_create_dumb *args); | ||
81 | |||
82 | /* map memory region for drm framebuffer to user space. */ | ||
83 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, | ||
84 | struct drm_device *dev, uint32_t handle, uint64_t *offset); | ||
85 | |||
86 | /* page fault handler and mmap fault address(virtual) to physical memory. */ | ||
87 | int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); | ||
88 | |||
89 | /* | ||
90 | * mmap the physically continuous memory that a gem object contains | ||
91 | * to user space. | ||
92 | */ | ||
93 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | ||
94 | struct drm_file *file_priv); | ||
95 | |||
96 | /* set vm_flags and we can change the vm attribute to other one at here. */ | ||
97 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | ||
98 | |||
99 | /* | ||
100 | * destroy memory region allocated. | ||
101 | * - a gem handle and physical memory region pointed by a gem object | ||
102 | * would be released by drm_gem_handle_delete(). | ||
103 | */ | ||
104 | int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, | ||
105 | struct drm_device *dev, unsigned int handle); | ||
106 | |||
107 | #endif | ||