diff options
author | Dave Airlie <airlied@redhat.com> | 2012-01-03 04:25:18 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2012-01-03 04:25:26 -0500 |
commit | 863f78b5ff7f0e897f26973de882cfca04453f5d (patch) | |
tree | 864dbd5eb72fb7ac1865a7aa72dce3dd5f7a4c73 | |
parent | 5c2a5ce689c99037771a6c110374461781a6f042 (diff) | |
parent | d84083268bd707ebb8ed2f4fc26ebc7a0c453a83 (diff) |
Merge branch 'exynos-drm-next' of git://git.infradead.org/users/kmpark/linux-samsung into drm-core-next
these patch sets include the following features:
- add Samsung SoC Exynos based HDMI support.
- add pm feature for fimd driver.
- add multi buffer plane pixel formats to drm/drm_fourcc.h.
multi buffer plane pixel format has seperated memory spaces for each
plane. for exampme, NV12M has Y plane and CbCr plane and these are in
non-continuous memory region. compared with NV12, NV12M's memory shape
is like following.
NV12 : ______(Y)(CbCr)_______
NV12M : __(Y)_ ..... _(CbCr)__
- bug fix to vblank.
- code clean to exynos gem framework.
* 'exynos-drm-next' of git://git.infradead.org/users/kmpark/linux-samsung:
drm/exynos: added hdmi display support
drm/exynos: added mutex lock and code clean.
drm/exynos: extend vblank off delay time.
drm/exynos: change driver name.
drm/exynos: Support multi buffers
drm: Add multi buffer plane pixel formats
drm/exynos: added pm support.
drm/exynos: remove buffer creation of fbdev from drm framebuffer creation
drm/exynos: Split creation of gem object and gem handle
drm/exynos: Fix a fake mmap offset creation
drm/exynos: gem code cleanup
26 files changed, 3854 insertions, 304 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 847466aab435..f9aaa56eae07 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig | |||
@@ -18,3 +18,10 @@ config DRM_EXYNOS_FIMD | |||
18 | help | 18 | help |
19 | Choose this option if you want to use Exynos FIMD for DRM. | 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 | 20 | If M is selected, the module will be called exynos_drm_fimd |
21 | |||
22 | config DRM_EXYNOS_HDMI | ||
23 | tristate "Exynos DRM HDMI" | ||
24 | depends on DRM_EXYNOS | ||
25 | help | ||
26 | Choose this option if you want to use Exynos HDMI for DRM. | ||
27 | If M is selected, the module will be called exynos_drm_hdmi | ||
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index c99127214f8e..395e69c9a96e 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile | |||
@@ -10,3 +10,5 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ | |||
10 | 10 | ||
11 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o | 11 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o |
12 | obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o | 12 | obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o |
13 | obj-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \ | ||
14 | exynos_hdmiphy.o exynos_drm_hdmi.o | ||
diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c new file mode 100644 index 000000000000..84b614fe26fd --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_ddc.c | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
3 | * Authors: | ||
4 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include "drmP.h" | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/module.h> | ||
19 | |||
20 | |||
21 | #include "exynos_drm_drv.h" | ||
22 | #include "exynos_hdmi.h" | ||
23 | |||
24 | static int s5p_ddc_probe(struct i2c_client *client, | ||
25 | const struct i2c_device_id *dev_id) | ||
26 | { | ||
27 | hdmi_attach_ddc_client(client); | ||
28 | |||
29 | dev_info(&client->adapter->dev, "attached s5p_ddc " | ||
30 | "into i2c adapter successfully\n"); | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static int s5p_ddc_remove(struct i2c_client *client) | ||
36 | { | ||
37 | dev_info(&client->adapter->dev, "detached s5p_ddc " | ||
38 | "from i2c adapter successfully\n"); | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static struct i2c_device_id ddc_idtable[] = { | ||
44 | {"s5p_ddc", 0}, | ||
45 | { }, | ||
46 | }; | ||
47 | |||
48 | struct i2c_driver ddc_driver = { | ||
49 | .driver = { | ||
50 | .name = "s5p_ddc", | ||
51 | .owner = THIS_MODULE, | ||
52 | }, | ||
53 | .id_table = ddc_idtable, | ||
54 | .probe = s5p_ddc_probe, | ||
55 | .remove = __devexit_p(s5p_ddc_remove), | ||
56 | .command = NULL, | ||
57 | }; | ||
58 | EXPORT_SYMBOL(ddc_driver); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 2bb07bca511a..3cf785c58186 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c | |||
@@ -73,7 +73,7 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, | |||
73 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | 73 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); |
74 | if (!buffer) { | 74 | if (!buffer) { |
75 | DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); | 75 | DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); |
76 | return ERR_PTR(-ENOMEM); | 76 | return NULL; |
77 | } | 77 | } |
78 | 78 | ||
79 | buffer->size = size; | 79 | buffer->size = size; |
@@ -84,8 +84,7 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, | |||
84 | */ | 84 | */ |
85 | if (lowlevel_buffer_allocate(dev, buffer) < 0) { | 85 | if (lowlevel_buffer_allocate(dev, buffer) < 0) { |
86 | kfree(buffer); | 86 | kfree(buffer); |
87 | buffer = NULL; | 87 | return NULL; |
88 | return ERR_PTR(-ENOMEM); | ||
89 | } | 88 | } |
90 | 89 | ||
91 | return buffer; | 90 | return buffer; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h index 6e91f9caa5db..c913f2bad760 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.h +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.h | |||
@@ -30,9 +30,6 @@ | |||
30 | struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, | 30 | struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, |
31 | unsigned int size); | 31 | unsigned int size); |
32 | 32 | ||
33 | /* get memory information of a drm framebuffer. */ | ||
34 | struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb); | ||
35 | |||
36 | /* remove allocated physical memory. */ | 33 | /* remove allocated physical memory. */ |
37 | void exynos_drm_buf_destroy(struct drm_device *dev, | 34 | void exynos_drm_buf_destroy(struct drm_device *dev, |
38 | struct exynos_drm_gem_buf *buffer); | 35 | struct exynos_drm_gem_buf *buffer); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index e1ce9fd5a160..e3861ac49295 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c | |||
@@ -34,7 +34,6 @@ | |||
34 | #include "exynos_drm_fb.h" | 34 | #include "exynos_drm_fb.h" |
35 | #include "exynos_drm_encoder.h" | 35 | #include "exynos_drm_encoder.h" |
36 | #include "exynos_drm_gem.h" | 36 | #include "exynos_drm_gem.h" |
37 | #include "exynos_drm_buf.h" | ||
38 | 37 | ||
39 | #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ | 38 | #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ |
40 | drm_crtc) | 39 | drm_crtc) |
@@ -80,19 +79,23 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, | |||
80 | struct exynos_drm_gem_buf *buffer; | 79 | struct exynos_drm_gem_buf *buffer; |
81 | unsigned int actual_w; | 80 | unsigned int actual_w; |
82 | unsigned int actual_h; | 81 | unsigned int actual_h; |
82 | int nr = exynos_drm_format_num_buffers(fb->pixel_format); | ||
83 | int i; | ||
84 | |||
85 | for (i = 0; i < nr; i++) { | ||
86 | buffer = exynos_drm_fb_buffer(fb, i); | ||
87 | if (!buffer) { | ||
88 | DRM_LOG_KMS("buffer is null\n"); | ||
89 | return -EFAULT; | ||
90 | } | ||
83 | 91 | ||
84 | buffer = exynos_drm_fb_get_buf(fb); | 92 | overlay->dma_addr[i] = buffer->dma_addr; |
85 | if (!buffer) { | 93 | overlay->vaddr[i] = buffer->kvaddr; |
86 | DRM_LOG_KMS("buffer is null.\n"); | ||
87 | return -EFAULT; | ||
88 | } | ||
89 | |||
90 | overlay->dma_addr = buffer->dma_addr; | ||
91 | overlay->vaddr = buffer->kvaddr; | ||
92 | 94 | ||
93 | DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n", | 95 | DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", |
94 | (unsigned long)overlay->vaddr, | 96 | i, (unsigned long)overlay->vaddr[i], |
95 | (unsigned long)overlay->dma_addr); | 97 | (unsigned long)overlay->dma_addr[i]); |
98 | } | ||
96 | 99 | ||
97 | actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); | 100 | actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); |
98 | actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); | 101 | actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); |
@@ -104,6 +107,7 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, | |||
104 | overlay->fb_height = fb->height; | 107 | overlay->fb_height = fb->height; |
105 | overlay->bpp = fb->bits_per_pixel; | 108 | overlay->bpp = fb->bits_per_pixel; |
106 | overlay->pitch = fb->pitches[0]; | 109 | overlay->pitch = fb->pitches[0]; |
110 | overlay->pixel_format = fb->pixel_format; | ||
107 | 111 | ||
108 | /* set overlay range to be displayed. */ | 112 | /* set overlay range to be displayed. */ |
109 | overlay->crtc_x = pos->crtc_x; | 113 | overlay->crtc_x = pos->crtc_x; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 050684ceab9d..35889ca255e9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
@@ -38,12 +38,14 @@ | |||
38 | #include "exynos_drm_gem.h" | 38 | #include "exynos_drm_gem.h" |
39 | #include "exynos_drm_plane.h" | 39 | #include "exynos_drm_plane.h" |
40 | 40 | ||
41 | #define DRIVER_NAME "exynos-drm" | 41 | #define DRIVER_NAME "exynos" |
42 | #define DRIVER_DESC "Samsung SoC DRM" | 42 | #define DRIVER_DESC "Samsung SoC DRM" |
43 | #define DRIVER_DATE "20110530" | 43 | #define DRIVER_DATE "20110530" |
44 | #define DRIVER_MAJOR 1 | 44 | #define DRIVER_MAJOR 1 |
45 | #define DRIVER_MINOR 0 | 45 | #define DRIVER_MINOR 0 |
46 | 46 | ||
47 | #define VBLANK_OFF_DELAY 50000 | ||
48 | |||
47 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) | 49 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) |
48 | { | 50 | { |
49 | struct exynos_drm_private *private; | 51 | struct exynos_drm_private *private; |
@@ -107,6 +109,8 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) | |||
107 | goto err_drm_device; | 109 | goto err_drm_device; |
108 | } | 110 | } |
109 | 111 | ||
112 | drm_vblank_offdelay = VBLANK_OFF_DELAY; | ||
113 | |||
110 | return 0; | 114 | return 0; |
111 | 115 | ||
112 | err_drm_device: | 116 | err_drm_device: |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 8e8d8f0f8f33..e685e1e33055 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h | |||
@@ -34,12 +34,15 @@ | |||
34 | 34 | ||
35 | #define MAX_CRTC 2 | 35 | #define MAX_CRTC 2 |
36 | #define MAX_PLANE 5 | 36 | #define MAX_PLANE 5 |
37 | #define MAX_FB_BUFFER 3 | ||
37 | #define DEFAULT_ZPOS -1 | 38 | #define DEFAULT_ZPOS -1 |
38 | 39 | ||
39 | struct drm_device; | 40 | struct drm_device; |
40 | struct exynos_drm_overlay; | 41 | struct exynos_drm_overlay; |
41 | struct drm_connector; | 42 | struct drm_connector; |
42 | 43 | ||
44 | extern unsigned int drm_vblank_offdelay; | ||
45 | |||
43 | /* this enumerates display type. */ | 46 | /* this enumerates display type. */ |
44 | enum exynos_drm_output_type { | 47 | enum exynos_drm_output_type { |
45 | EXYNOS_DISPLAY_TYPE_NONE, | 48 | EXYNOS_DISPLAY_TYPE_NONE, |
@@ -82,9 +85,10 @@ struct exynos_drm_overlay_ops { | |||
82 | * @scan_flag: interlace or progressive way. | 85 | * @scan_flag: interlace or progressive way. |
83 | * (it could be DRM_MODE_FLAG_*) | 86 | * (it could be DRM_MODE_FLAG_*) |
84 | * @bpp: pixel size.(in bit) | 87 | * @bpp: pixel size.(in bit) |
85 | * @dma_addr: bus(accessed by dma) address to the memory region allocated | 88 | * @pixel_format: fourcc pixel format of this overlay |
86 | * for a overlay. | 89 | * @dma_addr: array of bus(accessed by dma) address to the memory region |
87 | * @vaddr: virtual memory addresss to this overlay. | 90 | * allocated for a overlay. |
91 | * @vaddr: array of virtual memory addresss to this overlay. | ||
88 | * @zpos: order of overlay layer(z position). | 92 | * @zpos: order of overlay layer(z position). |
89 | * @default_win: a window to be enabled. | 93 | * @default_win: a window to be enabled. |
90 | * @color_key: color key on or off. | 94 | * @color_key: color key on or off. |
@@ -112,8 +116,9 @@ struct exynos_drm_overlay { | |||
112 | unsigned int scan_flag; | 116 | unsigned int scan_flag; |
113 | unsigned int bpp; | 117 | unsigned int bpp; |
114 | unsigned int pitch; | 118 | unsigned int pitch; |
115 | dma_addr_t dma_addr; | 119 | uint32_t pixel_format; |
116 | void __iomem *vaddr; | 120 | dma_addr_t dma_addr[MAX_FB_BUFFER]; |
121 | void __iomem *vaddr[MAX_FB_BUFFER]; | ||
117 | int zpos; | 122 | int zpos; |
118 | 123 | ||
119 | bool default_win; | 124 | bool default_win; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index df5eec6c1aba..3733fe6723d3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c | |||
@@ -33,7 +33,6 @@ | |||
33 | 33 | ||
34 | #include "exynos_drm_drv.h" | 34 | #include "exynos_drm_drv.h" |
35 | #include "exynos_drm_fb.h" | 35 | #include "exynos_drm_fb.h" |
36 | #include "exynos_drm_buf.h" | ||
37 | #include "exynos_drm_gem.h" | 36 | #include "exynos_drm_gem.h" |
38 | 37 | ||
39 | #define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) | 38 | #define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) |
@@ -42,15 +41,11 @@ | |||
42 | * exynos specific framebuffer structure. | 41 | * exynos specific framebuffer structure. |
43 | * | 42 | * |
44 | * @fb: drm framebuffer obejct. | 43 | * @fb: drm framebuffer obejct. |
45 | * @exynos_gem_obj: exynos specific gem object containing a gem object. | 44 | * @exynos_gem_obj: array of exynos specific gem object containing a gem object. |
46 | * @buffer: pointer to exynos_drm_gem_buffer object. | ||
47 | * - contain the memory information to memory region allocated | ||
48 | * at default framebuffer creation. | ||
49 | */ | 45 | */ |
50 | struct exynos_drm_fb { | 46 | struct exynos_drm_fb { |
51 | struct drm_framebuffer fb; | 47 | struct drm_framebuffer fb; |
52 | struct exynos_drm_gem_obj *exynos_gem_obj; | 48 | struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER]; |
53 | struct exynos_drm_gem_buf *buffer; | ||
54 | }; | 49 | }; |
55 | 50 | ||
56 | static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) | 51 | static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) |
@@ -61,13 +56,6 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) | |||
61 | 56 | ||
62 | drm_framebuffer_cleanup(fb); | 57 | drm_framebuffer_cleanup(fb); |
63 | 58 | ||
64 | /* | ||
65 | * default framebuffer has no gem object so | ||
66 | * a buffer of the default framebuffer should be released at here. | ||
67 | */ | ||
68 | if (!exynos_fb->exynos_gem_obj && exynos_fb->buffer) | ||
69 | exynos_drm_buf_destroy(fb->dev, exynos_fb->buffer); | ||
70 | |||
71 | kfree(exynos_fb); | 59 | kfree(exynos_fb); |
72 | exynos_fb = NULL; | 60 | exynos_fb = NULL; |
73 | } | 61 | } |
@@ -81,7 +69,7 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, | |||
81 | DRM_DEBUG_KMS("%s\n", __FILE__); | 69 | DRM_DEBUG_KMS("%s\n", __FILE__); |
82 | 70 | ||
83 | return drm_gem_handle_create(file_priv, | 71 | return drm_gem_handle_create(file_priv, |
84 | &exynos_fb->exynos_gem_obj->base, handle); | 72 | &exynos_fb->exynos_gem_obj[0]->base, handle); |
85 | } | 73 | } |
86 | 74 | ||
87 | static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, | 75 | static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, |
@@ -102,132 +90,88 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { | |||
102 | .dirty = exynos_drm_fb_dirty, | 90 | .dirty = exynos_drm_fb_dirty, |
103 | }; | 91 | }; |
104 | 92 | ||
105 | static struct drm_framebuffer * | 93 | struct drm_framebuffer * |
106 | exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, | 94 | exynos_drm_framebuffer_init(struct drm_device *dev, |
107 | struct drm_mode_fb_cmd2 *mode_cmd) | 95 | struct drm_mode_fb_cmd2 *mode_cmd, |
96 | struct drm_gem_object *obj) | ||
108 | { | 97 | { |
109 | struct exynos_drm_fb *exynos_fb; | 98 | struct exynos_drm_fb *exynos_fb; |
110 | struct drm_framebuffer *fb; | ||
111 | struct exynos_drm_gem_obj *exynos_gem_obj = NULL; | ||
112 | struct drm_gem_object *obj; | ||
113 | unsigned int size; | ||
114 | int ret; | 99 | int ret; |
115 | 100 | ||
116 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
117 | |||
118 | DRM_LOG_KMS("drm fb create(%dx%d)\n", | ||
119 | mode_cmd->width, mode_cmd->height); | ||
120 | |||
121 | exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); | 101 | exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); |
122 | if (!exynos_fb) { | 102 | if (!exynos_fb) { |
123 | DRM_ERROR("failed to allocate exynos drm framebuffer.\n"); | 103 | DRM_ERROR("failed to allocate exynos drm framebuffer\n"); |
124 | return ERR_PTR(-ENOMEM); | 104 | return ERR_PTR(-ENOMEM); |
125 | } | 105 | } |
126 | 106 | ||
127 | fb = &exynos_fb->fb; | 107 | ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); |
128 | ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs); | ||
129 | if (ret) { | 108 | if (ret) { |
130 | DRM_ERROR("failed to initialize framebuffer.\n"); | 109 | DRM_ERROR("failed to initialize framebuffer\n"); |
131 | goto err_init; | 110 | return ERR_PTR(ret); |
132 | } | 111 | } |
133 | 112 | ||
134 | DRM_LOG_KMS("create: fb id: %d\n", fb->base.id); | 113 | drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); |
114 | exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj); | ||
135 | 115 | ||
136 | size = mode_cmd->pitches[0] * mode_cmd->height; | 116 | return &exynos_fb->fb; |
117 | } | ||
137 | 118 | ||
138 | /* | 119 | static struct drm_framebuffer * |
139 | * mode_cmd->handles[0] could be NULL at booting time or | 120 | exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, |
140 | * with user request. if NULL, a new buffer or a gem object | 121 | struct drm_mode_fb_cmd2 *mode_cmd) |
141 | * would be allocated. | 122 | { |
142 | */ | 123 | struct drm_gem_object *obj; |
143 | if (!mode_cmd->handles[0]) { | 124 | struct drm_framebuffer *fb; |
144 | if (!file_priv) { | 125 | struct exynos_drm_fb *exynos_fb; |
145 | struct exynos_drm_gem_buf *buffer; | 126 | int nr; |
146 | 127 | int i; | |
147 | /* | ||
148 | * in case that file_priv is NULL, it allocates | ||
149 | * only buffer and this buffer would be used | ||
150 | * for default framebuffer. | ||
151 | */ | ||
152 | buffer = exynos_drm_buf_create(dev, size); | ||
153 | if (IS_ERR(buffer)) { | ||
154 | ret = PTR_ERR(buffer); | ||
155 | goto err_buffer; | ||
156 | } | ||
157 | |||
158 | exynos_fb->buffer = buffer; | ||
159 | |||
160 | DRM_LOG_KMS("default: dma_addr = 0x%lx, size = 0x%x\n", | ||
161 | (unsigned long)buffer->dma_addr, size); | ||
162 | |||
163 | goto out; | ||
164 | } else { | ||
165 | exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, | ||
166 | &mode_cmd->handles[0], | ||
167 | size); | ||
168 | if (IS_ERR(exynos_gem_obj)) { | ||
169 | ret = PTR_ERR(exynos_gem_obj); | ||
170 | goto err_buffer; | ||
171 | } | ||
172 | } | ||
173 | } else { | ||
174 | obj = drm_gem_object_lookup(dev, file_priv, | ||
175 | mode_cmd->handles[0]); | ||
176 | if (!obj) { | ||
177 | DRM_ERROR("failed to lookup gem object.\n"); | ||
178 | goto err_buffer; | ||
179 | } | ||
180 | 128 | ||
181 | exynos_gem_obj = to_exynos_gem_obj(obj); | 129 | DRM_DEBUG_KMS("%s\n", __FILE__); |
182 | 130 | ||
183 | drm_gem_object_unreference_unlocked(obj); | 131 | obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); |
132 | if (!obj) { | ||
133 | DRM_ERROR("failed to lookup gem object\n"); | ||
134 | return ERR_PTR(-ENOENT); | ||
184 | } | 135 | } |
185 | 136 | ||
186 | /* | 137 | drm_gem_object_unreference_unlocked(obj); |
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->buffer = exynos_gem_obj->buffer; | ||
193 | |||
194 | DRM_LOG_KMS("dma_addr = 0x%lx, size = 0x%x, gem object = 0x%x\n", | ||
195 | (unsigned long)exynos_fb->buffer->dma_addr, size, | ||
196 | (unsigned int)&exynos_gem_obj->base); | ||
197 | 138 | ||
198 | out: | 139 | fb = exynos_drm_framebuffer_init(dev, mode_cmd, obj); |
199 | exynos_fb->exynos_gem_obj = exynos_gem_obj; | 140 | if (IS_ERR(fb)) |
141 | return fb; | ||
200 | 142 | ||
201 | drm_helper_mode_fill_fb_struct(fb, mode_cmd); | 143 | exynos_fb = to_exynos_fb(fb); |
144 | nr = exynos_drm_format_num_buffers(fb->pixel_format); | ||
202 | 145 | ||
203 | return fb; | 146 | for (i = 1; i < nr; i++) { |
204 | 147 | obj = drm_gem_object_lookup(dev, file_priv, | |
205 | err_buffer: | 148 | mode_cmd->handles[i]); |
206 | drm_framebuffer_cleanup(fb); | 149 | if (!obj) { |
207 | 150 | DRM_ERROR("failed to lookup gem object\n"); | |
208 | err_init: | 151 | exynos_drm_fb_destroy(fb); |
209 | kfree(exynos_fb); | 152 | return ERR_PTR(-ENOENT); |
153 | } | ||
210 | 154 | ||
211 | return ERR_PTR(ret); | 155 | drm_gem_object_unreference_unlocked(obj); |
212 | } | ||
213 | 156 | ||
214 | struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, | 157 | exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj); |
215 | struct drm_file *file_priv, | 158 | } |
216 | struct drm_mode_fb_cmd2 *mode_cmd) | ||
217 | { | ||
218 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
219 | 159 | ||
220 | return exynos_drm_fb_init(file_priv, dev, mode_cmd); | 160 | return fb; |
221 | } | 161 | } |
222 | 162 | ||
223 | struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb) | 163 | struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, |
164 | int index) | ||
224 | { | 165 | { |
225 | struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); | 166 | struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); |
226 | struct exynos_drm_gem_buf *buffer; | 167 | struct exynos_drm_gem_buf *buffer; |
227 | 168 | ||
228 | DRM_DEBUG_KMS("%s\n", __FILE__); | 169 | DRM_DEBUG_KMS("%s\n", __FILE__); |
229 | 170 | ||
230 | buffer = exynos_fb->buffer; | 171 | if (index >= MAX_FB_BUFFER) |
172 | return NULL; | ||
173 | |||
174 | buffer = exynos_fb->exynos_gem_obj[index]->buffer; | ||
231 | if (!buffer) | 175 | if (!buffer) |
232 | return NULL; | 176 | return NULL; |
233 | 177 | ||
@@ -248,7 +192,7 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev) | |||
248 | } | 192 | } |
249 | 193 | ||
250 | static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { | 194 | static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { |
251 | .fb_create = exynos_drm_fb_create, | 195 | .fb_create = exynos_user_fb_create, |
252 | .output_poll_changed = exynos_drm_output_poll_changed, | 196 | .output_poll_changed = exynos_drm_output_poll_changed, |
253 | }; | 197 | }; |
254 | 198 | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h index 52c5982bdbbc..3ecb30d93552 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h | |||
@@ -28,9 +28,27 @@ | |||
28 | #ifndef _EXYNOS_DRM_FB_H_ | 28 | #ifndef _EXYNOS_DRM_FB_H_ |
29 | #define _EXYNOS_DRM_FB_H | 29 | #define _EXYNOS_DRM_FB_H |
30 | 30 | ||
31 | struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, | 31 | static inline int exynos_drm_format_num_buffers(uint32_t format) |
32 | struct drm_file *filp, | 32 | { |
33 | struct drm_mode_fb_cmd2 *mode_cmd); | 33 | switch (format) { |
34 | case DRM_FORMAT_NV12M: | ||
35 | case DRM_FORMAT_NV12MT: | ||
36 | return 2; | ||
37 | case DRM_FORMAT_YUV420M: | ||
38 | return 3; | ||
39 | default: | ||
40 | return 1; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | struct drm_framebuffer * | ||
45 | exynos_drm_framebuffer_init(struct drm_device *dev, | ||
46 | struct drm_mode_fb_cmd2 *mode_cmd, | ||
47 | struct drm_gem_object *obj); | ||
48 | |||
49 | /* get memory information of a drm framebuffer */ | ||
50 | struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, | ||
51 | int index); | ||
34 | 52 | ||
35 | void exynos_drm_mode_config_init(struct drm_device *dev); | 53 | void exynos_drm_mode_config_init(struct drm_device *dev); |
36 | 54 | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index c8b278447c4f..d7ae29d2f3d6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c | |||
@@ -34,7 +34,6 @@ | |||
34 | #include "exynos_drm_drv.h" | 34 | #include "exynos_drm_drv.h" |
35 | #include "exynos_drm_fb.h" | 35 | #include "exynos_drm_fb.h" |
36 | #include "exynos_drm_gem.h" | 36 | #include "exynos_drm_gem.h" |
37 | #include "exynos_drm_buf.h" | ||
38 | 37 | ||
39 | #define MAX_CONNECTOR 4 | 38 | #define MAX_CONNECTOR 4 |
40 | #define PREFERRED_BPP 32 | 39 | #define PREFERRED_BPP 32 |
@@ -43,8 +42,8 @@ | |||
43 | drm_fb_helper) | 42 | drm_fb_helper) |
44 | 43 | ||
45 | struct exynos_drm_fbdev { | 44 | struct exynos_drm_fbdev { |
46 | struct drm_fb_helper drm_fb_helper; | 45 | struct drm_fb_helper drm_fb_helper; |
47 | struct drm_framebuffer *fb; | 46 | struct exynos_drm_gem_obj *exynos_gem_obj; |
48 | }; | 47 | }; |
49 | 48 | ||
50 | static int exynos_drm_fbdev_set_par(struct fb_info *info) | 49 | static int exynos_drm_fbdev_set_par(struct fb_info *info) |
@@ -90,19 +89,17 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, | |||
90 | { | 89 | { |
91 | struct fb_info *fbi = helper->fbdev; | 90 | struct fb_info *fbi = helper->fbdev; |
92 | struct drm_device *dev = helper->dev; | 91 | struct drm_device *dev = helper->dev; |
93 | struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper); | ||
94 | struct exynos_drm_gem_buf *buffer; | 92 | struct exynos_drm_gem_buf *buffer; |
95 | unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); | 93 | unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); |
96 | unsigned long offset; | 94 | unsigned long offset; |
97 | 95 | ||
98 | DRM_DEBUG_KMS("%s\n", __FILE__); | 96 | DRM_DEBUG_KMS("%s\n", __FILE__); |
99 | 97 | ||
100 | exynos_fb->fb = fb; | ||
101 | |||
102 | drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); | 98 | drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); |
103 | drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); | 99 | drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); |
104 | 100 | ||
105 | buffer = exynos_drm_fb_get_buf(fb); | 101 | /* RGB formats use only one buffer */ |
102 | buffer = exynos_drm_fb_buffer(fb, 0); | ||
106 | if (!buffer) { | 103 | if (!buffer) { |
107 | DRM_LOG_KMS("buffer is null.\n"); | 104 | DRM_LOG_KMS("buffer is null.\n"); |
108 | return -EFAULT; | 105 | return -EFAULT; |
@@ -124,10 +121,12 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | |||
124 | struct drm_fb_helper_surface_size *sizes) | 121 | struct drm_fb_helper_surface_size *sizes) |
125 | { | 122 | { |
126 | struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); | 123 | struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); |
124 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
127 | struct drm_device *dev = helper->dev; | 125 | struct drm_device *dev = helper->dev; |
128 | struct fb_info *fbi; | 126 | struct fb_info *fbi; |
129 | struct drm_mode_fb_cmd2 mode_cmd = { 0 }; | 127 | struct drm_mode_fb_cmd2 mode_cmd = { 0 }; |
130 | struct platform_device *pdev = dev->platformdev; | 128 | struct platform_device *pdev = dev->platformdev; |
129 | unsigned long size; | ||
131 | int ret; | 130 | int ret; |
132 | 131 | ||
133 | DRM_DEBUG_KMS("%s\n", __FILE__); | 132 | DRM_DEBUG_KMS("%s\n", __FILE__); |
@@ -151,14 +150,23 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | |||
151 | goto out; | 150 | goto out; |
152 | } | 151 | } |
153 | 152 | ||
154 | exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd); | 153 | size = mode_cmd.pitches[0] * mode_cmd.height; |
155 | if (IS_ERR_OR_NULL(exynos_fbdev->fb)) { | 154 | exynos_gem_obj = exynos_drm_gem_create(dev, size); |
155 | if (IS_ERR(exynos_gem_obj)) { | ||
156 | ret = PTR_ERR(exynos_gem_obj); | ||
157 | goto out; | ||
158 | } | ||
159 | |||
160 | exynos_fbdev->exynos_gem_obj = exynos_gem_obj; | ||
161 | |||
162 | helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, | ||
163 | &exynos_gem_obj->base); | ||
164 | if (IS_ERR_OR_NULL(helper->fb)) { | ||
156 | DRM_ERROR("failed to create drm framebuffer.\n"); | 165 | DRM_ERROR("failed to create drm framebuffer.\n"); |
157 | ret = PTR_ERR(exynos_fbdev->fb); | 166 | ret = PTR_ERR(helper->fb); |
158 | goto out; | 167 | goto out; |
159 | } | 168 | } |
160 | 169 | ||
161 | helper->fb = exynos_fbdev->fb; | ||
162 | helper->fbdev = fbi; | 170 | helper->fbdev = fbi; |
163 | 171 | ||
164 | fbi->par = helper; | 172 | fbi->par = helper; |
@@ -172,8 +180,10 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | |||
172 | } | 180 | } |
173 | 181 | ||
174 | ret = exynos_drm_fbdev_update(helper, helper->fb); | 182 | ret = exynos_drm_fbdev_update(helper, helper->fb); |
175 | if (ret < 0) | 183 | if (ret < 0) { |
176 | fb_dealloc_cmap(&fbi->cmap); | 184 | fb_dealloc_cmap(&fbi->cmap); |
185 | goto out; | ||
186 | } | ||
177 | 187 | ||
178 | /* | 188 | /* |
179 | * if failed, all resources allocated above would be released by | 189 | * if failed, all resources allocated above would be released by |
@@ -206,16 +216,13 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, | |||
206 | { | 216 | { |
207 | struct drm_device *dev = helper->dev; | 217 | struct drm_device *dev = helper->dev; |
208 | struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); | 218 | struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); |
209 | struct drm_framebuffer *fb = exynos_fbdev->fb; | 219 | struct exynos_drm_gem_obj *exynos_gem_obj; |
220 | struct drm_framebuffer *fb = helper->fb; | ||
210 | struct drm_mode_fb_cmd2 mode_cmd = { 0 }; | 221 | struct drm_mode_fb_cmd2 mode_cmd = { 0 }; |
222 | unsigned long size; | ||
211 | 223 | ||
212 | DRM_DEBUG_KMS("%s\n", __FILE__); | 224 | DRM_DEBUG_KMS("%s\n", __FILE__); |
213 | 225 | ||
214 | if (helper->fb != fb) { | ||
215 | DRM_ERROR("drm framebuffer is different\n"); | ||
216 | return -EINVAL; | ||
217 | } | ||
218 | |||
219 | if (exynos_drm_fbdev_is_samefb(fb, sizes)) | 226 | if (exynos_drm_fbdev_is_samefb(fb, sizes)) |
220 | return 0; | 227 | return 0; |
221 | 228 | ||
@@ -225,16 +232,26 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, | |||
225 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | 232 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, |
226 | sizes->surface_depth); | 233 | sizes->surface_depth); |
227 | 234 | ||
235 | if (exynos_fbdev->exynos_gem_obj) | ||
236 | exynos_drm_gem_destroy(exynos_fbdev->exynos_gem_obj); | ||
237 | |||
228 | if (fb->funcs->destroy) | 238 | if (fb->funcs->destroy) |
229 | fb->funcs->destroy(fb); | 239 | fb->funcs->destroy(fb); |
230 | 240 | ||
231 | exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd); | 241 | size = mode_cmd.pitches[0] * mode_cmd.height; |
232 | if (IS_ERR(exynos_fbdev->fb)) { | 242 | exynos_gem_obj = exynos_drm_gem_create(dev, size); |
233 | DRM_ERROR("failed to allocate fb.\n"); | 243 | if (IS_ERR(exynos_gem_obj)) |
234 | return PTR_ERR(exynos_fbdev->fb); | 244 | return PTR_ERR(exynos_gem_obj); |
245 | |||
246 | exynos_fbdev->exynos_gem_obj = exynos_gem_obj; | ||
247 | |||
248 | helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, | ||
249 | &exynos_gem_obj->base); | ||
250 | if (IS_ERR_OR_NULL(helper->fb)) { | ||
251 | DRM_ERROR("failed to create drm framebuffer.\n"); | ||
252 | return PTR_ERR(helper->fb); | ||
235 | } | 253 | } |
236 | 254 | ||
237 | helper->fb = exynos_fbdev->fb; | ||
238 | return exynos_drm_fbdev_update(helper, helper->fb); | 255 | return exynos_drm_fbdev_update(helper, helper->fb); |
239 | } | 256 | } |
240 | 257 | ||
@@ -368,6 +385,9 @@ void exynos_drm_fbdev_fini(struct drm_device *dev) | |||
368 | 385 | ||
369 | fbdev = to_exynos_fbdev(private->fb_helper); | 386 | fbdev = to_exynos_fbdev(private->fb_helper); |
370 | 387 | ||
388 | if (fbdev->exynos_gem_obj) | ||
389 | exynos_drm_gem_destroy(fbdev->exynos_gem_obj); | ||
390 | |||
371 | exynos_drm_fbdev_destroy(dev, private->fb_helper); | 391 | exynos_drm_fbdev_destroy(dev, private->fb_helper); |
372 | kfree(fbdev); | 392 | kfree(fbdev); |
373 | private->fb_helper = NULL; | 393 | private->fb_helper = NULL; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index fe4172e48ad2..ca83139cd309 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c | |||
@@ -87,6 +87,7 @@ struct fimd_context { | |||
87 | u32 vidcon0; | 87 | u32 vidcon0; |
88 | u32 vidcon1; | 88 | u32 vidcon1; |
89 | bool suspended; | 89 | bool suspended; |
90 | struct mutex lock; | ||
90 | 91 | ||
91 | struct fb_videomode *timing; | 92 | struct fb_videomode *timing; |
92 | }; | 93 | }; |
@@ -137,11 +138,22 @@ static struct exynos_drm_display_ops fimd_display_ops = { | |||
137 | 138 | ||
138 | static void fimd_dpms(struct device *subdrv_dev, int mode) | 139 | static void fimd_dpms(struct device *subdrv_dev, int mode) |
139 | { | 140 | { |
141 | struct fimd_context *ctx = get_fimd_context(subdrv_dev); | ||
142 | |||
140 | DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); | 143 | DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); |
141 | 144 | ||
145 | mutex_lock(&ctx->lock); | ||
146 | |||
142 | switch (mode) { | 147 | switch (mode) { |
143 | case DRM_MODE_DPMS_ON: | 148 | case DRM_MODE_DPMS_ON: |
144 | pm_runtime_get_sync(subdrv_dev); | 149 | /* |
150 | * enable fimd hardware only if suspended status. | ||
151 | * | ||
152 | * P.S. fimd_dpms function would be called at booting time so | ||
153 | * clk_enable could be called double time. | ||
154 | */ | ||
155 | if (ctx->suspended) | ||
156 | pm_runtime_get_sync(subdrv_dev); | ||
145 | break; | 157 | break; |
146 | case DRM_MODE_DPMS_STANDBY: | 158 | case DRM_MODE_DPMS_STANDBY: |
147 | case DRM_MODE_DPMS_SUSPEND: | 159 | case DRM_MODE_DPMS_SUSPEND: |
@@ -152,6 +164,8 @@ static void fimd_dpms(struct device *subdrv_dev, int mode) | |||
152 | DRM_DEBUG_KMS("unspecified mode %d\n", mode); | 164 | DRM_DEBUG_KMS("unspecified mode %d\n", mode); |
153 | break; | 165 | break; |
154 | } | 166 | } |
167 | |||
168 | mutex_unlock(&ctx->lock); | ||
155 | } | 169 | } |
156 | 170 | ||
157 | static void fimd_apply(struct device *subdrv_dev) | 171 | static void fimd_apply(struct device *subdrv_dev) |
@@ -181,6 +195,9 @@ static void fimd_commit(struct device *dev) | |||
181 | struct fb_videomode *timing = ctx->timing; | 195 | struct fb_videomode *timing = ctx->timing; |
182 | u32 val; | 196 | u32 val; |
183 | 197 | ||
198 | if (ctx->suspended) | ||
199 | return; | ||
200 | |||
184 | DRM_DEBUG_KMS("%s\n", __FILE__); | 201 | DRM_DEBUG_KMS("%s\n", __FILE__); |
185 | 202 | ||
186 | /* setup polarity values from machine code. */ | 203 | /* setup polarity values from machine code. */ |
@@ -310,8 +327,8 @@ static void fimd_win_mode_set(struct device *dev, | |||
310 | win_data->ovl_height = overlay->crtc_height; | 327 | win_data->ovl_height = overlay->crtc_height; |
311 | win_data->fb_width = overlay->fb_width; | 328 | win_data->fb_width = overlay->fb_width; |
312 | win_data->fb_height = overlay->fb_height; | 329 | win_data->fb_height = overlay->fb_height; |
313 | win_data->dma_addr = overlay->dma_addr + offset; | 330 | win_data->dma_addr = overlay->dma_addr[0] + offset; |
314 | win_data->vaddr = overlay->vaddr + offset; | 331 | win_data->vaddr = overlay->vaddr[0] + offset; |
315 | win_data->bpp = overlay->bpp; | 332 | win_data->bpp = overlay->bpp; |
316 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * | 333 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * |
317 | (overlay->bpp >> 3); | 334 | (overlay->bpp >> 3); |
@@ -414,6 +431,9 @@ static void fimd_win_commit(struct device *dev, int zpos) | |||
414 | 431 | ||
415 | DRM_DEBUG_KMS("%s\n", __FILE__); | 432 | DRM_DEBUG_KMS("%s\n", __FILE__); |
416 | 433 | ||
434 | if (ctx->suspended) | ||
435 | return; | ||
436 | |||
417 | if (win == DEFAULT_ZPOS) | 437 | if (win == DEFAULT_ZPOS) |
418 | win = ctx->default_win; | 438 | win = ctx->default_win; |
419 | 439 | ||
@@ -797,13 +817,6 @@ static int __devinit fimd_probe(struct platform_device *pdev) | |||
797 | goto err_req_irq; | 817 | goto err_req_irq; |
798 | } | 818 | } |
799 | 819 | ||
800 | pm_runtime_set_active(dev); | ||
801 | pm_runtime_enable(dev); | ||
802 | pm_runtime_get_sync(dev); | ||
803 | |||
804 | for (win = 0; win < WINDOWS_NR; win++) | ||
805 | fimd_clear_win(ctx, win); | ||
806 | |||
807 | ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); | 820 | ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); |
808 | ctx->vidcon0 = pdata->vidcon0; | 821 | ctx->vidcon0 = pdata->vidcon0; |
809 | ctx->vidcon1 = pdata->vidcon1; | 822 | ctx->vidcon1 = pdata->vidcon1; |
@@ -825,7 +838,17 @@ static int __devinit fimd_probe(struct platform_device *pdev) | |||
825 | subdrv->manager.display_ops = &fimd_display_ops; | 838 | subdrv->manager.display_ops = &fimd_display_ops; |
826 | subdrv->manager.dev = dev; | 839 | subdrv->manager.dev = dev; |
827 | 840 | ||
841 | mutex_init(&ctx->lock); | ||
842 | |||
828 | platform_set_drvdata(pdev, ctx); | 843 | platform_set_drvdata(pdev, ctx); |
844 | |||
845 | pm_runtime_set_active(dev); | ||
846 | pm_runtime_enable(dev); | ||
847 | pm_runtime_get_sync(dev); | ||
848 | |||
849 | for (win = 0; win < WINDOWS_NR; win++) | ||
850 | fimd_clear_win(ctx, win); | ||
851 | |||
829 | exynos_drm_subdrv_register(subdrv); | 852 | exynos_drm_subdrv_register(subdrv); |
830 | 853 | ||
831 | return 0; | 854 | return 0; |
@@ -885,6 +908,47 @@ out: | |||
885 | return 0; | 908 | return 0; |
886 | } | 909 | } |
887 | 910 | ||
911 | #ifdef CONFIG_PM_SLEEP | ||
912 | static int fimd_suspend(struct device *dev) | ||
913 | { | ||
914 | int ret; | ||
915 | |||
916 | if (pm_runtime_suspended(dev)) | ||
917 | return 0; | ||
918 | |||
919 | ret = pm_runtime_suspend(dev); | ||
920 | if (ret < 0) | ||
921 | return ret; | ||
922 | |||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | static int fimd_resume(struct device *dev) | ||
927 | { | ||
928 | int ret; | ||
929 | |||
930 | ret = pm_runtime_resume(dev); | ||
931 | if (ret < 0) { | ||
932 | DRM_ERROR("failed to resume runtime pm.\n"); | ||
933 | return ret; | ||
934 | } | ||
935 | |||
936 | pm_runtime_disable(dev); | ||
937 | |||
938 | ret = pm_runtime_set_active(dev); | ||
939 | if (ret < 0) { | ||
940 | DRM_ERROR("failed to active runtime pm.\n"); | ||
941 | pm_runtime_enable(dev); | ||
942 | pm_runtime_suspend(dev); | ||
943 | return ret; | ||
944 | } | ||
945 | |||
946 | pm_runtime_enable(dev); | ||
947 | |||
948 | return 0; | ||
949 | } | ||
950 | #endif | ||
951 | |||
888 | #ifdef CONFIG_PM_RUNTIME | 952 | #ifdef CONFIG_PM_RUNTIME |
889 | static int fimd_runtime_suspend(struct device *dev) | 953 | static int fimd_runtime_suspend(struct device *dev) |
890 | { | 954 | { |
@@ -917,11 +981,19 @@ static int fimd_runtime_resume(struct device *dev) | |||
917 | } | 981 | } |
918 | 982 | ||
919 | ctx->suspended = false; | 983 | ctx->suspended = false; |
984 | |||
985 | /* if vblank was enabled status, enable it again. */ | ||
986 | if (test_and_clear_bit(0, &ctx->irq_flags)) | ||
987 | fimd_enable_vblank(dev); | ||
988 | |||
989 | fimd_apply(dev); | ||
990 | |||
920 | return 0; | 991 | return 0; |
921 | } | 992 | } |
922 | #endif | 993 | #endif |
923 | 994 | ||
924 | static const struct dev_pm_ops fimd_pm_ops = { | 995 | static const struct dev_pm_ops fimd_pm_ops = { |
996 | SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume) | ||
925 | SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) | 997 | SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) |
926 | }; | 998 | }; |
927 | 999 | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index aba0fe47f7ea..025abb3e3b67 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c | |||
@@ -55,17 +55,54 @@ static unsigned int convert_to_vm_err_msg(int msg) | |||
55 | return out_msg; | 55 | return out_msg; |
56 | } | 56 | } |
57 | 57 | ||
58 | static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj) | 58 | static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, |
59 | struct drm_file *file_priv, | ||
60 | unsigned int *handle) | ||
59 | { | 61 | { |
62 | int ret; | ||
63 | |||
64 | /* | ||
65 | * allocate a id of idr table where the obj is registered | ||
66 | * and handle has the id what user can see. | ||
67 | */ | ||
68 | ret = drm_gem_handle_create(file_priv, obj, handle); | ||
69 | if (ret) | ||
70 | return ret; | ||
71 | |||
72 | DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); | ||
73 | |||
74 | /* drop reference from allocate - handle holds it now. */ | ||
75 | drm_gem_object_unreference_unlocked(obj); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) | ||
81 | { | ||
82 | struct drm_gem_object *obj; | ||
83 | |||
60 | DRM_DEBUG_KMS("%s\n", __FILE__); | 84 | DRM_DEBUG_KMS("%s\n", __FILE__); |
61 | 85 | ||
62 | return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT; | 86 | if (!exynos_gem_obj) |
87 | return; | ||
88 | |||
89 | obj = &exynos_gem_obj->base; | ||
90 | |||
91 | DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); | ||
92 | |||
93 | exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer); | ||
94 | |||
95 | if (obj->map_list.map) | ||
96 | drm_gem_free_mmap_offset(obj); | ||
97 | |||
98 | /* release file pointer to gem object. */ | ||
99 | drm_gem_object_release(obj); | ||
100 | |||
101 | kfree(exynos_gem_obj); | ||
63 | } | 102 | } |
64 | 103 | ||
65 | static struct exynos_drm_gem_obj | 104 | static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, |
66 | *exynos_drm_gem_init(struct drm_device *drm_dev, | 105 | unsigned long size) |
67 | struct drm_file *file_priv, unsigned int *handle, | ||
68 | unsigned int size) | ||
69 | { | 106 | { |
70 | struct exynos_drm_gem_obj *exynos_gem_obj; | 107 | struct exynos_drm_gem_obj *exynos_gem_obj; |
71 | struct drm_gem_object *obj; | 108 | struct drm_gem_object *obj; |
@@ -73,75 +110,41 @@ static struct exynos_drm_gem_obj | |||
73 | 110 | ||
74 | exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); | 111 | exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); |
75 | if (!exynos_gem_obj) { | 112 | if (!exynos_gem_obj) { |
76 | DRM_ERROR("failed to allocate exynos gem object.\n"); | 113 | DRM_ERROR("failed to allocate exynos gem object\n"); |
77 | return ERR_PTR(-ENOMEM); | 114 | return NULL; |
78 | } | 115 | } |
79 | 116 | ||
80 | obj = &exynos_gem_obj->base; | 117 | obj = &exynos_gem_obj->base; |
81 | 118 | ||
82 | ret = drm_gem_object_init(drm_dev, obj, size); | 119 | ret = drm_gem_object_init(dev, obj, size); |
83 | if (ret < 0) { | 120 | if (ret < 0) { |
84 | DRM_ERROR("failed to initialize gem object.\n"); | 121 | DRM_ERROR("failed to initialize gem object\n"); |
85 | ret = -EINVAL; | 122 | kfree(exynos_gem_obj); |
86 | goto err_object_init; | 123 | return NULL; |
87 | } | 124 | } |
88 | 125 | ||
89 | DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); | 126 | DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); |
90 | 127 | ||
91 | ret = drm_gem_create_mmap_offset(obj); | ||
92 | if (ret < 0) { | ||
93 | DRM_ERROR("failed to allocate mmap offset.\n"); | ||
94 | goto err_create_mmap_offset; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * allocate a id of idr table where the obj is registered | ||
99 | * and handle has the id what user can see. | ||
100 | */ | ||
101 | ret = drm_gem_handle_create(file_priv, obj, handle); | ||
102 | if (ret) | ||
103 | goto err_handle_create; | ||
104 | |||
105 | DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); | ||
106 | |||
107 | /* drop reference from allocate - handle holds it now. */ | ||
108 | drm_gem_object_unreference_unlocked(obj); | ||
109 | |||
110 | return exynos_gem_obj; | 128 | return exynos_gem_obj; |
111 | |||
112 | err_handle_create: | ||
113 | drm_gem_free_mmap_offset(obj); | ||
114 | |||
115 | err_create_mmap_offset: | ||
116 | drm_gem_object_release(obj); | ||
117 | |||
118 | err_object_init: | ||
119 | kfree(exynos_gem_obj); | ||
120 | |||
121 | return ERR_PTR(ret); | ||
122 | } | 129 | } |
123 | 130 | ||
124 | struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, | 131 | struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, |
125 | struct drm_file *file_priv, | 132 | unsigned long size) |
126 | unsigned int *handle, unsigned long size) | ||
127 | { | 133 | { |
128 | |||
129 | struct exynos_drm_gem_obj *exynos_gem_obj = NULL; | ||
130 | struct exynos_drm_gem_buf *buffer; | 134 | struct exynos_drm_gem_buf *buffer; |
135 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
131 | 136 | ||
132 | size = roundup(size, PAGE_SIZE); | 137 | size = roundup(size, PAGE_SIZE); |
133 | |||
134 | DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size); | 138 | DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size); |
135 | 139 | ||
136 | buffer = exynos_drm_buf_create(dev, size); | 140 | buffer = exynos_drm_buf_create(dev, size); |
137 | if (IS_ERR(buffer)) { | 141 | if (!buffer) |
138 | return ERR_CAST(buffer); | 142 | return ERR_PTR(-ENOMEM); |
139 | } | ||
140 | 143 | ||
141 | exynos_gem_obj = exynos_drm_gem_init(dev, file_priv, handle, size); | 144 | exynos_gem_obj = exynos_drm_gem_init(dev, size); |
142 | if (IS_ERR(exynos_gem_obj)) { | 145 | if (!exynos_gem_obj) { |
143 | exynos_drm_buf_destroy(dev, buffer); | 146 | exynos_drm_buf_destroy(dev, buffer); |
144 | return exynos_gem_obj; | 147 | return ERR_PTR(-ENOMEM); |
145 | } | 148 | } |
146 | 149 | ||
147 | exynos_gem_obj->buffer = buffer; | 150 | exynos_gem_obj->buffer = buffer; |
@@ -150,23 +153,30 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, | |||
150 | } | 153 | } |
151 | 154 | ||
152 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, | 155 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, |
153 | struct drm_file *file_priv) | 156 | struct drm_file *file_priv) |
154 | { | 157 | { |
155 | struct drm_exynos_gem_create *args = data; | 158 | struct drm_exynos_gem_create *args = data; |
156 | struct exynos_drm_gem_obj *exynos_gem_obj = NULL; | 159 | struct exynos_drm_gem_obj *exynos_gem_obj; |
160 | int ret; | ||
157 | 161 | ||
158 | DRM_DEBUG_KMS("%s\n", __FILE__); | 162 | DRM_DEBUG_KMS("%s\n", __FILE__); |
159 | 163 | ||
160 | exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, | 164 | exynos_gem_obj = exynos_drm_gem_create(dev, args->size); |
161 | &args->handle, args->size); | ||
162 | if (IS_ERR(exynos_gem_obj)) | 165 | if (IS_ERR(exynos_gem_obj)) |
163 | return PTR_ERR(exynos_gem_obj); | 166 | return PTR_ERR(exynos_gem_obj); |
164 | 167 | ||
168 | ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, | ||
169 | &args->handle); | ||
170 | if (ret) { | ||
171 | exynos_drm_gem_destroy(exynos_gem_obj); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
165 | return 0; | 175 | return 0; |
166 | } | 176 | } |
167 | 177 | ||
168 | int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, | 178 | int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, |
169 | struct drm_file *file_priv) | 179 | struct drm_file *file_priv) |
170 | { | 180 | { |
171 | struct drm_exynos_gem_map_off *args = data; | 181 | struct drm_exynos_gem_map_off *args = data; |
172 | 182 | ||
@@ -185,7 +195,7 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, | |||
185 | } | 195 | } |
186 | 196 | ||
187 | static int exynos_drm_gem_mmap_buffer(struct file *filp, | 197 | static int exynos_drm_gem_mmap_buffer(struct file *filp, |
188 | struct vm_area_struct *vma) | 198 | struct vm_area_struct *vma) |
189 | { | 199 | { |
190 | struct drm_gem_object *obj = filp->private_data; | 200 | struct drm_gem_object *obj = filp->private_data; |
191 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | 201 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); |
@@ -196,6 +206,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, | |||
196 | 206 | ||
197 | vma->vm_flags |= (VM_IO | VM_RESERVED); | 207 | vma->vm_flags |= (VM_IO | VM_RESERVED); |
198 | 208 | ||
209 | /* in case of direct mapping, always having non-cachable attribute */ | ||
199 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | 210 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
200 | vma->vm_file = filp; | 211 | vma->vm_file = filp; |
201 | 212 | ||
@@ -232,7 +243,7 @@ static const struct file_operations exynos_drm_gem_fops = { | |||
232 | }; | 243 | }; |
233 | 244 | ||
234 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | 245 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, |
235 | struct drm_file *file_priv) | 246 | struct drm_file *file_priv) |
236 | { | 247 | { |
237 | struct drm_exynos_gem_mmap *args = data; | 248 | struct drm_exynos_gem_mmap *args = data; |
238 | struct drm_gem_object *obj; | 249 | struct drm_gem_object *obj; |
@@ -278,32 +289,19 @@ int exynos_drm_gem_init_object(struct drm_gem_object *obj) | |||
278 | return 0; | 289 | return 0; |
279 | } | 290 | } |
280 | 291 | ||
281 | void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj) | 292 | void exynos_drm_gem_free_object(struct drm_gem_object *obj) |
282 | { | 293 | { |
283 | struct exynos_drm_gem_obj *exynos_gem_obj; | ||
284 | |||
285 | DRM_DEBUG_KMS("%s\n", __FILE__); | 294 | DRM_DEBUG_KMS("%s\n", __FILE__); |
286 | 295 | ||
287 | DRM_DEBUG_KMS("handle count = %d\n", | 296 | exynos_drm_gem_destroy(to_exynos_gem_obj(obj)); |
288 | atomic_read(&gem_obj->handle_count)); | ||
289 | |||
290 | if (gem_obj->map_list.map) | ||
291 | drm_gem_free_mmap_offset(gem_obj); | ||
292 | |||
293 | /* release file pointer to gem object. */ | ||
294 | drm_gem_object_release(gem_obj); | ||
295 | |||
296 | exynos_gem_obj = to_exynos_gem_obj(gem_obj); | ||
297 | |||
298 | exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->buffer); | ||
299 | |||
300 | kfree(exynos_gem_obj); | ||
301 | } | 297 | } |
302 | 298 | ||
303 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | 299 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, |
304 | struct drm_device *dev, struct drm_mode_create_dumb *args) | 300 | struct drm_device *dev, |
301 | struct drm_mode_create_dumb *args) | ||
305 | { | 302 | { |
306 | struct exynos_drm_gem_obj *exynos_gem_obj; | 303 | struct exynos_drm_gem_obj *exynos_gem_obj; |
304 | int ret; | ||
307 | 305 | ||
308 | DRM_DEBUG_KMS("%s\n", __FILE__); | 306 | DRM_DEBUG_KMS("%s\n", __FILE__); |
309 | 307 | ||
@@ -316,19 +314,27 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | |||
316 | args->pitch = args->width * args->bpp >> 3; | 314 | args->pitch = args->width * args->bpp >> 3; |
317 | args->size = args->pitch * args->height; | 315 | args->size = args->pitch * args->height; |
318 | 316 | ||
319 | exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, &args->handle, | 317 | exynos_gem_obj = exynos_drm_gem_create(dev, args->size); |
320 | args->size); | ||
321 | if (IS_ERR(exynos_gem_obj)) | 318 | if (IS_ERR(exynos_gem_obj)) |
322 | return PTR_ERR(exynos_gem_obj); | 319 | return PTR_ERR(exynos_gem_obj); |
323 | 320 | ||
321 | ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, | ||
322 | &args->handle); | ||
323 | if (ret) { | ||
324 | exynos_drm_gem_destroy(exynos_gem_obj); | ||
325 | return ret; | ||
326 | } | ||
327 | |||
324 | return 0; | 328 | return 0; |
325 | } | 329 | } |
326 | 330 | ||
327 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, | 331 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, |
328 | struct drm_device *dev, uint32_t handle, uint64_t *offset) | 332 | struct drm_device *dev, uint32_t handle, |
333 | uint64_t *offset) | ||
329 | { | 334 | { |
330 | struct exynos_drm_gem_obj *exynos_gem_obj; | 335 | struct exynos_drm_gem_obj *exynos_gem_obj; |
331 | struct drm_gem_object *obj; | 336 | struct drm_gem_object *obj; |
337 | int ret = 0; | ||
332 | 338 | ||
333 | DRM_DEBUG_KMS("%s\n", __FILE__); | 339 | DRM_DEBUG_KMS("%s\n", __FILE__); |
334 | 340 | ||
@@ -343,19 +349,46 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, | |||
343 | obj = drm_gem_object_lookup(dev, file_priv, handle); | 349 | obj = drm_gem_object_lookup(dev, file_priv, handle); |
344 | if (!obj) { | 350 | if (!obj) { |
345 | DRM_ERROR("failed to lookup gem object.\n"); | 351 | DRM_ERROR("failed to lookup gem object.\n"); |
346 | mutex_unlock(&dev->struct_mutex); | 352 | ret = -EINVAL; |
347 | return -EINVAL; | 353 | goto unlock; |
348 | } | 354 | } |
349 | 355 | ||
350 | exynos_gem_obj = to_exynos_gem_obj(obj); | 356 | exynos_gem_obj = to_exynos_gem_obj(obj); |
351 | 357 | ||
352 | *offset = get_gem_mmap_offset(&exynos_gem_obj->base); | 358 | if (!exynos_gem_obj->base.map_list.map) { |
353 | 359 | ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base); | |
354 | drm_gem_object_unreference(obj); | 360 | if (ret) |
361 | goto out; | ||
362 | } | ||
355 | 363 | ||
364 | *offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT; | ||
356 | DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); | 365 | DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); |
357 | 366 | ||
367 | out: | ||
368 | drm_gem_object_unreference(obj); | ||
369 | unlock: | ||
358 | mutex_unlock(&dev->struct_mutex); | 370 | mutex_unlock(&dev->struct_mutex); |
371 | return ret; | ||
372 | } | ||
373 | |||
374 | int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, | ||
375 | struct drm_device *dev, | ||
376 | unsigned int handle) | ||
377 | { | ||
378 | int ret; | ||
379 | |||
380 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
381 | |||
382 | /* | ||
383 | * obj->refcount and obj->handle_count are decreased and | ||
384 | * if both them are 0 then exynos_drm_gem_free_object() | ||
385 | * would be called by callback to release resources. | ||
386 | */ | ||
387 | ret = drm_gem_handle_delete(file_priv, handle); | ||
388 | if (ret < 0) { | ||
389 | DRM_ERROR("failed to delete drm_gem_handle.\n"); | ||
390 | return ret; | ||
391 | } | ||
359 | 392 | ||
360 | return 0; | 393 | return 0; |
361 | } | 394 | } |
@@ -403,28 +436,6 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | |||
403 | return ret; | 436 | return ret; |
404 | } | 437 | } |
405 | 438 | ||
406 | |||
407 | int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, | ||
408 | struct drm_device *dev, unsigned int handle) | ||
409 | { | ||
410 | int ret; | ||
411 | |||
412 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
413 | |||
414 | /* | ||
415 | * obj->refcount and obj->handle_count are decreased and | ||
416 | * if both them are 0 then exynos_drm_gem_free_object() | ||
417 | * would be called by callback to release resources. | ||
418 | */ | ||
419 | ret = drm_gem_handle_delete(file_priv, handle); | ||
420 | if (ret < 0) { | ||
421 | DRM_ERROR("failed to delete drm_gem_handle.\n"); | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | 439 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); |
429 | MODULE_DESCRIPTION("Samsung SoC DRM GEM Module"); | 440 | MODULE_DESCRIPTION("Samsung SoC DRM GEM Module"); |
430 | MODULE_LICENSE("GPL"); | 441 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index ef8797334e6d..67cdc9168708 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h | |||
@@ -60,14 +60,16 @@ struct exynos_drm_gem_buf { | |||
60 | * user can access the buffer through kms_bo.handle. | 60 | * user can access the buffer through kms_bo.handle. |
61 | */ | 61 | */ |
62 | struct exynos_drm_gem_obj { | 62 | struct exynos_drm_gem_obj { |
63 | struct drm_gem_object base; | 63 | struct drm_gem_object base; |
64 | struct exynos_drm_gem_buf *buffer; | 64 | struct exynos_drm_gem_buf *buffer; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | /* create a new buffer and get a new gem handle. */ | 67 | /* destroy a buffer with gem object */ |
68 | void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj); | ||
69 | |||
70 | /* create a new buffer with gem object */ | ||
68 | struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, | 71 | struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, |
69 | struct drm_file *file_priv, | 72 | unsigned long size); |
70 | unsigned int *handle, unsigned long size); | ||
71 | 73 | ||
72 | /* | 74 | /* |
73 | * request gem object creation and buffer allocation as the size | 75 | * request gem object creation and buffer allocation as the size |
@@ -75,15 +77,18 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, | |||
75 | * height and bpp. | 77 | * height and bpp. |
76 | */ | 78 | */ |
77 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, | 79 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, |
78 | struct drm_file *file_priv); | 80 | struct drm_file *file_priv); |
79 | 81 | ||
80 | /* get buffer offset to map to user space. */ | 82 | /* get buffer offset to map to user space. */ |
81 | int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, | 83 | int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, |
82 | struct drm_file *file_priv); | 84 | struct drm_file *file_priv); |
83 | 85 | ||
84 | /* unmap a buffer from user space. */ | 86 | /* |
85 | int exynos_drm_gem_munmap_ioctl(struct drm_device *dev, void *data, | 87 | * mmap the physically continuous memory that a gem object contains |
86 | struct drm_file *file_priv); | 88 | * to user space. |
89 | */ | ||
90 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | ||
91 | struct drm_file *file_priv); | ||
87 | 92 | ||
88 | /* initialize gem object. */ | 93 | /* initialize gem object. */ |
89 | int exynos_drm_gem_init_object(struct drm_gem_object *obj); | 94 | int exynos_drm_gem_init_object(struct drm_gem_object *obj); |
@@ -93,24 +98,13 @@ void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj); | |||
93 | 98 | ||
94 | /* create memory region for drm framebuffer. */ | 99 | /* create memory region for drm framebuffer. */ |
95 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | 100 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, |
96 | struct drm_device *dev, struct drm_mode_create_dumb *args); | 101 | struct drm_device *dev, |
102 | struct drm_mode_create_dumb *args); | ||
97 | 103 | ||
98 | /* map memory region for drm framebuffer to user space. */ | 104 | /* map memory region for drm framebuffer to user space. */ |
99 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, | 105 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, |
100 | struct drm_device *dev, uint32_t handle, uint64_t *offset); | 106 | struct drm_device *dev, uint32_t handle, |
101 | 107 | uint64_t *offset); | |
102 | /* page fault handler and mmap fault address(virtual) to physical memory. */ | ||
103 | int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); | ||
104 | |||
105 | /* | ||
106 | * mmap the physically continuous memory that a gem object contains | ||
107 | * to user space. | ||
108 | */ | ||
109 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | ||
110 | struct drm_file *file_priv); | ||
111 | |||
112 | /* set vm_flags and we can change the vm attribute to other one at here. */ | ||
113 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | ||
114 | 108 | ||
115 | /* | 109 | /* |
116 | * destroy memory region allocated. | 110 | * destroy memory region allocated. |
@@ -118,6 +112,13 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | |||
118 | * would be released by drm_gem_handle_delete(). | 112 | * would be released by drm_gem_handle_delete(). |
119 | */ | 113 | */ |
120 | int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, | 114 | int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, |
121 | struct drm_device *dev, unsigned int handle); | 115 | struct drm_device *dev, |
116 | unsigned int handle); | ||
117 | |||
118 | /* page fault handler and mmap fault address(virtual) to physical memory. */ | ||
119 | int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); | ||
120 | |||
121 | /* set vm_flags and we can change the vm attribute to other one at here. */ | ||
122 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | ||
122 | 123 | ||
123 | #endif | 124 | #endif |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c new file mode 100644 index 000000000000..ed8a319ed84b --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c | |||
@@ -0,0 +1,439 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
3 | * Authors: | ||
4 | * Inki Dae <inki.dae@samsung.com> | ||
5 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include "drmP.h" | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/pm_runtime.h> | ||
21 | |||
22 | #include <drm/exynos_drm.h> | ||
23 | |||
24 | #include "exynos_drm_drv.h" | ||
25 | #include "exynos_drm_hdmi.h" | ||
26 | |||
27 | #define to_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
28 | #define to_subdrv(dev) to_context(dev) | ||
29 | #define get_ctx_from_subdrv(subdrv) container_of(subdrv,\ | ||
30 | struct drm_hdmi_context, subdrv); | ||
31 | |||
32 | /* these callback points shoud be set by specific drivers. */ | ||
33 | static struct exynos_hdmi_display_ops *hdmi_display_ops; | ||
34 | static struct exynos_hdmi_manager_ops *hdmi_manager_ops; | ||
35 | static struct exynos_hdmi_overlay_ops *hdmi_overlay_ops; | ||
36 | |||
37 | struct drm_hdmi_context { | ||
38 | struct exynos_drm_subdrv subdrv; | ||
39 | struct exynos_drm_hdmi_context *hdmi_ctx; | ||
40 | struct exynos_drm_hdmi_context *mixer_ctx; | ||
41 | struct work_struct work; | ||
42 | }; | ||
43 | |||
44 | void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops | ||
45 | *display_ops) | ||
46 | { | ||
47 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
48 | |||
49 | if (display_ops) | ||
50 | hdmi_display_ops = display_ops; | ||
51 | } | ||
52 | EXPORT_SYMBOL(exynos_drm_display_ops_register); | ||
53 | |||
54 | void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops | ||
55 | *manager_ops) | ||
56 | { | ||
57 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
58 | |||
59 | if (manager_ops) | ||
60 | hdmi_manager_ops = manager_ops; | ||
61 | } | ||
62 | EXPORT_SYMBOL(exynos_drm_manager_ops_register); | ||
63 | |||
64 | void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops | ||
65 | *overlay_ops) | ||
66 | { | ||
67 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
68 | |||
69 | if (overlay_ops) | ||
70 | hdmi_overlay_ops = overlay_ops; | ||
71 | } | ||
72 | EXPORT_SYMBOL(exynos_drm_overlay_ops_register); | ||
73 | |||
74 | static bool drm_hdmi_is_connected(struct device *dev) | ||
75 | { | ||
76 | struct drm_hdmi_context *ctx = to_context(dev); | ||
77 | |||
78 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
79 | |||
80 | if (hdmi_display_ops && hdmi_display_ops->is_connected) | ||
81 | return hdmi_display_ops->is_connected(ctx->hdmi_ctx->ctx); | ||
82 | |||
83 | return false; | ||
84 | } | ||
85 | |||
86 | static int drm_hdmi_get_edid(struct device *dev, | ||
87 | struct drm_connector *connector, u8 *edid, int len) | ||
88 | { | ||
89 | struct drm_hdmi_context *ctx = to_context(dev); | ||
90 | |||
91 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
92 | |||
93 | if (hdmi_display_ops && hdmi_display_ops->get_edid) | ||
94 | return hdmi_display_ops->get_edid(ctx->hdmi_ctx->ctx, | ||
95 | connector, edid, len); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int drm_hdmi_check_timing(struct device *dev, void *timing) | ||
101 | { | ||
102 | struct drm_hdmi_context *ctx = to_context(dev); | ||
103 | |||
104 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
105 | |||
106 | if (hdmi_display_ops && hdmi_display_ops->check_timing) | ||
107 | return hdmi_display_ops->check_timing(ctx->hdmi_ctx->ctx, | ||
108 | timing); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int drm_hdmi_power_on(struct device *dev, int mode) | ||
114 | { | ||
115 | struct drm_hdmi_context *ctx = to_context(dev); | ||
116 | |||
117 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
118 | |||
119 | if (hdmi_display_ops && hdmi_display_ops->power_on) | ||
120 | return hdmi_display_ops->power_on(ctx->hdmi_ctx->ctx, mode); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static struct exynos_drm_display_ops drm_hdmi_display_ops = { | ||
126 | .type = EXYNOS_DISPLAY_TYPE_HDMI, | ||
127 | .is_connected = drm_hdmi_is_connected, | ||
128 | .get_edid = drm_hdmi_get_edid, | ||
129 | .check_timing = drm_hdmi_check_timing, | ||
130 | .power_on = drm_hdmi_power_on, | ||
131 | }; | ||
132 | |||
133 | static int drm_hdmi_enable_vblank(struct device *subdrv_dev) | ||
134 | { | ||
135 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
136 | struct exynos_drm_subdrv *subdrv = &ctx->subdrv; | ||
137 | struct exynos_drm_manager *manager = &subdrv->manager; | ||
138 | |||
139 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
140 | |||
141 | if (hdmi_overlay_ops && hdmi_overlay_ops->enable_vblank) | ||
142 | return hdmi_overlay_ops->enable_vblank(ctx->mixer_ctx->ctx, | ||
143 | manager->pipe); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static void drm_hdmi_disable_vblank(struct device *subdrv_dev) | ||
149 | { | ||
150 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
151 | |||
152 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
153 | |||
154 | if (hdmi_overlay_ops && hdmi_overlay_ops->disable_vblank) | ||
155 | return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx); | ||
156 | } | ||
157 | |||
158 | static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) | ||
159 | { | ||
160 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
161 | |||
162 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
163 | |||
164 | if (hdmi_manager_ops && hdmi_manager_ops->mode_set) | ||
165 | hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode); | ||
166 | } | ||
167 | |||
168 | static void drm_hdmi_commit(struct device *subdrv_dev) | ||
169 | { | ||
170 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
171 | |||
172 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
173 | |||
174 | if (hdmi_manager_ops && hdmi_manager_ops->commit) | ||
175 | hdmi_manager_ops->commit(ctx->hdmi_ctx->ctx); | ||
176 | } | ||
177 | |||
178 | static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) | ||
179 | { | ||
180 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
181 | |||
182 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
183 | |||
184 | switch (mode) { | ||
185 | case DRM_MODE_DPMS_ON: | ||
186 | break; | ||
187 | case DRM_MODE_DPMS_STANDBY: | ||
188 | case DRM_MODE_DPMS_SUSPEND: | ||
189 | case DRM_MODE_DPMS_OFF: | ||
190 | if (hdmi_manager_ops && hdmi_manager_ops->disable) | ||
191 | hdmi_manager_ops->disable(ctx->hdmi_ctx->ctx); | ||
192 | break; | ||
193 | default: | ||
194 | DRM_DEBUG_KMS("unkown dps mode: %d\n", mode); | ||
195 | break; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { | ||
200 | .dpms = drm_hdmi_dpms, | ||
201 | .enable_vblank = drm_hdmi_enable_vblank, | ||
202 | .disable_vblank = drm_hdmi_disable_vblank, | ||
203 | .mode_set = drm_hdmi_mode_set, | ||
204 | .commit = drm_hdmi_commit, | ||
205 | }; | ||
206 | |||
207 | static void drm_mixer_mode_set(struct device *subdrv_dev, | ||
208 | struct exynos_drm_overlay *overlay) | ||
209 | { | ||
210 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
211 | |||
212 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
213 | |||
214 | if (hdmi_overlay_ops && hdmi_overlay_ops->win_mode_set) | ||
215 | hdmi_overlay_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); | ||
216 | } | ||
217 | |||
218 | static void drm_mixer_commit(struct device *subdrv_dev, int zpos) | ||
219 | { | ||
220 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
221 | |||
222 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
223 | |||
224 | if (hdmi_overlay_ops && hdmi_overlay_ops->win_commit) | ||
225 | hdmi_overlay_ops->win_commit(ctx->mixer_ctx->ctx, zpos); | ||
226 | } | ||
227 | |||
228 | static void drm_mixer_disable(struct device *subdrv_dev, int zpos) | ||
229 | { | ||
230 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
231 | |||
232 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
233 | |||
234 | if (hdmi_overlay_ops && hdmi_overlay_ops->win_disable) | ||
235 | hdmi_overlay_ops->win_disable(ctx->mixer_ctx->ctx, zpos); | ||
236 | } | ||
237 | |||
238 | static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { | ||
239 | .mode_set = drm_mixer_mode_set, | ||
240 | .commit = drm_mixer_commit, | ||
241 | .disable = drm_mixer_disable, | ||
242 | }; | ||
243 | |||
244 | |||
245 | static int hdmi_subdrv_probe(struct drm_device *drm_dev, | ||
246 | struct device *dev) | ||
247 | { | ||
248 | struct exynos_drm_subdrv *subdrv = to_subdrv(dev); | ||
249 | struct drm_hdmi_context *ctx; | ||
250 | struct platform_device *pdev = to_platform_device(dev); | ||
251 | struct exynos_drm_common_hdmi_pd *pd; | ||
252 | int ret; | ||
253 | |||
254 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
255 | |||
256 | pd = pdev->dev.platform_data; | ||
257 | |||
258 | if (!pd) { | ||
259 | DRM_DEBUG_KMS("platform data is null.\n"); | ||
260 | return -EFAULT; | ||
261 | } | ||
262 | |||
263 | if (!pd->hdmi_dev) { | ||
264 | DRM_DEBUG_KMS("hdmi device is null.\n"); | ||
265 | return -EFAULT; | ||
266 | } | ||
267 | |||
268 | if (!pd->mixer_dev) { | ||
269 | DRM_DEBUG_KMS("mixer device is null.\n"); | ||
270 | return -EFAULT; | ||
271 | } | ||
272 | |||
273 | ret = platform_driver_register(&hdmi_driver); | ||
274 | if (ret) { | ||
275 | DRM_DEBUG_KMS("failed to register hdmi driver.\n"); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | ret = platform_driver_register(&mixer_driver); | ||
280 | if (ret) { | ||
281 | DRM_DEBUG_KMS("failed to register mixer driver.\n"); | ||
282 | goto err_hdmidrv; | ||
283 | } | ||
284 | |||
285 | ctx = get_ctx_from_subdrv(subdrv); | ||
286 | |||
287 | ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *) | ||
288 | to_context(pd->hdmi_dev); | ||
289 | if (!ctx->hdmi_ctx) { | ||
290 | DRM_DEBUG_KMS("hdmi context is null.\n"); | ||
291 | ret = -EFAULT; | ||
292 | goto err_mixerdrv; | ||
293 | } | ||
294 | |||
295 | ctx->hdmi_ctx->drm_dev = drm_dev; | ||
296 | |||
297 | ctx->mixer_ctx = (struct exynos_drm_hdmi_context *) | ||
298 | to_context(pd->mixer_dev); | ||
299 | if (!ctx->mixer_ctx) { | ||
300 | DRM_DEBUG_KMS("mixer context is null.\n"); | ||
301 | ret = -EFAULT; | ||
302 | goto err_mixerdrv; | ||
303 | } | ||
304 | |||
305 | ctx->mixer_ctx->drm_dev = drm_dev; | ||
306 | |||
307 | return 0; | ||
308 | |||
309 | err_mixerdrv: | ||
310 | platform_driver_unregister(&mixer_driver); | ||
311 | err_hdmidrv: | ||
312 | platform_driver_unregister(&hdmi_driver); | ||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | static void hdmi_subdrv_remove(struct drm_device *drm_dev) | ||
317 | { | ||
318 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
319 | |||
320 | platform_driver_unregister(&hdmi_driver); | ||
321 | platform_driver_unregister(&mixer_driver); | ||
322 | } | ||
323 | |||
324 | static void exynos_drm_hdmi_late_probe(struct work_struct *work) | ||
325 | { | ||
326 | struct drm_hdmi_context *ctx = container_of(work, | ||
327 | struct drm_hdmi_context, work); | ||
328 | |||
329 | /* | ||
330 | * this function calls subdrv->probe() so this must be called | ||
331 | * after probe context. | ||
332 | * | ||
333 | * PS. subdrv->probe() will call platform_driver_register() to probe | ||
334 | * hdmi and mixer driver. | ||
335 | */ | ||
336 | exynos_drm_subdrv_register(&ctx->subdrv); | ||
337 | } | ||
338 | |||
339 | static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) | ||
340 | { | ||
341 | struct device *dev = &pdev->dev; | ||
342 | struct exynos_drm_subdrv *subdrv; | ||
343 | struct drm_hdmi_context *ctx; | ||
344 | |||
345 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
346 | |||
347 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
348 | if (!ctx) { | ||
349 | DRM_LOG_KMS("failed to alloc common hdmi context.\n"); | ||
350 | return -ENOMEM; | ||
351 | } | ||
352 | |||
353 | subdrv = &ctx->subdrv; | ||
354 | |||
355 | subdrv->probe = hdmi_subdrv_probe; | ||
356 | subdrv->remove = hdmi_subdrv_remove; | ||
357 | subdrv->manager.pipe = -1; | ||
358 | subdrv->manager.ops = &drm_hdmi_manager_ops; | ||
359 | subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops; | ||
360 | subdrv->manager.display_ops = &drm_hdmi_display_ops; | ||
361 | subdrv->manager.dev = dev; | ||
362 | |||
363 | platform_set_drvdata(pdev, subdrv); | ||
364 | |||
365 | INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe); | ||
366 | |||
367 | schedule_work(&ctx->work); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int hdmi_runtime_suspend(struct device *dev) | ||
373 | { | ||
374 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int hdmi_runtime_resume(struct device *dev) | ||
380 | { | ||
381 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static const struct dev_pm_ops hdmi_pm_ops = { | ||
387 | .runtime_suspend = hdmi_runtime_suspend, | ||
388 | .runtime_resume = hdmi_runtime_resume, | ||
389 | }; | ||
390 | |||
391 | static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) | ||
392 | { | ||
393 | struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); | ||
394 | |||
395 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
396 | |||
397 | exynos_drm_subdrv_unregister(&ctx->subdrv); | ||
398 | kfree(ctx); | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static struct platform_driver exynos_drm_common_hdmi_driver = { | ||
404 | .probe = exynos_drm_hdmi_probe, | ||
405 | .remove = __devexit_p(exynos_drm_hdmi_remove), | ||
406 | .driver = { | ||
407 | .name = "exynos-drm-hdmi", | ||
408 | .owner = THIS_MODULE, | ||
409 | .pm = &hdmi_pm_ops, | ||
410 | }, | ||
411 | }; | ||
412 | |||
413 | static int __init exynos_drm_hdmi_init(void) | ||
414 | { | ||
415 | int ret; | ||
416 | |||
417 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
418 | |||
419 | ret = platform_driver_register(&exynos_drm_common_hdmi_driver); | ||
420 | if (ret) { | ||
421 | DRM_DEBUG_KMS("failed to register hdmi common driver.\n"); | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static void __exit exynos_drm_hdmi_exit(void) | ||
429 | { | ||
430 | platform_driver_unregister(&exynos_drm_common_hdmi_driver); | ||
431 | } | ||
432 | |||
433 | module_init(exynos_drm_hdmi_init); | ||
434 | module_exit(exynos_drm_hdmi_exit); | ||
435 | |||
436 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
437 | MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); | ||
438 | MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver"); | ||
439 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h new file mode 100644 index 000000000000..3c29f790ee45 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* exynos_drm_hdmi.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_HDMI_H_ | ||
27 | #define _EXYNOS_DRM_HDMI_H_ | ||
28 | |||
29 | /* | ||
30 | * exynos hdmi common context structure. | ||
31 | * | ||
32 | * @drm_dev: pointer to drm_device. | ||
33 | * @ctx: pointer to the context of specific device driver. | ||
34 | * this context should be hdmi_context or mixer_context. | ||
35 | */ | ||
36 | struct exynos_drm_hdmi_context { | ||
37 | struct drm_device *drm_dev; | ||
38 | void *ctx; | ||
39 | }; | ||
40 | |||
41 | struct exynos_hdmi_display_ops { | ||
42 | bool (*is_connected)(void *ctx); | ||
43 | int (*get_edid)(void *ctx, struct drm_connector *connector, | ||
44 | u8 *edid, int len); | ||
45 | int (*check_timing)(void *ctx, void *timing); | ||
46 | int (*power_on)(void *ctx, int mode); | ||
47 | }; | ||
48 | |||
49 | struct exynos_hdmi_manager_ops { | ||
50 | void (*mode_set)(void *ctx, void *mode); | ||
51 | void (*commit)(void *ctx); | ||
52 | void (*disable)(void *ctx); | ||
53 | }; | ||
54 | |||
55 | struct exynos_hdmi_overlay_ops { | ||
56 | int (*enable_vblank)(void *ctx, int pipe); | ||
57 | void (*disable_vblank)(void *ctx); | ||
58 | void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); | ||
59 | void (*win_commit)(void *ctx, int zpos); | ||
60 | void (*win_disable)(void *ctx, int zpos); | ||
61 | }; | ||
62 | |||
63 | extern struct platform_driver hdmi_driver; | ||
64 | extern struct platform_driver mixer_driver; | ||
65 | |||
66 | void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops | ||
67 | *display_ops); | ||
68 | void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops | ||
69 | *manager_ops); | ||
70 | void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops | ||
71 | *overlay_ops); | ||
72 | |||
73 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c new file mode 100644 index 000000000000..f48f7ce92f5f --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c | |||
@@ -0,0 +1,1176 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
3 | * Authors: | ||
4 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * | ||
8 | * Based on drivers/media/video/s5p-tv/hdmi_drv.c | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include "drmP.h" | ||
18 | #include "drm_edid.h" | ||
19 | #include "drm_crtc_helper.h" | ||
20 | |||
21 | #include "regs-hdmi.h" | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/spinlock.h> | ||
25 | #include <linux/wait.h> | ||
26 | #include <linux/i2c.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/irq.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/pm_runtime.h> | ||
33 | #include <linux/clk.h> | ||
34 | #include <linux/regulator/consumer.h> | ||
35 | |||
36 | #include <drm/exynos_drm.h> | ||
37 | |||
38 | #include "exynos_drm_drv.h" | ||
39 | #include "exynos_drm_hdmi.h" | ||
40 | |||
41 | #include "exynos_hdmi.h" | ||
42 | |||
43 | #define HDMI_OVERLAY_NUMBER 3 | ||
44 | #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
45 | |||
46 | static const u8 hdmiphy_conf27[32] = { | ||
47 | 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, | ||
48 | 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, | ||
49 | 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, | ||
50 | 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, | ||
51 | }; | ||
52 | |||
53 | static const u8 hdmiphy_conf27_027[32] = { | ||
54 | 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, | ||
55 | 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, | ||
56 | 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, | ||
57 | 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, | ||
58 | }; | ||
59 | |||
60 | static const u8 hdmiphy_conf74_175[32] = { | ||
61 | 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, | ||
62 | 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, | ||
63 | 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, | ||
64 | 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, | ||
65 | }; | ||
66 | |||
67 | static const u8 hdmiphy_conf74_25[32] = { | ||
68 | 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, | ||
69 | 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, | ||
70 | 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0, | ||
71 | 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, | ||
72 | }; | ||
73 | |||
74 | static const u8 hdmiphy_conf148_5[32] = { | ||
75 | 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, | ||
76 | 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, | ||
77 | 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, | ||
78 | 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, | ||
79 | }; | ||
80 | |||
81 | struct hdmi_tg_regs { | ||
82 | u8 cmd; | ||
83 | u8 h_fsz_l; | ||
84 | u8 h_fsz_h; | ||
85 | u8 hact_st_l; | ||
86 | u8 hact_st_h; | ||
87 | u8 hact_sz_l; | ||
88 | u8 hact_sz_h; | ||
89 | u8 v_fsz_l; | ||
90 | u8 v_fsz_h; | ||
91 | u8 vsync_l; | ||
92 | u8 vsync_h; | ||
93 | u8 vsync2_l; | ||
94 | u8 vsync2_h; | ||
95 | u8 vact_st_l; | ||
96 | u8 vact_st_h; | ||
97 | u8 vact_sz_l; | ||
98 | u8 vact_sz_h; | ||
99 | u8 field_chg_l; | ||
100 | u8 field_chg_h; | ||
101 | u8 vact_st2_l; | ||
102 | u8 vact_st2_h; | ||
103 | u8 vsync_top_hdmi_l; | ||
104 | u8 vsync_top_hdmi_h; | ||
105 | u8 vsync_bot_hdmi_l; | ||
106 | u8 vsync_bot_hdmi_h; | ||
107 | u8 field_top_hdmi_l; | ||
108 | u8 field_top_hdmi_h; | ||
109 | u8 field_bot_hdmi_l; | ||
110 | u8 field_bot_hdmi_h; | ||
111 | }; | ||
112 | |||
113 | struct hdmi_core_regs { | ||
114 | u8 h_blank[2]; | ||
115 | u8 v_blank[3]; | ||
116 | u8 h_v_line[3]; | ||
117 | u8 vsync_pol[1]; | ||
118 | u8 int_pro_mode[1]; | ||
119 | u8 v_blank_f[3]; | ||
120 | u8 h_sync_gen[3]; | ||
121 | u8 v_sync_gen1[3]; | ||
122 | u8 v_sync_gen2[3]; | ||
123 | u8 v_sync_gen3[3]; | ||
124 | }; | ||
125 | |||
126 | struct hdmi_preset_conf { | ||
127 | struct hdmi_core_regs core; | ||
128 | struct hdmi_tg_regs tg; | ||
129 | }; | ||
130 | |||
131 | static const struct hdmi_preset_conf hdmi_conf_480p = { | ||
132 | .core = { | ||
133 | .h_blank = {0x8a, 0x00}, | ||
134 | .v_blank = {0x0d, 0x6a, 0x01}, | ||
135 | .h_v_line = {0x0d, 0xa2, 0x35}, | ||
136 | .vsync_pol = {0x01}, | ||
137 | .int_pro_mode = {0x00}, | ||
138 | .v_blank_f = {0x00, 0x00, 0x00}, | ||
139 | .h_sync_gen = {0x0e, 0x30, 0x11}, | ||
140 | .v_sync_gen1 = {0x0f, 0x90, 0x00}, | ||
141 | /* other don't care */ | ||
142 | }, | ||
143 | .tg = { | ||
144 | 0x00, /* cmd */ | ||
145 | 0x5a, 0x03, /* h_fsz */ | ||
146 | 0x8a, 0x00, 0xd0, 0x02, /* hact */ | ||
147 | 0x0d, 0x02, /* v_fsz */ | ||
148 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
149 | 0x2d, 0x00, 0xe0, 0x01, /* vact */ | ||
150 | 0x33, 0x02, /* field_chg */ | ||
151 | 0x49, 0x02, /* vact_st2 */ | ||
152 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
153 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
154 | }, | ||
155 | }; | ||
156 | |||
157 | static const struct hdmi_preset_conf hdmi_conf_720p60 = { | ||
158 | .core = { | ||
159 | .h_blank = {0x72, 0x01}, | ||
160 | .v_blank = {0xee, 0xf2, 0x00}, | ||
161 | .h_v_line = {0xee, 0x22, 0x67}, | ||
162 | .vsync_pol = {0x00}, | ||
163 | .int_pro_mode = {0x00}, | ||
164 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
165 | .h_sync_gen = {0x6c, 0x50, 0x02}, | ||
166 | .v_sync_gen1 = {0x0a, 0x50, 0x00}, | ||
167 | .v_sync_gen2 = {0x01, 0x10, 0x00}, | ||
168 | .v_sync_gen3 = {0x01, 0x10, 0x00}, | ||
169 | /* other don't care */ | ||
170 | }, | ||
171 | .tg = { | ||
172 | 0x00, /* cmd */ | ||
173 | 0x72, 0x06, /* h_fsz */ | ||
174 | 0x71, 0x01, 0x01, 0x05, /* hact */ | ||
175 | 0xee, 0x02, /* v_fsz */ | ||
176 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
177 | 0x1e, 0x00, 0xd0, 0x02, /* vact */ | ||
178 | 0x33, 0x02, /* field_chg */ | ||
179 | 0x49, 0x02, /* vact_st2 */ | ||
180 | 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ | ||
181 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
182 | }, | ||
183 | }; | ||
184 | |||
185 | static const struct hdmi_preset_conf hdmi_conf_1080i50 = { | ||
186 | .core = { | ||
187 | .h_blank = {0xd0, 0x02}, | ||
188 | .v_blank = {0x32, 0xB2, 0x00}, | ||
189 | .h_v_line = {0x65, 0x04, 0xa5}, | ||
190 | .vsync_pol = {0x00}, | ||
191 | .int_pro_mode = {0x01}, | ||
192 | .v_blank_f = {0x49, 0x2A, 0x23}, | ||
193 | .h_sync_gen = {0x0E, 0xEA, 0x08}, | ||
194 | .v_sync_gen1 = {0x07, 0x20, 0x00}, | ||
195 | .v_sync_gen2 = {0x39, 0x42, 0x23}, | ||
196 | .v_sync_gen3 = {0x38, 0x87, 0x73}, | ||
197 | /* other don't care */ | ||
198 | }, | ||
199 | .tg = { | ||
200 | 0x00, /* cmd */ | ||
201 | 0x50, 0x0A, /* h_fsz */ | ||
202 | 0xCF, 0x02, 0x81, 0x07, /* hact */ | ||
203 | 0x65, 0x04, /* v_fsz */ | ||
204 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
205 | 0x16, 0x00, 0x1c, 0x02, /* vact */ | ||
206 | 0x33, 0x02, /* field_chg */ | ||
207 | 0x49, 0x02, /* vact_st2 */ | ||
208 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
209 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
210 | }, | ||
211 | }; | ||
212 | |||
213 | static const struct hdmi_preset_conf hdmi_conf_1080p50 = { | ||
214 | .core = { | ||
215 | .h_blank = {0xd0, 0x02}, | ||
216 | .v_blank = {0x65, 0x6c, 0x01}, | ||
217 | .h_v_line = {0x65, 0x04, 0xa5}, | ||
218 | .vsync_pol = {0x00}, | ||
219 | .int_pro_mode = {0x00}, | ||
220 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
221 | .h_sync_gen = {0x0e, 0xea, 0x08}, | ||
222 | .v_sync_gen1 = {0x09, 0x40, 0x00}, | ||
223 | .v_sync_gen2 = {0x01, 0x10, 0x00}, | ||
224 | .v_sync_gen3 = {0x01, 0x10, 0x00}, | ||
225 | /* other don't care */ | ||
226 | }, | ||
227 | .tg = { | ||
228 | 0x00, /* cmd */ | ||
229 | 0x50, 0x0A, /* h_fsz */ | ||
230 | 0xCF, 0x02, 0x81, 0x07, /* hact */ | ||
231 | 0x65, 0x04, /* v_fsz */ | ||
232 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
233 | 0x2d, 0x00, 0x38, 0x04, /* vact */ | ||
234 | 0x33, 0x02, /* field_chg */ | ||
235 | 0x48, 0x02, /* vact_st2 */ | ||
236 | 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ | ||
237 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
238 | }, | ||
239 | }; | ||
240 | |||
241 | static const struct hdmi_preset_conf hdmi_conf_1080i60 = { | ||
242 | .core = { | ||
243 | .h_blank = {0x18, 0x01}, | ||
244 | .v_blank = {0x32, 0xB2, 0x00}, | ||
245 | .h_v_line = {0x65, 0x84, 0x89}, | ||
246 | .vsync_pol = {0x00}, | ||
247 | .int_pro_mode = {0x01}, | ||
248 | .v_blank_f = {0x49, 0x2A, 0x23}, | ||
249 | .h_sync_gen = {0x56, 0x08, 0x02}, | ||
250 | .v_sync_gen1 = {0x07, 0x20, 0x00}, | ||
251 | .v_sync_gen2 = {0x39, 0x42, 0x23}, | ||
252 | .v_sync_gen3 = {0xa4, 0x44, 0x4a}, | ||
253 | /* other don't care */ | ||
254 | }, | ||
255 | .tg = { | ||
256 | 0x00, /* cmd */ | ||
257 | 0x98, 0x08, /* h_fsz */ | ||
258 | 0x17, 0x01, 0x81, 0x07, /* hact */ | ||
259 | 0x65, 0x04, /* v_fsz */ | ||
260 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
261 | 0x16, 0x00, 0x1c, 0x02, /* vact */ | ||
262 | 0x33, 0x02, /* field_chg */ | ||
263 | 0x49, 0x02, /* vact_st2 */ | ||
264 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
265 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
266 | }, | ||
267 | }; | ||
268 | |||
269 | static const struct hdmi_preset_conf hdmi_conf_1080p60 = { | ||
270 | .core = { | ||
271 | .h_blank = {0x18, 0x01}, | ||
272 | .v_blank = {0x65, 0x6c, 0x01}, | ||
273 | .h_v_line = {0x65, 0x84, 0x89}, | ||
274 | .vsync_pol = {0x00}, | ||
275 | .int_pro_mode = {0x00}, | ||
276 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
277 | .h_sync_gen = {0x56, 0x08, 0x02}, | ||
278 | .v_sync_gen1 = {0x09, 0x40, 0x00}, | ||
279 | .v_sync_gen2 = {0x01, 0x10, 0x00}, | ||
280 | .v_sync_gen3 = {0x01, 0x10, 0x00}, | ||
281 | /* other don't care */ | ||
282 | }, | ||
283 | .tg = { | ||
284 | 0x00, /* cmd */ | ||
285 | 0x98, 0x08, /* h_fsz */ | ||
286 | 0x17, 0x01, 0x81, 0x07, /* hact */ | ||
287 | 0x65, 0x04, /* v_fsz */ | ||
288 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
289 | 0x2d, 0x00, 0x38, 0x04, /* vact */ | ||
290 | 0x33, 0x02, /* field_chg */ | ||
291 | 0x48, 0x02, /* vact_st2 */ | ||
292 | 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ | ||
293 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
294 | }, | ||
295 | }; | ||
296 | |||
297 | static const struct hdmi_conf hdmi_confs[] = { | ||
298 | { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, | ||
299 | { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, | ||
300 | { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p }, | ||
301 | { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, | ||
302 | { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, | ||
303 | { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, | ||
304 | { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 }, | ||
305 | }; | ||
306 | |||
307 | |||
308 | static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id) | ||
309 | { | ||
310 | return readl(hdata->regs + reg_id); | ||
311 | } | ||
312 | |||
313 | static inline void hdmi_reg_writeb(struct hdmi_context *hdata, | ||
314 | u32 reg_id, u8 value) | ||
315 | { | ||
316 | writeb(value, hdata->regs + reg_id); | ||
317 | } | ||
318 | |||
319 | static inline void hdmi_reg_writemask(struct hdmi_context *hdata, | ||
320 | u32 reg_id, u32 value, u32 mask) | ||
321 | { | ||
322 | u32 old = readl(hdata->regs + reg_id); | ||
323 | value = (value & mask) | (old & ~mask); | ||
324 | writel(value, hdata->regs + reg_id); | ||
325 | } | ||
326 | |||
327 | static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) | ||
328 | { | ||
329 | #define DUMPREG(reg_id) \ | ||
330 | DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ | ||
331 | readl(hdata->regs + reg_id)) | ||
332 | DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix); | ||
333 | DUMPREG(HDMI_INTC_FLAG); | ||
334 | DUMPREG(HDMI_INTC_CON); | ||
335 | DUMPREG(HDMI_HPD_STATUS); | ||
336 | DUMPREG(HDMI_PHY_RSTOUT); | ||
337 | DUMPREG(HDMI_PHY_VPLL); | ||
338 | DUMPREG(HDMI_PHY_CMU); | ||
339 | DUMPREG(HDMI_CORE_RSTOUT); | ||
340 | |||
341 | DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix); | ||
342 | DUMPREG(HDMI_CON_0); | ||
343 | DUMPREG(HDMI_CON_1); | ||
344 | DUMPREG(HDMI_CON_2); | ||
345 | DUMPREG(HDMI_SYS_STATUS); | ||
346 | DUMPREG(HDMI_PHY_STATUS); | ||
347 | DUMPREG(HDMI_STATUS_EN); | ||
348 | DUMPREG(HDMI_HPD); | ||
349 | DUMPREG(HDMI_MODE_SEL); | ||
350 | DUMPREG(HDMI_HPD_GEN); | ||
351 | DUMPREG(HDMI_DC_CONTROL); | ||
352 | DUMPREG(HDMI_VIDEO_PATTERN_GEN); | ||
353 | |||
354 | DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); | ||
355 | DUMPREG(HDMI_H_BLANK_0); | ||
356 | DUMPREG(HDMI_H_BLANK_1); | ||
357 | DUMPREG(HDMI_V_BLANK_0); | ||
358 | DUMPREG(HDMI_V_BLANK_1); | ||
359 | DUMPREG(HDMI_V_BLANK_2); | ||
360 | DUMPREG(HDMI_H_V_LINE_0); | ||
361 | DUMPREG(HDMI_H_V_LINE_1); | ||
362 | DUMPREG(HDMI_H_V_LINE_2); | ||
363 | DUMPREG(HDMI_VSYNC_POL); | ||
364 | DUMPREG(HDMI_INT_PRO_MODE); | ||
365 | DUMPREG(HDMI_V_BLANK_F_0); | ||
366 | DUMPREG(HDMI_V_BLANK_F_1); | ||
367 | DUMPREG(HDMI_V_BLANK_F_2); | ||
368 | DUMPREG(HDMI_H_SYNC_GEN_0); | ||
369 | DUMPREG(HDMI_H_SYNC_GEN_1); | ||
370 | DUMPREG(HDMI_H_SYNC_GEN_2); | ||
371 | DUMPREG(HDMI_V_SYNC_GEN_1_0); | ||
372 | DUMPREG(HDMI_V_SYNC_GEN_1_1); | ||
373 | DUMPREG(HDMI_V_SYNC_GEN_1_2); | ||
374 | DUMPREG(HDMI_V_SYNC_GEN_2_0); | ||
375 | DUMPREG(HDMI_V_SYNC_GEN_2_1); | ||
376 | DUMPREG(HDMI_V_SYNC_GEN_2_2); | ||
377 | DUMPREG(HDMI_V_SYNC_GEN_3_0); | ||
378 | DUMPREG(HDMI_V_SYNC_GEN_3_1); | ||
379 | DUMPREG(HDMI_V_SYNC_GEN_3_2); | ||
380 | |||
381 | DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); | ||
382 | DUMPREG(HDMI_TG_CMD); | ||
383 | DUMPREG(HDMI_TG_H_FSZ_L); | ||
384 | DUMPREG(HDMI_TG_H_FSZ_H); | ||
385 | DUMPREG(HDMI_TG_HACT_ST_L); | ||
386 | DUMPREG(HDMI_TG_HACT_ST_H); | ||
387 | DUMPREG(HDMI_TG_HACT_SZ_L); | ||
388 | DUMPREG(HDMI_TG_HACT_SZ_H); | ||
389 | DUMPREG(HDMI_TG_V_FSZ_L); | ||
390 | DUMPREG(HDMI_TG_V_FSZ_H); | ||
391 | DUMPREG(HDMI_TG_VSYNC_L); | ||
392 | DUMPREG(HDMI_TG_VSYNC_H); | ||
393 | DUMPREG(HDMI_TG_VSYNC2_L); | ||
394 | DUMPREG(HDMI_TG_VSYNC2_H); | ||
395 | DUMPREG(HDMI_TG_VACT_ST_L); | ||
396 | DUMPREG(HDMI_TG_VACT_ST_H); | ||
397 | DUMPREG(HDMI_TG_VACT_SZ_L); | ||
398 | DUMPREG(HDMI_TG_VACT_SZ_H); | ||
399 | DUMPREG(HDMI_TG_FIELD_CHG_L); | ||
400 | DUMPREG(HDMI_TG_FIELD_CHG_H); | ||
401 | DUMPREG(HDMI_TG_VACT_ST2_L); | ||
402 | DUMPREG(HDMI_TG_VACT_ST2_H); | ||
403 | DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); | ||
404 | DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); | ||
405 | DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); | ||
406 | DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); | ||
407 | DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); | ||
408 | DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); | ||
409 | DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); | ||
410 | DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); | ||
411 | #undef DUMPREG | ||
412 | } | ||
413 | |||
414 | static int hdmi_conf_index(struct drm_display_mode *mode) | ||
415 | { | ||
416 | int i; | ||
417 | |||
418 | for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i) | ||
419 | if (hdmi_confs[i].width == mode->hdisplay && | ||
420 | hdmi_confs[i].height == mode->vdisplay && | ||
421 | hdmi_confs[i].vrefresh == mode->vrefresh && | ||
422 | hdmi_confs[i].interlace == | ||
423 | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? | ||
424 | true : false)) | ||
425 | return i; | ||
426 | |||
427 | return -1; | ||
428 | } | ||
429 | |||
430 | static bool hdmi_is_connected(void *ctx) | ||
431 | { | ||
432 | struct hdmi_context *hdata = (struct hdmi_context *)ctx; | ||
433 | u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS); | ||
434 | |||
435 | if (val) | ||
436 | return true; | ||
437 | |||
438 | return false; | ||
439 | } | ||
440 | |||
441 | static int hdmi_get_edid(void *ctx, struct drm_connector *connector, | ||
442 | u8 *edid, int len) | ||
443 | { | ||
444 | struct edid *raw_edid; | ||
445 | struct hdmi_context *hdata = (struct hdmi_context *)ctx; | ||
446 | |||
447 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
448 | |||
449 | if (!hdata->ddc_port) | ||
450 | return -ENODEV; | ||
451 | |||
452 | raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter); | ||
453 | if (raw_edid) { | ||
454 | memcpy(edid, raw_edid, min((1 + raw_edid->extensions) | ||
455 | * EDID_LENGTH, len)); | ||
456 | DRM_DEBUG_KMS("width[%d] x height[%d]\n", | ||
457 | raw_edid->width_cm, raw_edid->height_cm); | ||
458 | } else { | ||
459 | return -ENODEV; | ||
460 | } | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static int hdmi_check_timing(void *ctx, void *timing) | ||
466 | { | ||
467 | struct fb_videomode *check_timing = timing; | ||
468 | int i; | ||
469 | |||
470 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
471 | |||
472 | DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, | ||
473 | check_timing->yres, check_timing->refresh, | ||
474 | check_timing->vmode); | ||
475 | |||
476 | for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i) | ||
477 | if (hdmi_confs[i].width == check_timing->xres && | ||
478 | hdmi_confs[i].height == check_timing->yres && | ||
479 | hdmi_confs[i].vrefresh == check_timing->refresh && | ||
480 | hdmi_confs[i].interlace == | ||
481 | ((check_timing->vmode & FB_VMODE_INTERLACED) ? | ||
482 | true : false)) | ||
483 | return 0; | ||
484 | |||
485 | return -EINVAL; | ||
486 | } | ||
487 | |||
488 | static int hdmi_display_power_on(void *ctx, int mode) | ||
489 | { | ||
490 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
491 | |||
492 | switch (mode) { | ||
493 | case DRM_MODE_DPMS_ON: | ||
494 | DRM_DEBUG_KMS("hdmi [on]\n"); | ||
495 | break; | ||
496 | case DRM_MODE_DPMS_STANDBY: | ||
497 | break; | ||
498 | case DRM_MODE_DPMS_SUSPEND: | ||
499 | break; | ||
500 | case DRM_MODE_DPMS_OFF: | ||
501 | DRM_DEBUG_KMS("hdmi [off]\n"); | ||
502 | break; | ||
503 | default: | ||
504 | break; | ||
505 | } | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static struct exynos_hdmi_display_ops display_ops = { | ||
511 | .is_connected = hdmi_is_connected, | ||
512 | .get_edid = hdmi_get_edid, | ||
513 | .check_timing = hdmi_check_timing, | ||
514 | .power_on = hdmi_display_power_on, | ||
515 | }; | ||
516 | |||
517 | static void hdmi_conf_reset(struct hdmi_context *hdata) | ||
518 | { | ||
519 | /* disable hpd handle for drm */ | ||
520 | hdata->hpd_handle = false; | ||
521 | |||
522 | /* resetting HDMI core */ | ||
523 | hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); | ||
524 | mdelay(10); | ||
525 | hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); | ||
526 | mdelay(10); | ||
527 | |||
528 | /* enable hpd handle for drm */ | ||
529 | hdata->hpd_handle = true; | ||
530 | } | ||
531 | |||
532 | static void hdmi_conf_init(struct hdmi_context *hdata) | ||
533 | { | ||
534 | /* disable hpd handle for drm */ | ||
535 | hdata->hpd_handle = false; | ||
536 | |||
537 | /* enable HPD interrupts */ | ||
538 | hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL | | ||
539 | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); | ||
540 | mdelay(10); | ||
541 | hdmi_reg_writemask(hdata, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | | ||
542 | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); | ||
543 | |||
544 | /* choose HDMI mode */ | ||
545 | hdmi_reg_writemask(hdata, HDMI_MODE_SEL, | ||
546 | HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); | ||
547 | /* disable bluescreen */ | ||
548 | hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); | ||
549 | /* choose bluescreen (fecal) color */ | ||
550 | hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12); | ||
551 | hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34); | ||
552 | hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56); | ||
553 | /* enable AVI packet every vsync, fixes purple line problem */ | ||
554 | hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); | ||
555 | /* force RGB, look to CEA-861-D, table 7 for more detail */ | ||
556 | hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5); | ||
557 | hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); | ||
558 | |||
559 | hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02); | ||
560 | hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02); | ||
561 | hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04); | ||
562 | |||
563 | /* enable hpd handle for drm */ | ||
564 | hdata->hpd_handle = true; | ||
565 | } | ||
566 | |||
567 | static void hdmi_timing_apply(struct hdmi_context *hdata, | ||
568 | const struct hdmi_preset_conf *conf) | ||
569 | { | ||
570 | const struct hdmi_core_regs *core = &conf->core; | ||
571 | const struct hdmi_tg_regs *tg = &conf->tg; | ||
572 | int tries; | ||
573 | |||
574 | /* setting core registers */ | ||
575 | hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); | ||
576 | hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); | ||
577 | hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]); | ||
578 | hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]); | ||
579 | hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]); | ||
580 | hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]); | ||
581 | hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]); | ||
582 | hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]); | ||
583 | hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); | ||
584 | hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); | ||
585 | hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]); | ||
586 | hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]); | ||
587 | hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]); | ||
588 | hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); | ||
589 | hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); | ||
590 | hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); | ||
591 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); | ||
592 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); | ||
593 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); | ||
594 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); | ||
595 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); | ||
596 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); | ||
597 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); | ||
598 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); | ||
599 | hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); | ||
600 | /* Timing generator registers */ | ||
601 | hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); | ||
602 | hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); | ||
603 | hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l); | ||
604 | hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h); | ||
605 | hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); | ||
606 | hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); | ||
607 | hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l); | ||
608 | hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h); | ||
609 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l); | ||
610 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h); | ||
611 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l); | ||
612 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h); | ||
613 | hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l); | ||
614 | hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h); | ||
615 | hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); | ||
616 | hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); | ||
617 | hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); | ||
618 | hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); | ||
619 | hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); | ||
620 | hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); | ||
621 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); | ||
622 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); | ||
623 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); | ||
624 | hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); | ||
625 | hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); | ||
626 | hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); | ||
627 | hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); | ||
628 | hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); | ||
629 | |||
630 | /* waiting for HDMIPHY's PLL to get to steady state */ | ||
631 | for (tries = 100; tries; --tries) { | ||
632 | u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS); | ||
633 | if (val & HDMI_PHY_STATUS_READY) | ||
634 | break; | ||
635 | mdelay(1); | ||
636 | } | ||
637 | /* steady state not achieved */ | ||
638 | if (tries == 0) { | ||
639 | DRM_ERROR("hdmiphy's pll could not reach steady state.\n"); | ||
640 | hdmi_regs_dump(hdata, "timing apply"); | ||
641 | } | ||
642 | |||
643 | clk_disable(hdata->res.sclk_hdmi); | ||
644 | clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); | ||
645 | clk_enable(hdata->res.sclk_hdmi); | ||
646 | |||
647 | /* enable HDMI and timing generator */ | ||
648 | hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); | ||
649 | if (core->int_pro_mode[0]) | ||
650 | hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | | ||
651 | HDMI_FIELD_EN); | ||
652 | else | ||
653 | hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); | ||
654 | } | ||
655 | |||
656 | static void hdmiphy_conf_reset(struct hdmi_context *hdata) | ||
657 | { | ||
658 | u8 buffer[2]; | ||
659 | |||
660 | clk_disable(hdata->res.sclk_hdmi); | ||
661 | clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel); | ||
662 | clk_enable(hdata->res.sclk_hdmi); | ||
663 | |||
664 | /* operation mode */ | ||
665 | buffer[0] = 0x1f; | ||
666 | buffer[1] = 0x00; | ||
667 | |||
668 | if (hdata->hdmiphy_port) | ||
669 | i2c_master_send(hdata->hdmiphy_port, buffer, 2); | ||
670 | |||
671 | /* reset hdmiphy */ | ||
672 | hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); | ||
673 | mdelay(10); | ||
674 | hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); | ||
675 | mdelay(10); | ||
676 | } | ||
677 | |||
678 | static void hdmiphy_conf_apply(struct hdmi_context *hdata) | ||
679 | { | ||
680 | u8 buffer[32]; | ||
681 | u8 operation[2]; | ||
682 | u8 read_buffer[32] = {0, }; | ||
683 | int ret; | ||
684 | int i; | ||
685 | |||
686 | if (!hdata->hdmiphy_port) { | ||
687 | DRM_ERROR("hdmiphy is not attached\n"); | ||
688 | return; | ||
689 | } | ||
690 | |||
691 | /* pixel clock */ | ||
692 | memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32); | ||
693 | ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32); | ||
694 | if (ret != 32) { | ||
695 | DRM_ERROR("failed to configure HDMIPHY via I2C\n"); | ||
696 | return; | ||
697 | } | ||
698 | |||
699 | mdelay(10); | ||
700 | |||
701 | /* operation mode */ | ||
702 | operation[0] = 0x1f; | ||
703 | operation[1] = 0x80; | ||
704 | |||
705 | ret = i2c_master_send(hdata->hdmiphy_port, operation, 2); | ||
706 | if (ret != 2) { | ||
707 | DRM_ERROR("failed to enable hdmiphy\n"); | ||
708 | return; | ||
709 | } | ||
710 | |||
711 | ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32); | ||
712 | if (ret < 0) { | ||
713 | DRM_ERROR("failed to read hdmiphy config\n"); | ||
714 | return; | ||
715 | } | ||
716 | |||
717 | for (i = 0; i < ret; i++) | ||
718 | DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - " | ||
719 | "recv [0x%02x]\n", i, buffer[i], read_buffer[i]); | ||
720 | } | ||
721 | |||
722 | static void hdmi_conf_apply(struct hdmi_context *hdata) | ||
723 | { | ||
724 | const struct hdmi_preset_conf *conf = | ||
725 | hdmi_confs[hdata->cur_conf].conf; | ||
726 | |||
727 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
728 | |||
729 | hdmiphy_conf_reset(hdata); | ||
730 | hdmiphy_conf_apply(hdata); | ||
731 | |||
732 | hdmi_conf_reset(hdata); | ||
733 | hdmi_conf_init(hdata); | ||
734 | |||
735 | /* setting core registers */ | ||
736 | hdmi_timing_apply(hdata, conf); | ||
737 | |||
738 | hdmi_regs_dump(hdata, "start"); | ||
739 | } | ||
740 | |||
741 | static void hdmi_mode_set(void *ctx, void *mode) | ||
742 | { | ||
743 | struct hdmi_context *hdata = (struct hdmi_context *)ctx; | ||
744 | int conf_idx; | ||
745 | |||
746 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
747 | |||
748 | conf_idx = hdmi_conf_index(mode); | ||
749 | if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs)) | ||
750 | hdata->cur_conf = conf_idx; | ||
751 | else | ||
752 | DRM_DEBUG_KMS("not supported mode\n"); | ||
753 | } | ||
754 | |||
755 | static void hdmi_commit(void *ctx) | ||
756 | { | ||
757 | struct hdmi_context *hdata = (struct hdmi_context *)ctx; | ||
758 | |||
759 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
760 | |||
761 | hdmi_conf_apply(hdata); | ||
762 | |||
763 | hdata->enabled = true; | ||
764 | } | ||
765 | |||
766 | static void hdmi_disable(void *ctx) | ||
767 | { | ||
768 | struct hdmi_context *hdata = (struct hdmi_context *)ctx; | ||
769 | |||
770 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
771 | |||
772 | if (hdata->enabled) { | ||
773 | hdmiphy_conf_reset(hdata); | ||
774 | hdmi_conf_reset(hdata); | ||
775 | } | ||
776 | } | ||
777 | |||
778 | static struct exynos_hdmi_manager_ops manager_ops = { | ||
779 | .mode_set = hdmi_mode_set, | ||
780 | .commit = hdmi_commit, | ||
781 | .disable = hdmi_disable, | ||
782 | }; | ||
783 | |||
784 | /* | ||
785 | * Handle hotplug events outside the interrupt handler proper. | ||
786 | */ | ||
787 | static void hdmi_hotplug_func(struct work_struct *work) | ||
788 | { | ||
789 | struct hdmi_context *hdata = | ||
790 | container_of(work, struct hdmi_context, hotplug_work); | ||
791 | struct exynos_drm_hdmi_context *ctx = | ||
792 | (struct exynos_drm_hdmi_context *)hdata->parent_ctx; | ||
793 | |||
794 | drm_helper_hpd_irq_event(ctx->drm_dev); | ||
795 | } | ||
796 | |||
797 | static irqreturn_t hdmi_irq_handler(int irq, void *arg) | ||
798 | { | ||
799 | struct exynos_drm_hdmi_context *ctx = arg; | ||
800 | struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx; | ||
801 | u32 intc_flag; | ||
802 | |||
803 | intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG); | ||
804 | /* clearing flags for HPD plug/unplug */ | ||
805 | if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { | ||
806 | DRM_DEBUG_KMS("unplugged, handling:%d\n", hdata->hpd_handle); | ||
807 | hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, | ||
808 | HDMI_INTC_FLAG_HPD_UNPLUG); | ||
809 | } | ||
810 | if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { | ||
811 | DRM_DEBUG_KMS("plugged, handling:%d\n", hdata->hpd_handle); | ||
812 | hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, | ||
813 | HDMI_INTC_FLAG_HPD_PLUG); | ||
814 | } | ||
815 | |||
816 | if (ctx->drm_dev && hdata->hpd_handle) | ||
817 | queue_work(hdata->wq, &hdata->hotplug_work); | ||
818 | |||
819 | return IRQ_HANDLED; | ||
820 | } | ||
821 | |||
822 | static int __devinit hdmi_resources_init(struct hdmi_context *hdata) | ||
823 | { | ||
824 | struct device *dev = hdata->dev; | ||
825 | struct hdmi_resources *res = &hdata->res; | ||
826 | static char *supply[] = { | ||
827 | "hdmi-en", | ||
828 | "vdd", | ||
829 | "vdd_osc", | ||
830 | "vdd_pll", | ||
831 | }; | ||
832 | int i, ret; | ||
833 | |||
834 | DRM_DEBUG_KMS("HDMI resource init\n"); | ||
835 | |||
836 | memset(res, 0, sizeof *res); | ||
837 | |||
838 | /* get clocks, power */ | ||
839 | res->hdmi = clk_get(dev, "hdmi"); | ||
840 | if (IS_ERR_OR_NULL(res->hdmi)) { | ||
841 | DRM_ERROR("failed to get clock 'hdmi'\n"); | ||
842 | goto fail; | ||
843 | } | ||
844 | res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); | ||
845 | if (IS_ERR_OR_NULL(res->sclk_hdmi)) { | ||
846 | DRM_ERROR("failed to get clock 'sclk_hdmi'\n"); | ||
847 | goto fail; | ||
848 | } | ||
849 | res->sclk_pixel = clk_get(dev, "sclk_pixel"); | ||
850 | if (IS_ERR_OR_NULL(res->sclk_pixel)) { | ||
851 | DRM_ERROR("failed to get clock 'sclk_pixel'\n"); | ||
852 | goto fail; | ||
853 | } | ||
854 | res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); | ||
855 | if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { | ||
856 | DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n"); | ||
857 | goto fail; | ||
858 | } | ||
859 | res->hdmiphy = clk_get(dev, "hdmiphy"); | ||
860 | if (IS_ERR_OR_NULL(res->hdmiphy)) { | ||
861 | DRM_ERROR("failed to get clock 'hdmiphy'\n"); | ||
862 | goto fail; | ||
863 | } | ||
864 | |||
865 | clk_set_parent(res->sclk_hdmi, res->sclk_pixel); | ||
866 | |||
867 | res->regul_bulk = kzalloc(ARRAY_SIZE(supply) * | ||
868 | sizeof res->regul_bulk[0], GFP_KERNEL); | ||
869 | if (!res->regul_bulk) { | ||
870 | DRM_ERROR("failed to get memory for regulators\n"); | ||
871 | goto fail; | ||
872 | } | ||
873 | for (i = 0; i < ARRAY_SIZE(supply); ++i) { | ||
874 | res->regul_bulk[i].supply = supply[i]; | ||
875 | res->regul_bulk[i].consumer = NULL; | ||
876 | } | ||
877 | ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); | ||
878 | if (ret) { | ||
879 | DRM_ERROR("failed to get regulators\n"); | ||
880 | goto fail; | ||
881 | } | ||
882 | res->regul_count = ARRAY_SIZE(supply); | ||
883 | |||
884 | return 0; | ||
885 | fail: | ||
886 | DRM_ERROR("HDMI resource init - failed\n"); | ||
887 | return -ENODEV; | ||
888 | } | ||
889 | |||
890 | static int hdmi_resources_cleanup(struct hdmi_context *hdata) | ||
891 | { | ||
892 | struct hdmi_resources *res = &hdata->res; | ||
893 | |||
894 | regulator_bulk_free(res->regul_count, res->regul_bulk); | ||
895 | /* kfree is NULL-safe */ | ||
896 | kfree(res->regul_bulk); | ||
897 | if (!IS_ERR_OR_NULL(res->hdmiphy)) | ||
898 | clk_put(res->hdmiphy); | ||
899 | if (!IS_ERR_OR_NULL(res->sclk_hdmiphy)) | ||
900 | clk_put(res->sclk_hdmiphy); | ||
901 | if (!IS_ERR_OR_NULL(res->sclk_pixel)) | ||
902 | clk_put(res->sclk_pixel); | ||
903 | if (!IS_ERR_OR_NULL(res->sclk_hdmi)) | ||
904 | clk_put(res->sclk_hdmi); | ||
905 | if (!IS_ERR_OR_NULL(res->hdmi)) | ||
906 | clk_put(res->hdmi); | ||
907 | memset(res, 0, sizeof *res); | ||
908 | |||
909 | return 0; | ||
910 | } | ||
911 | |||
912 | static void hdmi_resource_poweron(struct hdmi_context *hdata) | ||
913 | { | ||
914 | struct hdmi_resources *res = &hdata->res; | ||
915 | |||
916 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
917 | |||
918 | /* turn HDMI power on */ | ||
919 | regulator_bulk_enable(res->regul_count, res->regul_bulk); | ||
920 | /* power-on hdmi physical interface */ | ||
921 | clk_enable(res->hdmiphy); | ||
922 | /* turn clocks on */ | ||
923 | clk_enable(res->hdmi); | ||
924 | clk_enable(res->sclk_hdmi); | ||
925 | |||
926 | hdmiphy_conf_reset(hdata); | ||
927 | hdmi_conf_reset(hdata); | ||
928 | hdmi_conf_init(hdata); | ||
929 | |||
930 | } | ||
931 | |||
932 | static void hdmi_resource_poweroff(struct hdmi_context *hdata) | ||
933 | { | ||
934 | struct hdmi_resources *res = &hdata->res; | ||
935 | |||
936 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
937 | |||
938 | /* turn clocks off */ | ||
939 | clk_disable(res->sclk_hdmi); | ||
940 | clk_disable(res->hdmi); | ||
941 | /* power-off hdmiphy */ | ||
942 | clk_disable(res->hdmiphy); | ||
943 | /* turn HDMI power off */ | ||
944 | regulator_bulk_disable(res->regul_count, res->regul_bulk); | ||
945 | } | ||
946 | |||
947 | static int hdmi_runtime_suspend(struct device *dev) | ||
948 | { | ||
949 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); | ||
950 | |||
951 | DRM_DEBUG_KMS("%s\n", __func__); | ||
952 | |||
953 | hdmi_resource_poweroff((struct hdmi_context *)ctx->ctx); | ||
954 | |||
955 | return 0; | ||
956 | } | ||
957 | |||
958 | static int hdmi_runtime_resume(struct device *dev) | ||
959 | { | ||
960 | struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); | ||
961 | |||
962 | DRM_DEBUG_KMS("%s\n", __func__); | ||
963 | |||
964 | hdmi_resource_poweron((struct hdmi_context *)ctx->ctx); | ||
965 | |||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static const struct dev_pm_ops hdmi_pm_ops = { | ||
970 | .runtime_suspend = hdmi_runtime_suspend, | ||
971 | .runtime_resume = hdmi_runtime_resume, | ||
972 | }; | ||
973 | |||
974 | static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; | ||
975 | |||
976 | void hdmi_attach_ddc_client(struct i2c_client *ddc) | ||
977 | { | ||
978 | if (ddc) | ||
979 | hdmi_ddc = ddc; | ||
980 | } | ||
981 | EXPORT_SYMBOL(hdmi_attach_ddc_client); | ||
982 | |||
983 | void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy) | ||
984 | { | ||
985 | if (hdmiphy) | ||
986 | hdmi_hdmiphy = hdmiphy; | ||
987 | } | ||
988 | EXPORT_SYMBOL(hdmi_attach_hdmiphy_client); | ||
989 | |||
990 | static int __devinit hdmi_probe(struct platform_device *pdev) | ||
991 | { | ||
992 | struct device *dev = &pdev->dev; | ||
993 | struct exynos_drm_hdmi_context *drm_hdmi_ctx; | ||
994 | struct hdmi_context *hdata; | ||
995 | struct exynos_drm_hdmi_pdata *pdata; | ||
996 | struct resource *res; | ||
997 | int ret; | ||
998 | |||
999 | DRM_DEBUG_KMS("[%d]\n", __LINE__); | ||
1000 | |||
1001 | pdata = pdev->dev.platform_data; | ||
1002 | if (!pdata) { | ||
1003 | DRM_ERROR("no platform data specified\n"); | ||
1004 | return -EINVAL; | ||
1005 | } | ||
1006 | |||
1007 | drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); | ||
1008 | if (!drm_hdmi_ctx) { | ||
1009 | DRM_ERROR("failed to allocate common hdmi context.\n"); | ||
1010 | return -ENOMEM; | ||
1011 | } | ||
1012 | |||
1013 | hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL); | ||
1014 | if (!hdata) { | ||
1015 | DRM_ERROR("out of memory\n"); | ||
1016 | kfree(drm_hdmi_ctx); | ||
1017 | return -ENOMEM; | ||
1018 | } | ||
1019 | |||
1020 | drm_hdmi_ctx->ctx = (void *)hdata; | ||
1021 | hdata->parent_ctx = (void *)drm_hdmi_ctx; | ||
1022 | |||
1023 | platform_set_drvdata(pdev, drm_hdmi_ctx); | ||
1024 | |||
1025 | hdata->default_win = pdata->default_win; | ||
1026 | hdata->default_timing = &pdata->timing; | ||
1027 | hdata->default_bpp = pdata->bpp; | ||
1028 | hdata->dev = dev; | ||
1029 | |||
1030 | ret = hdmi_resources_init(hdata); | ||
1031 | if (ret) { | ||
1032 | ret = -EINVAL; | ||
1033 | goto err_data; | ||
1034 | } | ||
1035 | |||
1036 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1037 | if (!res) { | ||
1038 | DRM_ERROR("failed to find registers\n"); | ||
1039 | ret = -ENOENT; | ||
1040 | goto err_resource; | ||
1041 | } | ||
1042 | |||
1043 | hdata->regs_res = request_mem_region(res->start, resource_size(res), | ||
1044 | dev_name(dev)); | ||
1045 | if (!hdata->regs_res) { | ||
1046 | DRM_ERROR("failed to claim register region\n"); | ||
1047 | ret = -ENOENT; | ||
1048 | goto err_resource; | ||
1049 | } | ||
1050 | |||
1051 | hdata->regs = ioremap(res->start, resource_size(res)); | ||
1052 | if (!hdata->regs) { | ||
1053 | DRM_ERROR("failed to map registers\n"); | ||
1054 | ret = -ENXIO; | ||
1055 | goto err_req_region; | ||
1056 | } | ||
1057 | |||
1058 | /* DDC i2c driver */ | ||
1059 | if (i2c_add_driver(&ddc_driver)) { | ||
1060 | DRM_ERROR("failed to register ddc i2c driver\n"); | ||
1061 | ret = -ENOENT; | ||
1062 | goto err_iomap; | ||
1063 | } | ||
1064 | |||
1065 | hdata->ddc_port = hdmi_ddc; | ||
1066 | |||
1067 | /* hdmiphy i2c driver */ | ||
1068 | if (i2c_add_driver(&hdmiphy_driver)) { | ||
1069 | DRM_ERROR("failed to register hdmiphy i2c driver\n"); | ||
1070 | ret = -ENOENT; | ||
1071 | goto err_ddc; | ||
1072 | } | ||
1073 | |||
1074 | hdata->hdmiphy_port = hdmi_hdmiphy; | ||
1075 | |||
1076 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
1077 | if (res == NULL) { | ||
1078 | DRM_ERROR("get interrupt resource failed.\n"); | ||
1079 | ret = -ENXIO; | ||
1080 | goto err_hdmiphy; | ||
1081 | } | ||
1082 | |||
1083 | /* create workqueue and hotplug work */ | ||
1084 | hdata->wq = alloc_workqueue("exynos-drm-hdmi", | ||
1085 | WQ_UNBOUND | WQ_NON_REENTRANT, 1); | ||
1086 | if (hdata->wq == NULL) { | ||
1087 | DRM_ERROR("Failed to create workqueue.\n"); | ||
1088 | ret = -ENOMEM; | ||
1089 | goto err_hdmiphy; | ||
1090 | } | ||
1091 | INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func); | ||
1092 | |||
1093 | /* register hpd interrupt */ | ||
1094 | ret = request_irq(res->start, hdmi_irq_handler, 0, "drm_hdmi", | ||
1095 | drm_hdmi_ctx); | ||
1096 | if (ret) { | ||
1097 | DRM_ERROR("request interrupt failed.\n"); | ||
1098 | goto err_workqueue; | ||
1099 | } | ||
1100 | hdata->irq = res->start; | ||
1101 | |||
1102 | /* register specific callbacks to common hdmi. */ | ||
1103 | exynos_drm_display_ops_register(&display_ops); | ||
1104 | exynos_drm_manager_ops_register(&manager_ops); | ||
1105 | |||
1106 | hdmi_resource_poweron(hdata); | ||
1107 | |||
1108 | return 0; | ||
1109 | |||
1110 | err_workqueue: | ||
1111 | destroy_workqueue(hdata->wq); | ||
1112 | err_hdmiphy: | ||
1113 | i2c_del_driver(&hdmiphy_driver); | ||
1114 | err_ddc: | ||
1115 | i2c_del_driver(&ddc_driver); | ||
1116 | err_iomap: | ||
1117 | iounmap(hdata->regs); | ||
1118 | err_req_region: | ||
1119 | release_resource(hdata->regs_res); | ||
1120 | kfree(hdata->regs_res); | ||
1121 | err_resource: | ||
1122 | hdmi_resources_cleanup(hdata); | ||
1123 | err_data: | ||
1124 | kfree(hdata); | ||
1125 | kfree(drm_hdmi_ctx); | ||
1126 | return ret; | ||
1127 | } | ||
1128 | |||
1129 | static int __devexit hdmi_remove(struct platform_device *pdev) | ||
1130 | { | ||
1131 | struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev); | ||
1132 | struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx; | ||
1133 | |||
1134 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
1135 | |||
1136 | hdmi_resource_poweroff(hdata); | ||
1137 | |||
1138 | disable_irq(hdata->irq); | ||
1139 | free_irq(hdata->irq, hdata); | ||
1140 | |||
1141 | cancel_work_sync(&hdata->hotplug_work); | ||
1142 | destroy_workqueue(hdata->wq); | ||
1143 | |||
1144 | hdmi_resources_cleanup(hdata); | ||
1145 | |||
1146 | iounmap(hdata->regs); | ||
1147 | |||
1148 | release_resource(hdata->regs_res); | ||
1149 | kfree(hdata->regs_res); | ||
1150 | |||
1151 | /* hdmiphy i2c driver */ | ||
1152 | i2c_del_driver(&hdmiphy_driver); | ||
1153 | /* DDC i2c driver */ | ||
1154 | i2c_del_driver(&ddc_driver); | ||
1155 | |||
1156 | kfree(hdata); | ||
1157 | |||
1158 | return 0; | ||
1159 | } | ||
1160 | |||
1161 | struct platform_driver hdmi_driver = { | ||
1162 | .probe = hdmi_probe, | ||
1163 | .remove = __devexit_p(hdmi_remove), | ||
1164 | .driver = { | ||
1165 | .name = "exynos4-hdmi", | ||
1166 | .owner = THIS_MODULE, | ||
1167 | .pm = &hdmi_pm_ops, | ||
1168 | }, | ||
1169 | }; | ||
1170 | EXPORT_SYMBOL(hdmi_driver); | ||
1171 | |||
1172 | MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); | ||
1173 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
1174 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
1175 | MODULE_DESCRIPTION("Samsung DRM HDMI core Driver"); | ||
1176 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h new file mode 100644 index 000000000000..31d6cf84c1aa --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Inki Dae <inki.dae@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_HDMI_H_ | ||
29 | #define _EXYNOS_HDMI_H_ | ||
30 | |||
31 | struct hdmi_conf { | ||
32 | int width; | ||
33 | int height; | ||
34 | int vrefresh; | ||
35 | bool interlace; | ||
36 | const u8 *hdmiphy_data; | ||
37 | const struct hdmi_preset_conf *conf; | ||
38 | }; | ||
39 | |||
40 | struct hdmi_resources { | ||
41 | struct clk *hdmi; | ||
42 | struct clk *sclk_hdmi; | ||
43 | struct clk *sclk_pixel; | ||
44 | struct clk *sclk_hdmiphy; | ||
45 | struct clk *hdmiphy; | ||
46 | struct regulator_bulk_data *regul_bulk; | ||
47 | int regul_count; | ||
48 | }; | ||
49 | |||
50 | struct hdmi_context { | ||
51 | struct device *dev; | ||
52 | struct drm_device *drm_dev; | ||
53 | struct fb_videomode *default_timing; | ||
54 | unsigned int default_win; | ||
55 | unsigned int default_bpp; | ||
56 | bool hpd_handle; | ||
57 | bool enabled; | ||
58 | |||
59 | struct resource *regs_res; | ||
60 | /** base address of HDMI registers */ | ||
61 | void __iomem *regs; | ||
62 | /** HDMI hotplug interrupt */ | ||
63 | unsigned int irq; | ||
64 | /** workqueue for delayed work */ | ||
65 | struct workqueue_struct *wq; | ||
66 | /** hotplug handling work */ | ||
67 | struct work_struct hotplug_work; | ||
68 | |||
69 | struct i2c_client *ddc_port; | ||
70 | struct i2c_client *hdmiphy_port; | ||
71 | |||
72 | /** current hdmiphy conf index */ | ||
73 | int cur_conf; | ||
74 | /** other resources */ | ||
75 | struct hdmi_resources res; | ||
76 | |||
77 | void *parent_ctx; | ||
78 | }; | ||
79 | |||
80 | |||
81 | void hdmi_attach_ddc_client(struct i2c_client *ddc); | ||
82 | void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy); | ||
83 | |||
84 | extern struct i2c_driver hdmiphy_driver; | ||
85 | extern struct i2c_driver ddc_driver; | ||
86 | |||
87 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c new file mode 100644 index 000000000000..9fe2995ab9f9 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
3 | * Authors: | ||
4 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include "drmP.h" | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/module.h> | ||
19 | |||
20 | #include "exynos_drm_drv.h" | ||
21 | #include "exynos_hdmi.h" | ||
22 | |||
23 | |||
24 | static int hdmiphy_probe(struct i2c_client *client, | ||
25 | const struct i2c_device_id *id) | ||
26 | { | ||
27 | hdmi_attach_hdmiphy_client(client); | ||
28 | |||
29 | dev_info(&client->adapter->dev, "attached s5p_hdmiphy " | ||
30 | "into i2c adapter successfully\n"); | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static int hdmiphy_remove(struct i2c_client *client) | ||
36 | { | ||
37 | dev_info(&client->adapter->dev, "detached s5p_hdmiphy " | ||
38 | "from i2c adapter successfully\n"); | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static const struct i2c_device_id hdmiphy_id[] = { | ||
44 | { "s5p_hdmiphy", 0 }, | ||
45 | { }, | ||
46 | }; | ||
47 | |||
48 | struct i2c_driver hdmiphy_driver = { | ||
49 | .driver = { | ||
50 | .name = "s5p-hdmiphy", | ||
51 | .owner = THIS_MODULE, | ||
52 | }, | ||
53 | .id_table = hdmiphy_id, | ||
54 | .probe = hdmiphy_probe, | ||
55 | .remove = __devexit_p(hdmiphy_remove), | ||
56 | .command = NULL, | ||
57 | }; | ||
58 | EXPORT_SYMBOL(hdmiphy_driver); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c new file mode 100644 index 000000000000..ac24cff39775 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_mixer.c | |||
@@ -0,0 +1,1070 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
3 | * Authors: | ||
4 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
5 | * Inki Dae <inki.dae@samsung.com> | ||
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | ||
7 | * | ||
8 | * Based on drivers/media/video/s5p-tv/mixer_reg.c | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include "drmP.h" | ||
18 | |||
19 | #include "regs-mixer.h" | ||
20 | #include "regs-vp.h" | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/irq.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <linux/pm_runtime.h> | ||
32 | #include <linux/clk.h> | ||
33 | #include <linux/regulator/consumer.h> | ||
34 | |||
35 | #include <drm/exynos_drm.h> | ||
36 | |||
37 | #include "exynos_drm_drv.h" | ||
38 | #include "exynos_drm_hdmi.h" | ||
39 | #include "exynos_hdmi.h" | ||
40 | #include "exynos_mixer.h" | ||
41 | |||
42 | #define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
43 | |||
44 | static const u8 filter_y_horiz_tap8[] = { | ||
45 | 0, -1, -1, -1, -1, -1, -1, -1, | ||
46 | -1, -1, -1, -1, -1, 0, 0, 0, | ||
47 | 0, 2, 4, 5, 6, 6, 6, 6, | ||
48 | 6, 5, 5, 4, 3, 2, 1, 1, | ||
49 | 0, -6, -12, -16, -18, -20, -21, -20, | ||
50 | -20, -18, -16, -13, -10, -8, -5, -2, | ||
51 | 127, 126, 125, 121, 114, 107, 99, 89, | ||
52 | 79, 68, 57, 46, 35, 25, 16, 8, | ||
53 | }; | ||
54 | |||
55 | static const u8 filter_y_vert_tap4[] = { | ||
56 | 0, -3, -6, -8, -8, -8, -8, -7, | ||
57 | -6, -5, -4, -3, -2, -1, -1, 0, | ||
58 | 127, 126, 124, 118, 111, 102, 92, 81, | ||
59 | 70, 59, 48, 37, 27, 19, 11, 5, | ||
60 | 0, 5, 11, 19, 27, 37, 48, 59, | ||
61 | 70, 81, 92, 102, 111, 118, 124, 126, | ||
62 | 0, 0, -1, -1, -2, -3, -4, -5, | ||
63 | -6, -7, -8, -8, -8, -8, -6, -3, | ||
64 | }; | ||
65 | |||
66 | static const u8 filter_cr_horiz_tap4[] = { | ||
67 | 0, -3, -6, -8, -8, -8, -8, -7, | ||
68 | -6, -5, -4, -3, -2, -1, -1, 0, | ||
69 | 127, 126, 124, 118, 111, 102, 92, 81, | ||
70 | 70, 59, 48, 37, 27, 19, 11, 5, | ||
71 | }; | ||
72 | |||
73 | static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id) | ||
74 | { | ||
75 | return readl(res->vp_regs + reg_id); | ||
76 | } | ||
77 | |||
78 | static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id, | ||
79 | u32 val) | ||
80 | { | ||
81 | writel(val, res->vp_regs + reg_id); | ||
82 | } | ||
83 | |||
84 | static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id, | ||
85 | u32 val, u32 mask) | ||
86 | { | ||
87 | u32 old = vp_reg_read(res, reg_id); | ||
88 | |||
89 | val = (val & mask) | (old & ~mask); | ||
90 | writel(val, res->vp_regs + reg_id); | ||
91 | } | ||
92 | |||
93 | static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id) | ||
94 | { | ||
95 | return readl(res->mixer_regs + reg_id); | ||
96 | } | ||
97 | |||
98 | static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id, | ||
99 | u32 val) | ||
100 | { | ||
101 | writel(val, res->mixer_regs + reg_id); | ||
102 | } | ||
103 | |||
104 | static inline void mixer_reg_writemask(struct mixer_resources *res, | ||
105 | u32 reg_id, u32 val, u32 mask) | ||
106 | { | ||
107 | u32 old = mixer_reg_read(res, reg_id); | ||
108 | |||
109 | val = (val & mask) | (old & ~mask); | ||
110 | writel(val, res->mixer_regs + reg_id); | ||
111 | } | ||
112 | |||
113 | static void mixer_regs_dump(struct mixer_context *ctx) | ||
114 | { | ||
115 | #define DUMPREG(reg_id) \ | ||
116 | do { \ | ||
117 | DRM_DEBUG_KMS(#reg_id " = %08x\n", \ | ||
118 | (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \ | ||
119 | } while (0) | ||
120 | |||
121 | DUMPREG(MXR_STATUS); | ||
122 | DUMPREG(MXR_CFG); | ||
123 | DUMPREG(MXR_INT_EN); | ||
124 | DUMPREG(MXR_INT_STATUS); | ||
125 | |||
126 | DUMPREG(MXR_LAYER_CFG); | ||
127 | DUMPREG(MXR_VIDEO_CFG); | ||
128 | |||
129 | DUMPREG(MXR_GRAPHIC0_CFG); | ||
130 | DUMPREG(MXR_GRAPHIC0_BASE); | ||
131 | DUMPREG(MXR_GRAPHIC0_SPAN); | ||
132 | DUMPREG(MXR_GRAPHIC0_WH); | ||
133 | DUMPREG(MXR_GRAPHIC0_SXY); | ||
134 | DUMPREG(MXR_GRAPHIC0_DXY); | ||
135 | |||
136 | DUMPREG(MXR_GRAPHIC1_CFG); | ||
137 | DUMPREG(MXR_GRAPHIC1_BASE); | ||
138 | DUMPREG(MXR_GRAPHIC1_SPAN); | ||
139 | DUMPREG(MXR_GRAPHIC1_WH); | ||
140 | DUMPREG(MXR_GRAPHIC1_SXY); | ||
141 | DUMPREG(MXR_GRAPHIC1_DXY); | ||
142 | #undef DUMPREG | ||
143 | } | ||
144 | |||
145 | static void vp_regs_dump(struct mixer_context *ctx) | ||
146 | { | ||
147 | #define DUMPREG(reg_id) \ | ||
148 | do { \ | ||
149 | DRM_DEBUG_KMS(#reg_id " = %08x\n", \ | ||
150 | (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \ | ||
151 | } while (0) | ||
152 | |||
153 | DUMPREG(VP_ENABLE); | ||
154 | DUMPREG(VP_SRESET); | ||
155 | DUMPREG(VP_SHADOW_UPDATE); | ||
156 | DUMPREG(VP_FIELD_ID); | ||
157 | DUMPREG(VP_MODE); | ||
158 | DUMPREG(VP_IMG_SIZE_Y); | ||
159 | DUMPREG(VP_IMG_SIZE_C); | ||
160 | DUMPREG(VP_PER_RATE_CTRL); | ||
161 | DUMPREG(VP_TOP_Y_PTR); | ||
162 | DUMPREG(VP_BOT_Y_PTR); | ||
163 | DUMPREG(VP_TOP_C_PTR); | ||
164 | DUMPREG(VP_BOT_C_PTR); | ||
165 | DUMPREG(VP_ENDIAN_MODE); | ||
166 | DUMPREG(VP_SRC_H_POSITION); | ||
167 | DUMPREG(VP_SRC_V_POSITION); | ||
168 | DUMPREG(VP_SRC_WIDTH); | ||
169 | DUMPREG(VP_SRC_HEIGHT); | ||
170 | DUMPREG(VP_DST_H_POSITION); | ||
171 | DUMPREG(VP_DST_V_POSITION); | ||
172 | DUMPREG(VP_DST_WIDTH); | ||
173 | DUMPREG(VP_DST_HEIGHT); | ||
174 | DUMPREG(VP_H_RATIO); | ||
175 | DUMPREG(VP_V_RATIO); | ||
176 | |||
177 | #undef DUMPREG | ||
178 | } | ||
179 | |||
180 | static inline void vp_filter_set(struct mixer_resources *res, | ||
181 | int reg_id, const u8 *data, unsigned int size) | ||
182 | { | ||
183 | /* assure 4-byte align */ | ||
184 | BUG_ON(size & 3); | ||
185 | for (; size; size -= 4, reg_id += 4, data += 4) { | ||
186 | u32 val = (data[0] << 24) | (data[1] << 16) | | ||
187 | (data[2] << 8) | data[3]; | ||
188 | vp_reg_write(res, reg_id, val); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void vp_default_filter(struct mixer_resources *res) | ||
193 | { | ||
194 | vp_filter_set(res, VP_POLY8_Y0_LL, | ||
195 | filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); | ||
196 | vp_filter_set(res, VP_POLY4_Y0_LL, | ||
197 | filter_y_vert_tap4, sizeof filter_y_vert_tap4); | ||
198 | vp_filter_set(res, VP_POLY4_C0_LL, | ||
199 | filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4); | ||
200 | } | ||
201 | |||
202 | static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable) | ||
203 | { | ||
204 | struct mixer_resources *res = &ctx->mixer_res; | ||
205 | |||
206 | /* block update on vsync */ | ||
207 | mixer_reg_writemask(res, MXR_STATUS, enable ? | ||
208 | MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE); | ||
209 | |||
210 | vp_reg_write(res, VP_SHADOW_UPDATE, enable ? | ||
211 | VP_SHADOW_UPDATE_ENABLE : 0); | ||
212 | } | ||
213 | |||
214 | static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height) | ||
215 | { | ||
216 | struct mixer_resources *res = &ctx->mixer_res; | ||
217 | u32 val; | ||
218 | |||
219 | /* choosing between interlace and progressive mode */ | ||
220 | val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE : | ||
221 | MXR_CFG_SCAN_PROGRASSIVE); | ||
222 | |||
223 | /* choosing between porper HD and SD mode */ | ||
224 | if (height == 480) | ||
225 | val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; | ||
226 | else if (height == 576) | ||
227 | val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; | ||
228 | else if (height == 720) | ||
229 | val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; | ||
230 | else if (height == 1080) | ||
231 | val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; | ||
232 | else | ||
233 | val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; | ||
234 | |||
235 | mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK); | ||
236 | } | ||
237 | |||
238 | static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height) | ||
239 | { | ||
240 | struct mixer_resources *res = &ctx->mixer_res; | ||
241 | u32 val; | ||
242 | |||
243 | if (height == 480) { | ||
244 | val = MXR_CFG_RGB601_0_255; | ||
245 | } else if (height == 576) { | ||
246 | val = MXR_CFG_RGB601_0_255; | ||
247 | } else if (height == 720) { | ||
248 | val = MXR_CFG_RGB709_16_235; | ||
249 | mixer_reg_write(res, MXR_CM_COEFF_Y, | ||
250 | (1 << 30) | (94 << 20) | (314 << 10) | | ||
251 | (32 << 0)); | ||
252 | mixer_reg_write(res, MXR_CM_COEFF_CB, | ||
253 | (972 << 20) | (851 << 10) | (225 << 0)); | ||
254 | mixer_reg_write(res, MXR_CM_COEFF_CR, | ||
255 | (225 << 20) | (820 << 10) | (1004 << 0)); | ||
256 | } else if (height == 1080) { | ||
257 | val = MXR_CFG_RGB709_16_235; | ||
258 | mixer_reg_write(res, MXR_CM_COEFF_Y, | ||
259 | (1 << 30) | (94 << 20) | (314 << 10) | | ||
260 | (32 << 0)); | ||
261 | mixer_reg_write(res, MXR_CM_COEFF_CB, | ||
262 | (972 << 20) | (851 << 10) | (225 << 0)); | ||
263 | mixer_reg_write(res, MXR_CM_COEFF_CR, | ||
264 | (225 << 20) | (820 << 10) | (1004 << 0)); | ||
265 | } else { | ||
266 | val = MXR_CFG_RGB709_16_235; | ||
267 | mixer_reg_write(res, MXR_CM_COEFF_Y, | ||
268 | (1 << 30) | (94 << 20) | (314 << 10) | | ||
269 | (32 << 0)); | ||
270 | mixer_reg_write(res, MXR_CM_COEFF_CB, | ||
271 | (972 << 20) | (851 << 10) | (225 << 0)); | ||
272 | mixer_reg_write(res, MXR_CM_COEFF_CR, | ||
273 | (225 << 20) | (820 << 10) | (1004 << 0)); | ||
274 | } | ||
275 | |||
276 | mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK); | ||
277 | } | ||
278 | |||
279 | static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable) | ||
280 | { | ||
281 | struct mixer_resources *res = &ctx->mixer_res; | ||
282 | u32 val = enable ? ~0 : 0; | ||
283 | |||
284 | switch (win) { | ||
285 | case 0: | ||
286 | mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE); | ||
287 | break; | ||
288 | case 1: | ||
289 | mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE); | ||
290 | break; | ||
291 | case 2: | ||
292 | vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON); | ||
293 | mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_VP_ENABLE); | ||
294 | break; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | static void mixer_run(struct mixer_context *ctx) | ||
299 | { | ||
300 | struct mixer_resources *res = &ctx->mixer_res; | ||
301 | |||
302 | mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); | ||
303 | |||
304 | mixer_regs_dump(ctx); | ||
305 | } | ||
306 | |||
307 | static void vp_video_buffer(struct mixer_context *ctx, int win) | ||
308 | { | ||
309 | struct mixer_resources *res = &ctx->mixer_res; | ||
310 | unsigned long flags; | ||
311 | struct hdmi_win_data *win_data; | ||
312 | unsigned int full_width, full_height, width, height; | ||
313 | unsigned int x_ratio, y_ratio; | ||
314 | unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset; | ||
315 | unsigned int mode_width, mode_height; | ||
316 | unsigned int buf_num; | ||
317 | dma_addr_t luma_addr[2], chroma_addr[2]; | ||
318 | bool tiled_mode = false; | ||
319 | bool crcb_mode = false; | ||
320 | u32 val; | ||
321 | |||
322 | win_data = &ctx->win_data[win]; | ||
323 | |||
324 | switch (win_data->pixel_format) { | ||
325 | case DRM_FORMAT_NV12MT: | ||
326 | tiled_mode = true; | ||
327 | case DRM_FORMAT_NV12M: | ||
328 | crcb_mode = false; | ||
329 | buf_num = 2; | ||
330 | break; | ||
331 | /* TODO: single buffer format NV12, NV21 */ | ||
332 | default: | ||
333 | /* ignore pixel format at disable time */ | ||
334 | if (!win_data->dma_addr) | ||
335 | break; | ||
336 | |||
337 | DRM_ERROR("pixel format for vp is wrong [%d].\n", | ||
338 | win_data->pixel_format); | ||
339 | return; | ||
340 | } | ||
341 | |||
342 | full_width = win_data->fb_width; | ||
343 | full_height = win_data->fb_height; | ||
344 | width = win_data->crtc_width; | ||
345 | height = win_data->crtc_height; | ||
346 | mode_width = win_data->mode_width; | ||
347 | mode_height = win_data->mode_height; | ||
348 | |||
349 | /* scaling feature: (src << 16) / dst */ | ||
350 | x_ratio = (width << 16) / width; | ||
351 | y_ratio = (height << 16) / height; | ||
352 | |||
353 | src_x_offset = win_data->fb_x; | ||
354 | src_y_offset = win_data->fb_y; | ||
355 | dst_x_offset = win_data->crtc_x; | ||
356 | dst_y_offset = win_data->crtc_y; | ||
357 | |||
358 | if (buf_num == 2) { | ||
359 | luma_addr[0] = win_data->dma_addr; | ||
360 | chroma_addr[0] = win_data->chroma_dma_addr; | ||
361 | } else { | ||
362 | luma_addr[0] = win_data->dma_addr; | ||
363 | chroma_addr[0] = win_data->dma_addr | ||
364 | + (full_width * full_height); | ||
365 | } | ||
366 | |||
367 | if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) { | ||
368 | ctx->interlace = true; | ||
369 | if (tiled_mode) { | ||
370 | luma_addr[1] = luma_addr[0] + 0x40; | ||
371 | chroma_addr[1] = chroma_addr[0] + 0x40; | ||
372 | } else { | ||
373 | luma_addr[1] = luma_addr[0] + full_width; | ||
374 | chroma_addr[1] = chroma_addr[0] + full_width; | ||
375 | } | ||
376 | } else { | ||
377 | ctx->interlace = false; | ||
378 | luma_addr[1] = 0; | ||
379 | chroma_addr[1] = 0; | ||
380 | } | ||
381 | |||
382 | spin_lock_irqsave(&res->reg_slock, flags); | ||
383 | mixer_vsync_set_update(ctx, false); | ||
384 | |||
385 | /* interlace or progressive scan mode */ | ||
386 | val = (ctx->interlace ? ~0 : 0); | ||
387 | vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP); | ||
388 | |||
389 | /* setup format */ | ||
390 | val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12); | ||
391 | val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR); | ||
392 | vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK); | ||
393 | |||
394 | /* setting size of input image */ | ||
395 | vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(full_width) | | ||
396 | VP_IMG_VSIZE(full_height)); | ||
397 | /* chroma height has to reduced by 2 to avoid chroma distorions */ | ||
398 | vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(full_width) | | ||
399 | VP_IMG_VSIZE(full_height / 2)); | ||
400 | |||
401 | vp_reg_write(res, VP_SRC_WIDTH, width); | ||
402 | vp_reg_write(res, VP_SRC_HEIGHT, height); | ||
403 | vp_reg_write(res, VP_SRC_H_POSITION, | ||
404 | VP_SRC_H_POSITION_VAL(src_x_offset)); | ||
405 | vp_reg_write(res, VP_SRC_V_POSITION, src_y_offset); | ||
406 | |||
407 | vp_reg_write(res, VP_DST_WIDTH, width); | ||
408 | vp_reg_write(res, VP_DST_H_POSITION, dst_x_offset); | ||
409 | if (ctx->interlace) { | ||
410 | vp_reg_write(res, VP_DST_HEIGHT, height / 2); | ||
411 | vp_reg_write(res, VP_DST_V_POSITION, dst_y_offset / 2); | ||
412 | } else { | ||
413 | vp_reg_write(res, VP_DST_HEIGHT, height); | ||
414 | vp_reg_write(res, VP_DST_V_POSITION, dst_y_offset); | ||
415 | } | ||
416 | |||
417 | vp_reg_write(res, VP_H_RATIO, x_ratio); | ||
418 | vp_reg_write(res, VP_V_RATIO, y_ratio); | ||
419 | |||
420 | vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); | ||
421 | |||
422 | /* set buffer address to vp */ | ||
423 | vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]); | ||
424 | vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]); | ||
425 | vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]); | ||
426 | vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]); | ||
427 | |||
428 | mixer_cfg_scan(ctx, mode_height); | ||
429 | mixer_cfg_rgb_fmt(ctx, mode_height); | ||
430 | mixer_cfg_layer(ctx, win, true); | ||
431 | mixer_run(ctx); | ||
432 | |||
433 | mixer_vsync_set_update(ctx, true); | ||
434 | spin_unlock_irqrestore(&res->reg_slock, flags); | ||
435 | |||
436 | vp_regs_dump(ctx); | ||
437 | } | ||
438 | |||
439 | static void mixer_graph_buffer(struct mixer_context *ctx, int win) | ||
440 | { | ||
441 | struct mixer_resources *res = &ctx->mixer_res; | ||
442 | unsigned long flags; | ||
443 | struct hdmi_win_data *win_data; | ||
444 | unsigned int full_width, width, height; | ||
445 | unsigned int x_ratio, y_ratio; | ||
446 | unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset; | ||
447 | unsigned int mode_width, mode_height; | ||
448 | dma_addr_t dma_addr; | ||
449 | unsigned int fmt; | ||
450 | u32 val; | ||
451 | |||
452 | win_data = &ctx->win_data[win]; | ||
453 | |||
454 | #define RGB565 4 | ||
455 | #define ARGB1555 5 | ||
456 | #define ARGB4444 6 | ||
457 | #define ARGB8888 7 | ||
458 | |||
459 | switch (win_data->bpp) { | ||
460 | case 16: | ||
461 | fmt = ARGB4444; | ||
462 | break; | ||
463 | case 32: | ||
464 | fmt = ARGB8888; | ||
465 | break; | ||
466 | default: | ||
467 | fmt = ARGB8888; | ||
468 | } | ||
469 | |||
470 | dma_addr = win_data->dma_addr; | ||
471 | full_width = win_data->fb_width; | ||
472 | width = win_data->crtc_width; | ||
473 | height = win_data->crtc_height; | ||
474 | mode_width = win_data->mode_width; | ||
475 | mode_height = win_data->mode_height; | ||
476 | |||
477 | /* 2x scaling feature */ | ||
478 | x_ratio = 0; | ||
479 | y_ratio = 0; | ||
480 | |||
481 | src_x_offset = win_data->fb_x; | ||
482 | src_y_offset = win_data->fb_y; | ||
483 | dst_x_offset = win_data->crtc_x; | ||
484 | dst_y_offset = win_data->crtc_y; | ||
485 | |||
486 | /* converting dma address base and source offset */ | ||
487 | dma_addr = dma_addr | ||
488 | + (src_x_offset * win_data->bpp >> 3) | ||
489 | + (src_y_offset * full_width * win_data->bpp >> 3); | ||
490 | src_x_offset = 0; | ||
491 | src_y_offset = 0; | ||
492 | |||
493 | if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) | ||
494 | ctx->interlace = true; | ||
495 | else | ||
496 | ctx->interlace = false; | ||
497 | |||
498 | spin_lock_irqsave(&res->reg_slock, flags); | ||
499 | mixer_vsync_set_update(ctx, false); | ||
500 | |||
501 | /* setup format */ | ||
502 | mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win), | ||
503 | MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK); | ||
504 | |||
505 | /* setup geometry */ | ||
506 | mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), full_width); | ||
507 | |||
508 | val = MXR_GRP_WH_WIDTH(width); | ||
509 | val |= MXR_GRP_WH_HEIGHT(height); | ||
510 | val |= MXR_GRP_WH_H_SCALE(x_ratio); | ||
511 | val |= MXR_GRP_WH_V_SCALE(y_ratio); | ||
512 | mixer_reg_write(res, MXR_GRAPHIC_WH(win), val); | ||
513 | |||
514 | /* setup offsets in source image */ | ||
515 | val = MXR_GRP_SXY_SX(src_x_offset); | ||
516 | val |= MXR_GRP_SXY_SY(src_y_offset); | ||
517 | mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val); | ||
518 | |||
519 | /* setup offsets in display image */ | ||
520 | val = MXR_GRP_DXY_DX(dst_x_offset); | ||
521 | val |= MXR_GRP_DXY_DY(dst_y_offset); | ||
522 | mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val); | ||
523 | |||
524 | /* set buffer address to mixer */ | ||
525 | mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr); | ||
526 | |||
527 | mixer_cfg_scan(ctx, mode_height); | ||
528 | mixer_cfg_rgb_fmt(ctx, mode_height); | ||
529 | mixer_cfg_layer(ctx, win, true); | ||
530 | mixer_run(ctx); | ||
531 | |||
532 | mixer_vsync_set_update(ctx, true); | ||
533 | spin_unlock_irqrestore(&res->reg_slock, flags); | ||
534 | } | ||
535 | |||
536 | static void vp_win_reset(struct mixer_context *ctx) | ||
537 | { | ||
538 | struct mixer_resources *res = &ctx->mixer_res; | ||
539 | int tries = 100; | ||
540 | |||
541 | vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING); | ||
542 | for (tries = 100; tries; --tries) { | ||
543 | /* waiting until VP_SRESET_PROCESSING is 0 */ | ||
544 | if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING) | ||
545 | break; | ||
546 | mdelay(10); | ||
547 | } | ||
548 | WARN(tries == 0, "failed to reset Video Processor\n"); | ||
549 | } | ||
550 | |||
551 | static int mixer_enable_vblank(void *ctx, int pipe) | ||
552 | { | ||
553 | struct mixer_context *mixer_ctx = ctx; | ||
554 | struct mixer_resources *res = &mixer_ctx->mixer_res; | ||
555 | |||
556 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
557 | |||
558 | mixer_ctx->pipe = pipe; | ||
559 | |||
560 | /* enable vsync interrupt */ | ||
561 | mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC, | ||
562 | MXR_INT_EN_VSYNC); | ||
563 | |||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static void mixer_disable_vblank(void *ctx) | ||
568 | { | ||
569 | struct mixer_context *mixer_ctx = ctx; | ||
570 | struct mixer_resources *res = &mixer_ctx->mixer_res; | ||
571 | |||
572 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
573 | |||
574 | /* disable vsync interrupt */ | ||
575 | mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); | ||
576 | } | ||
577 | |||
578 | static void mixer_win_mode_set(void *ctx, | ||
579 | struct exynos_drm_overlay *overlay) | ||
580 | { | ||
581 | struct mixer_context *mixer_ctx = ctx; | ||
582 | struct hdmi_win_data *win_data; | ||
583 | int win; | ||
584 | |||
585 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
586 | |||
587 | if (!overlay) { | ||
588 | DRM_ERROR("overlay is NULL\n"); | ||
589 | return; | ||
590 | } | ||
591 | |||
592 | DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n", | ||
593 | overlay->fb_width, overlay->fb_height, | ||
594 | overlay->fb_x, overlay->fb_y, | ||
595 | overlay->crtc_width, overlay->crtc_height, | ||
596 | overlay->crtc_x, overlay->crtc_y); | ||
597 | |||
598 | win = overlay->zpos; | ||
599 | if (win == DEFAULT_ZPOS) | ||
600 | win = mixer_ctx->default_win; | ||
601 | |||
602 | if (win < 0 || win > HDMI_OVERLAY_NUMBER) { | ||
603 | DRM_ERROR("overlay plane[%d] is wrong\n", win); | ||
604 | return; | ||
605 | } | ||
606 | |||
607 | win_data = &mixer_ctx->win_data[win]; | ||
608 | |||
609 | win_data->dma_addr = overlay->dma_addr[0]; | ||
610 | win_data->vaddr = overlay->vaddr[0]; | ||
611 | win_data->chroma_dma_addr = overlay->dma_addr[1]; | ||
612 | win_data->chroma_vaddr = overlay->vaddr[1]; | ||
613 | win_data->pixel_format = overlay->pixel_format; | ||
614 | win_data->bpp = overlay->bpp; | ||
615 | |||
616 | win_data->crtc_x = overlay->crtc_x; | ||
617 | win_data->crtc_y = overlay->crtc_y; | ||
618 | win_data->crtc_width = overlay->crtc_width; | ||
619 | win_data->crtc_height = overlay->crtc_height; | ||
620 | |||
621 | win_data->fb_x = overlay->fb_x; | ||
622 | win_data->fb_y = overlay->fb_y; | ||
623 | win_data->fb_width = overlay->fb_width; | ||
624 | win_data->fb_height = overlay->fb_height; | ||
625 | |||
626 | win_data->mode_width = overlay->mode_width; | ||
627 | win_data->mode_height = overlay->mode_height; | ||
628 | |||
629 | win_data->scan_flags = overlay->scan_flag; | ||
630 | } | ||
631 | |||
632 | static void mixer_win_commit(void *ctx, int zpos) | ||
633 | { | ||
634 | struct mixer_context *mixer_ctx = ctx; | ||
635 | int win = zpos; | ||
636 | |||
637 | DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); | ||
638 | |||
639 | if (win == DEFAULT_ZPOS) | ||
640 | win = mixer_ctx->default_win; | ||
641 | |||
642 | if (win < 0 || win > HDMI_OVERLAY_NUMBER) { | ||
643 | DRM_ERROR("overlay plane[%d] is wrong\n", win); | ||
644 | return; | ||
645 | } | ||
646 | |||
647 | if (win > 1) | ||
648 | vp_video_buffer(mixer_ctx, win); | ||
649 | else | ||
650 | mixer_graph_buffer(mixer_ctx, win); | ||
651 | } | ||
652 | |||
653 | static void mixer_win_disable(void *ctx, int zpos) | ||
654 | { | ||
655 | struct mixer_context *mixer_ctx = ctx; | ||
656 | struct mixer_resources *res = &mixer_ctx->mixer_res; | ||
657 | unsigned long flags; | ||
658 | int win = zpos; | ||
659 | |||
660 | DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); | ||
661 | |||
662 | if (win == DEFAULT_ZPOS) | ||
663 | win = mixer_ctx->default_win; | ||
664 | |||
665 | if (win < 0 || win > HDMI_OVERLAY_NUMBER) { | ||
666 | DRM_ERROR("overlay plane[%d] is wrong\n", win); | ||
667 | return; | ||
668 | } | ||
669 | |||
670 | spin_lock_irqsave(&res->reg_slock, flags); | ||
671 | mixer_vsync_set_update(mixer_ctx, false); | ||
672 | |||
673 | mixer_cfg_layer(mixer_ctx, win, false); | ||
674 | |||
675 | mixer_vsync_set_update(mixer_ctx, true); | ||
676 | spin_unlock_irqrestore(&res->reg_slock, flags); | ||
677 | } | ||
678 | |||
679 | static struct exynos_hdmi_overlay_ops overlay_ops = { | ||
680 | .enable_vblank = mixer_enable_vblank, | ||
681 | .disable_vblank = mixer_disable_vblank, | ||
682 | .win_mode_set = mixer_win_mode_set, | ||
683 | .win_commit = mixer_win_commit, | ||
684 | .win_disable = mixer_win_disable, | ||
685 | }; | ||
686 | |||
687 | /* for pageflip event */ | ||
688 | static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc) | ||
689 | { | ||
690 | struct exynos_drm_private *dev_priv = drm_dev->dev_private; | ||
691 | struct drm_pending_vblank_event *e, *t; | ||
692 | struct timeval now; | ||
693 | unsigned long flags; | ||
694 | bool is_checked = false; | ||
695 | |||
696 | spin_lock_irqsave(&drm_dev->event_lock, flags); | ||
697 | |||
698 | list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, | ||
699 | base.link) { | ||
700 | /* if event's pipe isn't same as crtc then ignore it. */ | ||
701 | if (crtc != e->pipe) | ||
702 | continue; | ||
703 | |||
704 | is_checked = true; | ||
705 | do_gettimeofday(&now); | ||
706 | e->event.sequence = 0; | ||
707 | e->event.tv_sec = now.tv_sec; | ||
708 | e->event.tv_usec = now.tv_usec; | ||
709 | |||
710 | list_move_tail(&e->base.link, &e->base.file_priv->event_list); | ||
711 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
712 | } | ||
713 | |||
714 | if (is_checked) | ||
715 | drm_vblank_put(drm_dev, crtc); | ||
716 | |||
717 | spin_unlock_irqrestore(&drm_dev->event_lock, flags); | ||
718 | } | ||
719 | |||
720 | static irqreturn_t mixer_irq_handler(int irq, void *arg) | ||
721 | { | ||
722 | struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg; | ||
723 | struct mixer_context *ctx = | ||
724 | (struct mixer_context *)drm_hdmi_ctx->ctx; | ||
725 | struct mixer_resources *res = &ctx->mixer_res; | ||
726 | u32 val, val_base; | ||
727 | |||
728 | spin_lock(&res->reg_slock); | ||
729 | |||
730 | /* read interrupt status for handling and clearing flags for VSYNC */ | ||
731 | val = mixer_reg_read(res, MXR_INT_STATUS); | ||
732 | |||
733 | /* handling VSYNC */ | ||
734 | if (val & MXR_INT_STATUS_VSYNC) { | ||
735 | /* interlace scan need to check shadow register */ | ||
736 | if (ctx->interlace) { | ||
737 | val_base = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0)); | ||
738 | if (ctx->win_data[0].dma_addr != val_base) | ||
739 | goto out; | ||
740 | |||
741 | val_base = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1)); | ||
742 | if (ctx->win_data[1].dma_addr != val_base) | ||
743 | goto out; | ||
744 | } | ||
745 | |||
746 | drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe); | ||
747 | mixer_finish_pageflip(drm_hdmi_ctx->drm_dev, ctx->pipe); | ||
748 | } | ||
749 | |||
750 | out: | ||
751 | /* clear interrupts */ | ||
752 | if (~val & MXR_INT_EN_VSYNC) { | ||
753 | /* vsync interrupt use different bit for read and clear */ | ||
754 | val &= ~MXR_INT_EN_VSYNC; | ||
755 | val |= MXR_INT_CLEAR_VSYNC; | ||
756 | } | ||
757 | mixer_reg_write(res, MXR_INT_STATUS, val); | ||
758 | |||
759 | spin_unlock(&res->reg_slock); | ||
760 | |||
761 | return IRQ_HANDLED; | ||
762 | } | ||
763 | |||
764 | static void mixer_win_reset(struct mixer_context *ctx) | ||
765 | { | ||
766 | struct mixer_resources *res = &ctx->mixer_res; | ||
767 | unsigned long flags; | ||
768 | u32 val; /* value stored to register */ | ||
769 | |||
770 | spin_lock_irqsave(&res->reg_slock, flags); | ||
771 | mixer_vsync_set_update(ctx, false); | ||
772 | |||
773 | mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK); | ||
774 | |||
775 | /* set output in RGB888 mode */ | ||
776 | mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK); | ||
777 | |||
778 | /* 16 beat burst in DMA */ | ||
779 | mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST, | ||
780 | MXR_STATUS_BURST_MASK); | ||
781 | |||
782 | /* setting default layer priority: layer1 > video > layer0 | ||
783 | * because typical usage scenario would be | ||
784 | * layer0 - framebuffer | ||
785 | * video - video overlay | ||
786 | * layer1 - OSD | ||
787 | */ | ||
788 | val = MXR_LAYER_CFG_GRP0_VAL(1); | ||
789 | val |= MXR_LAYER_CFG_VP_VAL(2); | ||
790 | val |= MXR_LAYER_CFG_GRP1_VAL(3); | ||
791 | mixer_reg_write(res, MXR_LAYER_CFG, val); | ||
792 | |||
793 | /* setting background color */ | ||
794 | mixer_reg_write(res, MXR_BG_COLOR0, 0x008080); | ||
795 | mixer_reg_write(res, MXR_BG_COLOR1, 0x008080); | ||
796 | mixer_reg_write(res, MXR_BG_COLOR2, 0x008080); | ||
797 | |||
798 | /* setting graphical layers */ | ||
799 | |||
800 | val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ | ||
801 | val |= MXR_GRP_CFG_WIN_BLEND_EN; | ||
802 | val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ | ||
803 | |||
804 | /* the same configuration for both layers */ | ||
805 | mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val); | ||
806 | |||
807 | val |= MXR_GRP_CFG_BLEND_PRE_MUL; | ||
808 | val |= MXR_GRP_CFG_PIXEL_BLEND_EN; | ||
809 | mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val); | ||
810 | |||
811 | /* configuration of Video Processor Registers */ | ||
812 | vp_win_reset(ctx); | ||
813 | vp_default_filter(res); | ||
814 | |||
815 | /* disable all layers */ | ||
816 | mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE); | ||
817 | mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE); | ||
818 | mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE); | ||
819 | |||
820 | mixer_vsync_set_update(ctx, true); | ||
821 | spin_unlock_irqrestore(&res->reg_slock, flags); | ||
822 | } | ||
823 | |||
824 | static void mixer_resource_poweron(struct mixer_context *ctx) | ||
825 | { | ||
826 | struct mixer_resources *res = &ctx->mixer_res; | ||
827 | |||
828 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
829 | |||
830 | clk_enable(res->mixer); | ||
831 | clk_enable(res->vp); | ||
832 | clk_enable(res->sclk_mixer); | ||
833 | |||
834 | mixer_win_reset(ctx); | ||
835 | } | ||
836 | |||
837 | static void mixer_resource_poweroff(struct mixer_context *ctx) | ||
838 | { | ||
839 | struct mixer_resources *res = &ctx->mixer_res; | ||
840 | |||
841 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
842 | |||
843 | clk_disable(res->mixer); | ||
844 | clk_disable(res->vp); | ||
845 | clk_disable(res->sclk_mixer); | ||
846 | } | ||
847 | |||
848 | static int mixer_runtime_resume(struct device *dev) | ||
849 | { | ||
850 | struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev); | ||
851 | |||
852 | DRM_DEBUG_KMS("resume - start\n"); | ||
853 | |||
854 | mixer_resource_poweron((struct mixer_context *)ctx->ctx); | ||
855 | |||
856 | return 0; | ||
857 | } | ||
858 | |||
859 | static int mixer_runtime_suspend(struct device *dev) | ||
860 | { | ||
861 | struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev); | ||
862 | |||
863 | DRM_DEBUG_KMS("suspend - start\n"); | ||
864 | |||
865 | mixer_resource_poweroff((struct mixer_context *)ctx->ctx); | ||
866 | |||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | static const struct dev_pm_ops mixer_pm_ops = { | ||
871 | .runtime_suspend = mixer_runtime_suspend, | ||
872 | .runtime_resume = mixer_runtime_resume, | ||
873 | }; | ||
874 | |||
875 | static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, | ||
876 | struct platform_device *pdev) | ||
877 | { | ||
878 | struct mixer_context *mixer_ctx = | ||
879 | (struct mixer_context *)ctx->ctx; | ||
880 | struct device *dev = &pdev->dev; | ||
881 | struct mixer_resources *mixer_res = &mixer_ctx->mixer_res; | ||
882 | struct resource *res; | ||
883 | int ret; | ||
884 | |||
885 | mixer_res->dev = dev; | ||
886 | spin_lock_init(&mixer_res->reg_slock); | ||
887 | |||
888 | mixer_res->mixer = clk_get(dev, "mixer"); | ||
889 | if (IS_ERR_OR_NULL(mixer_res->mixer)) { | ||
890 | dev_err(dev, "failed to get clock 'mixer'\n"); | ||
891 | ret = -ENODEV; | ||
892 | goto fail; | ||
893 | } | ||
894 | mixer_res->vp = clk_get(dev, "vp"); | ||
895 | if (IS_ERR_OR_NULL(mixer_res->vp)) { | ||
896 | dev_err(dev, "failed to get clock 'vp'\n"); | ||
897 | ret = -ENODEV; | ||
898 | goto fail; | ||
899 | } | ||
900 | mixer_res->sclk_mixer = clk_get(dev, "sclk_mixer"); | ||
901 | if (IS_ERR_OR_NULL(mixer_res->sclk_mixer)) { | ||
902 | dev_err(dev, "failed to get clock 'sclk_mixer'\n"); | ||
903 | ret = -ENODEV; | ||
904 | goto fail; | ||
905 | } | ||
906 | mixer_res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); | ||
907 | if (IS_ERR_OR_NULL(mixer_res->sclk_hdmi)) { | ||
908 | dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); | ||
909 | ret = -ENODEV; | ||
910 | goto fail; | ||
911 | } | ||
912 | mixer_res->sclk_dac = clk_get(dev, "sclk_dac"); | ||
913 | if (IS_ERR_OR_NULL(mixer_res->sclk_dac)) { | ||
914 | dev_err(dev, "failed to get clock 'sclk_dac'\n"); | ||
915 | ret = -ENODEV; | ||
916 | goto fail; | ||
917 | } | ||
918 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr"); | ||
919 | if (res == NULL) { | ||
920 | dev_err(dev, "get memory resource failed.\n"); | ||
921 | ret = -ENXIO; | ||
922 | goto fail; | ||
923 | } | ||
924 | |||
925 | clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); | ||
926 | |||
927 | mixer_res->mixer_regs = ioremap(res->start, resource_size(res)); | ||
928 | if (mixer_res->mixer_regs == NULL) { | ||
929 | dev_err(dev, "register mapping failed.\n"); | ||
930 | ret = -ENXIO; | ||
931 | goto fail; | ||
932 | } | ||
933 | |||
934 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp"); | ||
935 | if (res == NULL) { | ||
936 | dev_err(dev, "get memory resource failed.\n"); | ||
937 | ret = -ENXIO; | ||
938 | goto fail_mixer_regs; | ||
939 | } | ||
940 | |||
941 | mixer_res->vp_regs = ioremap(res->start, resource_size(res)); | ||
942 | if (mixer_res->vp_regs == NULL) { | ||
943 | dev_err(dev, "register mapping failed.\n"); | ||
944 | ret = -ENXIO; | ||
945 | goto fail_mixer_regs; | ||
946 | } | ||
947 | |||
948 | res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); | ||
949 | if (res == NULL) { | ||
950 | dev_err(dev, "get interrupt resource failed.\n"); | ||
951 | ret = -ENXIO; | ||
952 | goto fail_vp_regs; | ||
953 | } | ||
954 | |||
955 | ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx); | ||
956 | if (ret) { | ||
957 | dev_err(dev, "request interrupt failed.\n"); | ||
958 | goto fail_vp_regs; | ||
959 | } | ||
960 | mixer_res->irq = res->start; | ||
961 | |||
962 | return 0; | ||
963 | |||
964 | fail_vp_regs: | ||
965 | iounmap(mixer_res->vp_regs); | ||
966 | |||
967 | fail_mixer_regs: | ||
968 | iounmap(mixer_res->mixer_regs); | ||
969 | |||
970 | fail: | ||
971 | if (!IS_ERR_OR_NULL(mixer_res->sclk_dac)) | ||
972 | clk_put(mixer_res->sclk_dac); | ||
973 | if (!IS_ERR_OR_NULL(mixer_res->sclk_hdmi)) | ||
974 | clk_put(mixer_res->sclk_hdmi); | ||
975 | if (!IS_ERR_OR_NULL(mixer_res->sclk_mixer)) | ||
976 | clk_put(mixer_res->sclk_mixer); | ||
977 | if (!IS_ERR_OR_NULL(mixer_res->vp)) | ||
978 | clk_put(mixer_res->vp); | ||
979 | if (!IS_ERR_OR_NULL(mixer_res->mixer)) | ||
980 | clk_put(mixer_res->mixer); | ||
981 | mixer_res->dev = NULL; | ||
982 | return ret; | ||
983 | } | ||
984 | |||
985 | static void mixer_resources_cleanup(struct mixer_context *ctx) | ||
986 | { | ||
987 | struct mixer_resources *res = &ctx->mixer_res; | ||
988 | |||
989 | disable_irq(res->irq); | ||
990 | free_irq(res->irq, ctx); | ||
991 | |||
992 | iounmap(res->vp_regs); | ||
993 | iounmap(res->mixer_regs); | ||
994 | } | ||
995 | |||
996 | static int __devinit mixer_probe(struct platform_device *pdev) | ||
997 | { | ||
998 | struct device *dev = &pdev->dev; | ||
999 | struct exynos_drm_hdmi_context *drm_hdmi_ctx; | ||
1000 | struct mixer_context *ctx; | ||
1001 | int ret; | ||
1002 | |||
1003 | dev_info(dev, "probe start\n"); | ||
1004 | |||
1005 | drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); | ||
1006 | if (!drm_hdmi_ctx) { | ||
1007 | DRM_ERROR("failed to allocate common hdmi context.\n"); | ||
1008 | return -ENOMEM; | ||
1009 | } | ||
1010 | |||
1011 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
1012 | if (!ctx) { | ||
1013 | DRM_ERROR("failed to alloc mixer context.\n"); | ||
1014 | kfree(drm_hdmi_ctx); | ||
1015 | return -ENOMEM; | ||
1016 | } | ||
1017 | |||
1018 | drm_hdmi_ctx->ctx = (void *)ctx; | ||
1019 | |||
1020 | platform_set_drvdata(pdev, drm_hdmi_ctx); | ||
1021 | |||
1022 | /* acquire resources: regs, irqs, clocks */ | ||
1023 | ret = mixer_resources_init(drm_hdmi_ctx, pdev); | ||
1024 | if (ret) | ||
1025 | goto fail; | ||
1026 | |||
1027 | /* register specific callback point to common hdmi. */ | ||
1028 | exynos_drm_overlay_ops_register(&overlay_ops); | ||
1029 | |||
1030 | mixer_resource_poweron(ctx); | ||
1031 | |||
1032 | return 0; | ||
1033 | |||
1034 | |||
1035 | fail: | ||
1036 | dev_info(dev, "probe failed\n"); | ||
1037 | return ret; | ||
1038 | } | ||
1039 | |||
1040 | static int mixer_remove(struct platform_device *pdev) | ||
1041 | { | ||
1042 | struct device *dev = &pdev->dev; | ||
1043 | struct exynos_drm_hdmi_context *drm_hdmi_ctx = | ||
1044 | platform_get_drvdata(pdev); | ||
1045 | struct mixer_context *ctx = (struct mixer_context *)drm_hdmi_ctx->ctx; | ||
1046 | |||
1047 | dev_info(dev, "remove sucessful\n"); | ||
1048 | |||
1049 | mixer_resource_poweroff(ctx); | ||
1050 | mixer_resources_cleanup(ctx); | ||
1051 | |||
1052 | return 0; | ||
1053 | } | ||
1054 | |||
1055 | struct platform_driver mixer_driver = { | ||
1056 | .driver = { | ||
1057 | .name = "s5p-mixer", | ||
1058 | .owner = THIS_MODULE, | ||
1059 | .pm = &mixer_pm_ops, | ||
1060 | }, | ||
1061 | .probe = mixer_probe, | ||
1062 | .remove = __devexit_p(mixer_remove), | ||
1063 | }; | ||
1064 | EXPORT_SYMBOL(mixer_driver); | ||
1065 | |||
1066 | MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); | ||
1067 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
1068 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
1069 | MODULE_DESCRIPTION("Samsung DRM HDMI mixer Driver"); | ||
1070 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.h b/drivers/gpu/drm/exynos/exynos_mixer.h new file mode 100644 index 000000000000..cebacfefc077 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_mixer.h | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * Authors: | ||
5 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
6 | * Inki Dae <inki.dae@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_MIXER_H_ | ||
29 | #define _EXYNOS_MIXER_H_ | ||
30 | |||
31 | #define HDMI_OVERLAY_NUMBER 3 | ||
32 | |||
33 | struct hdmi_win_data { | ||
34 | dma_addr_t dma_addr; | ||
35 | void __iomem *vaddr; | ||
36 | dma_addr_t chroma_dma_addr; | ||
37 | void __iomem *chroma_vaddr; | ||
38 | uint32_t pixel_format; | ||
39 | unsigned int bpp; | ||
40 | unsigned int crtc_x; | ||
41 | unsigned int crtc_y; | ||
42 | unsigned int crtc_width; | ||
43 | unsigned int crtc_height; | ||
44 | unsigned int fb_x; | ||
45 | unsigned int fb_y; | ||
46 | unsigned int fb_width; | ||
47 | unsigned int fb_height; | ||
48 | unsigned int mode_width; | ||
49 | unsigned int mode_height; | ||
50 | unsigned int scan_flags; | ||
51 | }; | ||
52 | |||
53 | struct mixer_resources { | ||
54 | struct device *dev; | ||
55 | /** interrupt index */ | ||
56 | int irq; | ||
57 | /** pointer to Mixer registers */ | ||
58 | void __iomem *mixer_regs; | ||
59 | /** pointer to Video Processor registers */ | ||
60 | void __iomem *vp_regs; | ||
61 | /** spinlock for protection of registers */ | ||
62 | spinlock_t reg_slock; | ||
63 | /** other resources */ | ||
64 | struct clk *mixer; | ||
65 | struct clk *vp; | ||
66 | struct clk *sclk_mixer; | ||
67 | struct clk *sclk_hdmi; | ||
68 | struct clk *sclk_dac; | ||
69 | }; | ||
70 | |||
71 | struct mixer_context { | ||
72 | unsigned int default_win; | ||
73 | struct fb_videomode *default_timing; | ||
74 | unsigned int default_bpp; | ||
75 | |||
76 | /** mixer interrupt */ | ||
77 | unsigned int irq; | ||
78 | /** current crtc pipe for vblank */ | ||
79 | int pipe; | ||
80 | /** interlace scan mode */ | ||
81 | bool interlace; | ||
82 | /** vp enabled status */ | ||
83 | bool vp_enabled; | ||
84 | |||
85 | /** mixer and vp resources */ | ||
86 | struct mixer_resources mixer_res; | ||
87 | |||
88 | /** overlay window data */ | ||
89 | struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER]; | ||
90 | }; | ||
91 | |||
92 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h new file mode 100644 index 000000000000..72e6b52be740 --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-hdmi.h | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Cloned from drivers/media/video/s5p-tv/regs-hdmi.h | ||
4 | * | ||
5 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
6 | * http://www.samsung.com/ | ||
7 | * | ||
8 | * HDMI register header file for Samsung TVOUT driver | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef SAMSUNG_REGS_HDMI_H | ||
16 | #define SAMSUNG_REGS_HDMI_H | ||
17 | |||
18 | /* | ||
19 | * Register part | ||
20 | */ | ||
21 | |||
22 | #define HDMI_CTRL_BASE(x) ((x) + 0x00000000) | ||
23 | #define HDMI_CORE_BASE(x) ((x) + 0x00010000) | ||
24 | #define HDMI_TG_BASE(x) ((x) + 0x00050000) | ||
25 | |||
26 | /* Control registers */ | ||
27 | #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) | ||
28 | #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) | ||
29 | #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) | ||
30 | #define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) | ||
31 | #define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018) | ||
32 | #define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C) | ||
33 | #define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) | ||
34 | |||
35 | /* Core registers */ | ||
36 | #define HDMI_CON_0 HDMI_CORE_BASE(0x0000) | ||
37 | #define HDMI_CON_1 HDMI_CORE_BASE(0x0004) | ||
38 | #define HDMI_CON_2 HDMI_CORE_BASE(0x0008) | ||
39 | #define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010) | ||
40 | #define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014) | ||
41 | #define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020) | ||
42 | #define HDMI_HPD HDMI_CORE_BASE(0x0030) | ||
43 | #define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040) | ||
44 | #define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) | ||
45 | #define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) | ||
46 | #define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) | ||
47 | #define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0) | ||
48 | #define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4) | ||
49 | #define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0) | ||
50 | #define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4) | ||
51 | #define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8) | ||
52 | #define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) | ||
53 | #define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) | ||
54 | #define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) | ||
55 | #define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4) | ||
56 | #define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8) | ||
57 | #define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) | ||
58 | #define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) | ||
59 | #define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) | ||
60 | #define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) | ||
61 | #define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) | ||
62 | #define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) | ||
63 | #define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) | ||
64 | #define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) | ||
65 | #define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) | ||
66 | #define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) | ||
67 | #define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) | ||
68 | #define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) | ||
69 | #define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) | ||
70 | #define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) | ||
71 | #define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) | ||
72 | #define HDMI_ACR_CON HDMI_CORE_BASE(0x0180) | ||
73 | #define HDMI_AVI_CON HDMI_CORE_BASE(0x0300) | ||
74 | #define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) | ||
75 | #define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0) | ||
76 | #define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) | ||
77 | #define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) | ||
78 | #define HDMI_AUI_CON HDMI_CORE_BASE(0x0360) | ||
79 | #define HDMI_SPD_CON HDMI_CORE_BASE(0x0400) | ||
80 | |||
81 | /* Timing generator registers */ | ||
82 | #define HDMI_TG_CMD HDMI_TG_BASE(0x0000) | ||
83 | #define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018) | ||
84 | #define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C) | ||
85 | #define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020) | ||
86 | #define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024) | ||
87 | #define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028) | ||
88 | #define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C) | ||
89 | #define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030) | ||
90 | #define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034) | ||
91 | #define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038) | ||
92 | #define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C) | ||
93 | #define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040) | ||
94 | #define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044) | ||
95 | #define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048) | ||
96 | #define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C) | ||
97 | #define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050) | ||
98 | #define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054) | ||
99 | #define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058) | ||
100 | #define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C) | ||
101 | #define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060) | ||
102 | #define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064) | ||
103 | #define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078) | ||
104 | #define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C) | ||
105 | #define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080) | ||
106 | #define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084) | ||
107 | #define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088) | ||
108 | #define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C) | ||
109 | #define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090) | ||
110 | #define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094) | ||
111 | |||
112 | /* | ||
113 | * Bit definition part | ||
114 | */ | ||
115 | |||
116 | /* HDMI_INTC_CON */ | ||
117 | #define HDMI_INTC_EN_GLOBAL (1 << 6) | ||
118 | #define HDMI_INTC_EN_HPD_PLUG (1 << 3) | ||
119 | #define HDMI_INTC_EN_HPD_UNPLUG (1 << 2) | ||
120 | |||
121 | /* HDMI_INTC_FLAG */ | ||
122 | #define HDMI_INTC_FLAG_HPD_PLUG (1 << 3) | ||
123 | #define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2) | ||
124 | |||
125 | /* HDMI_PHY_RSTOUT */ | ||
126 | #define HDMI_PHY_SW_RSTOUT (1 << 0) | ||
127 | |||
128 | /* HDMI_CORE_RSTOUT */ | ||
129 | #define HDMI_CORE_SW_RSTOUT (1 << 0) | ||
130 | |||
131 | /* HDMI_CON_0 */ | ||
132 | #define HDMI_BLUE_SCR_EN (1 << 5) | ||
133 | #define HDMI_EN (1 << 0) | ||
134 | |||
135 | /* HDMI_PHY_STATUS */ | ||
136 | #define HDMI_PHY_STATUS_READY (1 << 0) | ||
137 | |||
138 | /* HDMI_MODE_SEL */ | ||
139 | #define HDMI_MODE_HDMI_EN (1 << 1) | ||
140 | #define HDMI_MODE_DVI_EN (1 << 0) | ||
141 | #define HDMI_MODE_MASK (3 << 0) | ||
142 | |||
143 | /* HDMI_TG_CMD */ | ||
144 | #define HDMI_TG_EN (1 << 0) | ||
145 | #define HDMI_FIELD_EN (1 << 1) | ||
146 | |||
147 | #endif /* SAMSUNG_REGS_HDMI_H */ | ||
diff --git a/drivers/gpu/drm/exynos/regs-mixer.h b/drivers/gpu/drm/exynos/regs-mixer.h new file mode 100644 index 000000000000..fd2f4d14cf6d --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-mixer.h | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Cloned from drivers/media/video/s5p-tv/regs-mixer.h | ||
4 | * | ||
5 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
6 | * http://www.samsung.com/ | ||
7 | * | ||
8 | * Mixer register header file for Samsung Mixer driver | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | #ifndef SAMSUNG_REGS_MIXER_H | ||
15 | #define SAMSUNG_REGS_MIXER_H | ||
16 | |||
17 | /* | ||
18 | * Register part | ||
19 | */ | ||
20 | #define MXR_STATUS 0x0000 | ||
21 | #define MXR_CFG 0x0004 | ||
22 | #define MXR_INT_EN 0x0008 | ||
23 | #define MXR_INT_STATUS 0x000C | ||
24 | #define MXR_LAYER_CFG 0x0010 | ||
25 | #define MXR_VIDEO_CFG 0x0014 | ||
26 | #define MXR_GRAPHIC0_CFG 0x0020 | ||
27 | #define MXR_GRAPHIC0_BASE 0x0024 | ||
28 | #define MXR_GRAPHIC0_SPAN 0x0028 | ||
29 | #define MXR_GRAPHIC0_SXY 0x002C | ||
30 | #define MXR_GRAPHIC0_WH 0x0030 | ||
31 | #define MXR_GRAPHIC0_DXY 0x0034 | ||
32 | #define MXR_GRAPHIC0_BLANK 0x0038 | ||
33 | #define MXR_GRAPHIC1_CFG 0x0040 | ||
34 | #define MXR_GRAPHIC1_BASE 0x0044 | ||
35 | #define MXR_GRAPHIC1_SPAN 0x0048 | ||
36 | #define MXR_GRAPHIC1_SXY 0x004C | ||
37 | #define MXR_GRAPHIC1_WH 0x0050 | ||
38 | #define MXR_GRAPHIC1_DXY 0x0054 | ||
39 | #define MXR_GRAPHIC1_BLANK 0x0058 | ||
40 | #define MXR_BG_CFG 0x0060 | ||
41 | #define MXR_BG_COLOR0 0x0064 | ||
42 | #define MXR_BG_COLOR1 0x0068 | ||
43 | #define MXR_BG_COLOR2 0x006C | ||
44 | #define MXR_CM_COEFF_Y 0x0080 | ||
45 | #define MXR_CM_COEFF_CB 0x0084 | ||
46 | #define MXR_CM_COEFF_CR 0x0088 | ||
47 | #define MXR_GRAPHIC0_BASE_S 0x2024 | ||
48 | #define MXR_GRAPHIC1_BASE_S 0x2044 | ||
49 | |||
50 | /* for parametrized access to layer registers */ | ||
51 | #define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20) | ||
52 | #define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20) | ||
53 | #define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20) | ||
54 | #define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20) | ||
55 | #define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20) | ||
56 | #define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20) | ||
57 | #define MXR_GRAPHIC_BLANK(i) (0x0038 + (i) * 0x20) | ||
58 | #define MXR_GRAPHIC_BASE_S(i) (0x2024 + (i) * 0x20) | ||
59 | |||
60 | /* | ||
61 | * Bit definition part | ||
62 | */ | ||
63 | |||
64 | /* generates mask for range of bits */ | ||
65 | #define MXR_MASK(high_bit, low_bit) \ | ||
66 | (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) | ||
67 | |||
68 | #define MXR_MASK_VAL(val, high_bit, low_bit) \ | ||
69 | (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit)) | ||
70 | |||
71 | /* bits for MXR_STATUS */ | ||
72 | #define MXR_STATUS_16_BURST (1 << 7) | ||
73 | #define MXR_STATUS_BURST_MASK (1 << 7) | ||
74 | #define MXR_STATUS_BIG_ENDIAN (1 << 3) | ||
75 | #define MXR_STATUS_ENDIAN_MASK (1 << 3) | ||
76 | #define MXR_STATUS_SYNC_ENABLE (1 << 2) | ||
77 | #define MXR_STATUS_REG_RUN (1 << 0) | ||
78 | |||
79 | /* bits for MXR_CFG */ | ||
80 | #define MXR_CFG_RGB601_0_255 (0 << 9) | ||
81 | #define MXR_CFG_RGB601_16_235 (1 << 9) | ||
82 | #define MXR_CFG_RGB709_0_255 (2 << 9) | ||
83 | #define MXR_CFG_RGB709_16_235 (3 << 9) | ||
84 | #define MXR_CFG_RGB_FMT_MASK 0x600 | ||
85 | #define MXR_CFG_OUT_YUV444 (0 << 8) | ||
86 | #define MXR_CFG_OUT_RGB888 (1 << 8) | ||
87 | #define MXR_CFG_OUT_MASK (1 << 8) | ||
88 | #define MXR_CFG_DST_SDO (0 << 7) | ||
89 | #define MXR_CFG_DST_HDMI (1 << 7) | ||
90 | #define MXR_CFG_DST_MASK (1 << 7) | ||
91 | #define MXR_CFG_SCAN_HD_720 (0 << 6) | ||
92 | #define MXR_CFG_SCAN_HD_1080 (1 << 6) | ||
93 | #define MXR_CFG_GRP1_ENABLE (1 << 5) | ||
94 | #define MXR_CFG_GRP0_ENABLE (1 << 4) | ||
95 | #define MXR_CFG_VP_ENABLE (1 << 3) | ||
96 | #define MXR_CFG_SCAN_INTERLACE (0 << 2) | ||
97 | #define MXR_CFG_SCAN_PROGRASSIVE (1 << 2) | ||
98 | #define MXR_CFG_SCAN_NTSC (0 << 1) | ||
99 | #define MXR_CFG_SCAN_PAL (1 << 1) | ||
100 | #define MXR_CFG_SCAN_SD (0 << 0) | ||
101 | #define MXR_CFG_SCAN_HD (1 << 0) | ||
102 | #define MXR_CFG_SCAN_MASK 0x47 | ||
103 | |||
104 | /* bits for MXR_GRAPHICn_CFG */ | ||
105 | #define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21) | ||
106 | #define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20) | ||
107 | #define MXR_GRP_CFG_WIN_BLEND_EN (1 << 17) | ||
108 | #define MXR_GRP_CFG_PIXEL_BLEND_EN (1 << 16) | ||
109 | #define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8) | ||
110 | #define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0) | ||
111 | #define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0) | ||
112 | |||
113 | /* bits for MXR_GRAPHICn_WH */ | ||
114 | #define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28) | ||
115 | #define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12) | ||
116 | #define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16) | ||
117 | #define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0) | ||
118 | |||
119 | /* bits for MXR_GRAPHICn_SXY */ | ||
120 | #define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16) | ||
121 | #define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0) | ||
122 | |||
123 | /* bits for MXR_GRAPHICn_DXY */ | ||
124 | #define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16) | ||
125 | #define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0) | ||
126 | |||
127 | /* bits for MXR_INT_EN */ | ||
128 | #define MXR_INT_EN_VSYNC (1 << 11) | ||
129 | #define MXR_INT_EN_ALL (0x0f << 8) | ||
130 | |||
131 | /* bit for MXR_INT_STATUS */ | ||
132 | #define MXR_INT_CLEAR_VSYNC (1 << 11) | ||
133 | #define MXR_INT_STATUS_VSYNC (1 << 0) | ||
134 | |||
135 | /* bit for MXR_LAYER_CFG */ | ||
136 | #define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8) | ||
137 | #define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4) | ||
138 | #define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0) | ||
139 | |||
140 | #endif /* SAMSUNG_REGS_MIXER_H */ | ||
141 | |||
diff --git a/drivers/gpu/drm/exynos/regs-vp.h b/drivers/gpu/drm/exynos/regs-vp.h new file mode 100644 index 000000000000..10b737af0a72 --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-vp.h | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Cloned from drivers/media/video/s5p-tv/regs-vp.h | ||
4 | * | ||
5 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
6 | * http://www.samsung.com/ | ||
7 | * | ||
8 | * Video processor register header file for Samsung Mixer driver | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef SAMSUNG_REGS_VP_H | ||
16 | #define SAMSUNG_REGS_VP_H | ||
17 | |||
18 | /* | ||
19 | * Register part | ||
20 | */ | ||
21 | |||
22 | #define VP_ENABLE 0x0000 | ||
23 | #define VP_SRESET 0x0004 | ||
24 | #define VP_SHADOW_UPDATE 0x0008 | ||
25 | #define VP_FIELD_ID 0x000C | ||
26 | #define VP_MODE 0x0010 | ||
27 | #define VP_IMG_SIZE_Y 0x0014 | ||
28 | #define VP_IMG_SIZE_C 0x0018 | ||
29 | #define VP_PER_RATE_CTRL 0x001C | ||
30 | #define VP_TOP_Y_PTR 0x0028 | ||
31 | #define VP_BOT_Y_PTR 0x002C | ||
32 | #define VP_TOP_C_PTR 0x0030 | ||
33 | #define VP_BOT_C_PTR 0x0034 | ||
34 | #define VP_ENDIAN_MODE 0x03CC | ||
35 | #define VP_SRC_H_POSITION 0x0044 | ||
36 | #define VP_SRC_V_POSITION 0x0048 | ||
37 | #define VP_SRC_WIDTH 0x004C | ||
38 | #define VP_SRC_HEIGHT 0x0050 | ||
39 | #define VP_DST_H_POSITION 0x0054 | ||
40 | #define VP_DST_V_POSITION 0x0058 | ||
41 | #define VP_DST_WIDTH 0x005C | ||
42 | #define VP_DST_HEIGHT 0x0060 | ||
43 | #define VP_H_RATIO 0x0064 | ||
44 | #define VP_V_RATIO 0x0068 | ||
45 | #define VP_POLY8_Y0_LL 0x006C | ||
46 | #define VP_POLY4_Y0_LL 0x00EC | ||
47 | #define VP_POLY4_C0_LL 0x012C | ||
48 | |||
49 | /* | ||
50 | * Bit definition part | ||
51 | */ | ||
52 | |||
53 | /* generates mask for range of bits */ | ||
54 | |||
55 | #define VP_MASK(high_bit, low_bit) \ | ||
56 | (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) | ||
57 | |||
58 | #define VP_MASK_VAL(val, high_bit, low_bit) \ | ||
59 | (((val) << (low_bit)) & VP_MASK(high_bit, low_bit)) | ||
60 | |||
61 | /* VP_ENABLE */ | ||
62 | #define VP_ENABLE_ON (1 << 0) | ||
63 | |||
64 | /* VP_SRESET */ | ||
65 | #define VP_SRESET_PROCESSING (1 << 0) | ||
66 | |||
67 | /* VP_SHADOW_UPDATE */ | ||
68 | #define VP_SHADOW_UPDATE_ENABLE (1 << 0) | ||
69 | |||
70 | /* VP_MODE */ | ||
71 | #define VP_MODE_NV12 (0 << 6) | ||
72 | #define VP_MODE_NV21 (1 << 6) | ||
73 | #define VP_MODE_LINE_SKIP (1 << 5) | ||
74 | #define VP_MODE_MEM_LINEAR (0 << 4) | ||
75 | #define VP_MODE_MEM_TILED (1 << 4) | ||
76 | #define VP_MODE_FMT_MASK (5 << 4) | ||
77 | #define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2) | ||
78 | #define VP_MODE_2D_IPC (1 << 1) | ||
79 | |||
80 | /* VP_IMG_SIZE_Y */ | ||
81 | /* VP_IMG_SIZE_C */ | ||
82 | #define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16) | ||
83 | #define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0) | ||
84 | |||
85 | /* VP_SRC_H_POSITION */ | ||
86 | #define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4) | ||
87 | |||
88 | /* VP_ENDIAN_MODE */ | ||
89 | #define VP_ENDIAN_MODE_LITTLE (1 << 0) | ||
90 | |||
91 | #endif /* SAMSUNG_REGS_VP_H */ | ||
diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 4f99cb41ed6e..bdf0152cbbe9 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h | |||
@@ -107,6 +107,10 @@ | |||
107 | #define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ | 107 | #define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ |
108 | #define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ | 108 | #define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ |
109 | 109 | ||
110 | /* 2 non contiguous plane YCbCr */ | ||
111 | #define DRM_FORMAT_NV12M fourcc_code('N', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane */ | ||
112 | #define DRM_FORMAT_NV12MT fourcc_code('T', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane 64x32 macroblocks */ | ||
113 | |||
110 | /* | 114 | /* |
111 | * 3 plane YCbCr | 115 | * 3 plane YCbCr |
112 | * index 0: Y plane, [7:0] Y | 116 | * index 0: Y plane, [7:0] Y |
@@ -127,4 +131,7 @@ | |||
127 | #define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ | 131 | #define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ |
128 | #define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ | 132 | #define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ |
129 | 133 | ||
134 | /* 3 non contiguous plane YCbCr */ | ||
135 | #define DRM_FORMAT_YUV420M fourcc_code('Y', 'M', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ | ||
136 | |||
130 | #endif /* DRM_FOURCC_H */ | 137 | #endif /* DRM_FOURCC_H */ |
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 7a2262a7689f..5e120f1c5cd9 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h | |||
@@ -112,4 +112,31 @@ struct exynos_drm_fimd_pdata { | |||
112 | unsigned int bpp; | 112 | unsigned int bpp; |
113 | }; | 113 | }; |
114 | 114 | ||
115 | /** | ||
116 | * Platform Specific Structure for DRM based HDMI. | ||
117 | * | ||
118 | * @hdmi_dev: device point to specific hdmi driver. | ||
119 | * @mixer_dev: device point to specific mixer driver. | ||
120 | * | ||
121 | * this structure is used for common hdmi driver and each device object | ||
122 | * would be used to access specific device driver(hdmi or mixer driver) | ||
123 | */ | ||
124 | struct exynos_drm_common_hdmi_pd { | ||
125 | struct device *hdmi_dev; | ||
126 | struct device *mixer_dev; | ||
127 | }; | ||
128 | |||
129 | /** | ||
130 | * Platform Specific Structure for DRM based HDMI core. | ||
131 | * | ||
132 | * @timing: default video mode for initializing | ||
133 | * @default_win: default window layer number to be used for UI. | ||
134 | * @bpp: default bit per pixel. | ||
135 | */ | ||
136 | struct exynos_drm_hdmi_pdata { | ||
137 | struct fb_videomode timing; | ||
138 | unsigned int default_win; | ||
139 | unsigned int bpp; | ||
140 | }; | ||
141 | |||
115 | #endif | 142 | #endif |