diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/Kconfig | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/Kconfig | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/Makefile | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_backlight.c | 90 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_backlight.h | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 763 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_crtc.h | 60 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_drv.c | 361 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_drv.h | 47 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_kms.c | 160 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_kms.h | 34 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_plane.c | 268 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_plane.h | 22 | ||||
-rw-r--r-- | drivers/gpu/drm/shmobile/shmob_drm_regs.h | 311 |
15 files changed, 2159 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 7da5f0887ca..18321b68b88 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig | |||
@@ -208,3 +208,5 @@ source "drivers/gpu/drm/ast/Kconfig" | |||
208 | source "drivers/gpu/drm/mgag200/Kconfig" | 208 | source "drivers/gpu/drm/mgag200/Kconfig" |
209 | 209 | ||
210 | source "drivers/gpu/drm/cirrus/Kconfig" | 210 | source "drivers/gpu/drm/cirrus/Kconfig" |
211 | |||
212 | source "drivers/gpu/drm/shmobile/Kconfig" | ||
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 58961b92309..2ff5cefe9ea 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -47,4 +47,5 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ | |||
47 | obj-$(CONFIG_DRM_GMA500) += gma500/ | 47 | obj-$(CONFIG_DRM_GMA500) += gma500/ |
48 | obj-$(CONFIG_DRM_UDL) += udl/ | 48 | obj-$(CONFIG_DRM_UDL) += udl/ |
49 | obj-$(CONFIG_DRM_AST) += ast/ | 49 | obj-$(CONFIG_DRM_AST) += ast/ |
50 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ | ||
50 | obj-y += i2c/ | 51 | obj-y += i2c/ |
diff --git a/drivers/gpu/drm/shmobile/Kconfig b/drivers/gpu/drm/shmobile/Kconfig new file mode 100644 index 00000000000..7e7d52b2a2f --- /dev/null +++ b/drivers/gpu/drm/shmobile/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | config DRM_SHMOBILE | ||
2 | tristate "DRM Support for SH Mobile" | ||
3 | depends on DRM && (SUPERH || ARCH_SHMOBILE) | ||
4 | select DRM_KMS_HELPER | ||
5 | select DRM_KMS_CMA_HELPER | ||
6 | select DRM_GEM_CMA_HELPER | ||
7 | help | ||
8 | Choose this option if you have an SH Mobile chipset. | ||
9 | If M is selected the module will be called shmob-drm. | ||
10 | |||
diff --git a/drivers/gpu/drm/shmobile/Makefile b/drivers/gpu/drm/shmobile/Makefile new file mode 100644 index 00000000000..4c3eeb35563 --- /dev/null +++ b/drivers/gpu/drm/shmobile/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | shmob-drm-y := shmob_drm_backlight.o \ | ||
2 | shmob_drm_crtc.o \ | ||
3 | shmob_drm_drv.o \ | ||
4 | shmob_drm_kms.o \ | ||
5 | shmob_drm_plane.o | ||
6 | |||
7 | obj-$(CONFIG_DRM_SHMOBILE) += shmob-drm.o | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.c b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c new file mode 100644 index 00000000000..463aee18f77 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * shmob_drm_backlight.c -- SH Mobile DRM Backlight | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/backlight.h> | ||
15 | |||
16 | #include "shmob_drm_backlight.h" | ||
17 | #include "shmob_drm_crtc.h" | ||
18 | #include "shmob_drm_drv.h" | ||
19 | |||
20 | static int shmob_drm_backlight_update(struct backlight_device *bdev) | ||
21 | { | ||
22 | struct shmob_drm_connector *scon = bl_get_data(bdev); | ||
23 | struct shmob_drm_device *sdev = scon->connector.dev->dev_private; | ||
24 | const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; | ||
25 | int brightness = bdev->props.brightness; | ||
26 | |||
27 | if (bdev->props.power != FB_BLANK_UNBLANK || | ||
28 | bdev->props.state & BL_CORE_SUSPENDED) | ||
29 | brightness = 0; | ||
30 | |||
31 | return bdata->set_brightness(brightness); | ||
32 | } | ||
33 | |||
34 | static int shmob_drm_backlight_get_brightness(struct backlight_device *bdev) | ||
35 | { | ||
36 | struct shmob_drm_connector *scon = bl_get_data(bdev); | ||
37 | struct shmob_drm_device *sdev = scon->connector.dev->dev_private; | ||
38 | const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; | ||
39 | |||
40 | return bdata->get_brightness(); | ||
41 | } | ||
42 | |||
43 | static const struct backlight_ops shmob_drm_backlight_ops = { | ||
44 | .options = BL_CORE_SUSPENDRESUME, | ||
45 | .update_status = shmob_drm_backlight_update, | ||
46 | .get_brightness = shmob_drm_backlight_get_brightness, | ||
47 | }; | ||
48 | |||
49 | void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode) | ||
50 | { | ||
51 | if (scon->backlight == NULL) | ||
52 | return; | ||
53 | |||
54 | scon->backlight->props.power = mode == DRM_MODE_DPMS_ON | ||
55 | ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | ||
56 | backlight_update_status(scon->backlight); | ||
57 | } | ||
58 | |||
59 | int shmob_drm_backlight_init(struct shmob_drm_connector *scon) | ||
60 | { | ||
61 | struct shmob_drm_device *sdev = scon->connector.dev->dev_private; | ||
62 | const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; | ||
63 | struct drm_connector *connector = &scon->connector; | ||
64 | struct drm_device *dev = connector->dev; | ||
65 | struct backlight_device *backlight; | ||
66 | |||
67 | if (!bdata->max_brightness) | ||
68 | return 0; | ||
69 | |||
70 | backlight = backlight_device_register(bdata->name, dev->dev, scon, | ||
71 | &shmob_drm_backlight_ops, NULL); | ||
72 | if (IS_ERR(backlight)) { | ||
73 | dev_err(dev->dev, "unable to register backlight device: %ld\n", | ||
74 | PTR_ERR(backlight)); | ||
75 | return PTR_ERR(backlight); | ||
76 | } | ||
77 | |||
78 | backlight->props.max_brightness = bdata->max_brightness; | ||
79 | backlight->props.brightness = bdata->max_brightness; | ||
80 | backlight->props.power = FB_BLANK_POWERDOWN; | ||
81 | backlight_update_status(backlight); | ||
82 | |||
83 | scon->backlight = backlight; | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | void shmob_drm_backlight_exit(struct shmob_drm_connector *scon) | ||
88 | { | ||
89 | backlight_device_unregister(scon->backlight); | ||
90 | } | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.h b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h new file mode 100644 index 00000000000..9477595d2ff --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * shmob_drm_backlight.h -- SH Mobile DRM Backlight | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #ifndef __SHMOB_DRM_BACKLIGHT_H__ | ||
15 | #define __SHMOB_DRM_BACKLIGHT_H__ | ||
16 | |||
17 | struct shmob_drm_connector; | ||
18 | |||
19 | void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode); | ||
20 | int shmob_drm_backlight_init(struct shmob_drm_connector *scon); | ||
21 | void shmob_drm_backlight_exit(struct shmob_drm_connector *scon); | ||
22 | |||
23 | #endif /* __SHMOB_DRM_BACKLIGHT_H__ */ | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c new file mode 100644 index 00000000000..0e7a9306bd0 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c | |||
@@ -0,0 +1,763 @@ | |||
1 | /* | ||
2 | * shmob_drm_crtc.c -- SH Mobile DRM CRTCs | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/backlight.h> | ||
15 | #include <linux/clk.h> | ||
16 | |||
17 | #include <drm/drmP.h> | ||
18 | #include <drm/drm_crtc.h> | ||
19 | #include <drm/drm_crtc_helper.h> | ||
20 | #include <drm/drm_fb_cma_helper.h> | ||
21 | #include <drm/drm_gem_cma_helper.h> | ||
22 | |||
23 | #include <video/sh_mobile_meram.h> | ||
24 | |||
25 | #include "shmob_drm_backlight.h" | ||
26 | #include "shmob_drm_crtc.h" | ||
27 | #include "shmob_drm_drv.h" | ||
28 | #include "shmob_drm_kms.h" | ||
29 | #include "shmob_drm_plane.h" | ||
30 | #include "shmob_drm_regs.h" | ||
31 | |||
32 | /* | ||
33 | * TODO: panel support | ||
34 | */ | ||
35 | |||
36 | /* ----------------------------------------------------------------------------- | ||
37 | * Clock management | ||
38 | */ | ||
39 | |||
40 | static void shmob_drm_clk_on(struct shmob_drm_device *sdev) | ||
41 | { | ||
42 | if (sdev->clock) | ||
43 | clk_enable(sdev->clock); | ||
44 | #if 0 | ||
45 | if (sdev->meram_dev && sdev->meram_dev->pdev) | ||
46 | pm_runtime_get_sync(&sdev->meram_dev->pdev->dev); | ||
47 | #endif | ||
48 | } | ||
49 | |||
50 | static void shmob_drm_clk_off(struct shmob_drm_device *sdev) | ||
51 | { | ||
52 | #if 0 | ||
53 | if (sdev->meram_dev && sdev->meram_dev->pdev) | ||
54 | pm_runtime_put_sync(&sdev->meram_dev->pdev->dev); | ||
55 | #endif | ||
56 | if (sdev->clock) | ||
57 | clk_disable(sdev->clock); | ||
58 | } | ||
59 | |||
60 | /* ----------------------------------------------------------------------------- | ||
61 | * CRTC | ||
62 | */ | ||
63 | |||
64 | static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc) | ||
65 | { | ||
66 | struct drm_crtc *crtc = &scrtc->crtc; | ||
67 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
68 | const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; | ||
69 | const struct drm_display_mode *mode = &crtc->mode; | ||
70 | u32 value; | ||
71 | |||
72 | value = sdev->ldmt1r | ||
73 | | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL) | ||
74 | | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL) | ||
75 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0) | ||
76 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0) | ||
77 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0) | ||
78 | | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0) | ||
79 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0); | ||
80 | lcdc_write(sdev, LDMT1R, value); | ||
81 | |||
82 | if (idata->interface >= SHMOB_DRM_IFACE_SYS8A && | ||
83 | idata->interface <= SHMOB_DRM_IFACE_SYS24) { | ||
84 | /* Setup SYS bus. */ | ||
85 | value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT) | ||
86 | | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0) | ||
87 | | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0) | ||
88 | | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT) | ||
89 | | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT) | ||
90 | | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT); | ||
91 | lcdc_write(sdev, LDMT2R, value); | ||
92 | |||
93 | value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT) | ||
94 | | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT) | ||
95 | | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT) | ||
96 | | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT); | ||
97 | lcdc_write(sdev, LDMT3R, value); | ||
98 | } | ||
99 | |||
100 | value = ((mode->hdisplay / 8) << 16) /* HDCN */ | ||
101 | | (mode->htotal / 8); /* HTCN */ | ||
102 | lcdc_write(sdev, LDHCNR, value); | ||
103 | |||
104 | value = (((mode->hsync_end - mode->hsync_start) / 8) << 16) /* HSYNW */ | ||
105 | | (mode->hsync_start / 8); /* HSYNP */ | ||
106 | lcdc_write(sdev, LDHSYNR, value); | ||
107 | |||
108 | value = ((mode->hdisplay & 7) << 24) | ((mode->htotal & 7) << 16) | ||
109 | | (((mode->hsync_end - mode->hsync_start) & 7) << 8) | ||
110 | | (mode->hsync_start & 7); | ||
111 | lcdc_write(sdev, LDHAJR, value); | ||
112 | |||
113 | value = ((mode->vdisplay) << 16) /* VDLN */ | ||
114 | | mode->vtotal; /* VTLN */ | ||
115 | lcdc_write(sdev, LDVLNR, value); | ||
116 | |||
117 | value = ((mode->vsync_end - mode->vsync_start) << 16) /* VSYNW */ | ||
118 | | mode->vsync_start; /* VSYNP */ | ||
119 | lcdc_write(sdev, LDVSYNR, value); | ||
120 | } | ||
121 | |||
122 | static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start) | ||
123 | { | ||
124 | struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private; | ||
125 | u32 value; | ||
126 | |||
127 | value = lcdc_read(sdev, LDCNT2R); | ||
128 | if (start) | ||
129 | lcdc_write(sdev, LDCNT2R, value | LDCNT2R_DO); | ||
130 | else | ||
131 | lcdc_write(sdev, LDCNT2R, value & ~LDCNT2R_DO); | ||
132 | |||
133 | /* Wait until power is applied/stopped. */ | ||
134 | while (1) { | ||
135 | value = lcdc_read(sdev, LDPMR) & LDPMR_LPS; | ||
136 | if ((start && value) || (!start && !value)) | ||
137 | break; | ||
138 | |||
139 | cpu_relax(); | ||
140 | } | ||
141 | |||
142 | if (!start) { | ||
143 | /* Stop the dot clock. */ | ||
144 | lcdc_write(sdev, LDDCKSTPR, LDDCKSTPR_DCKSTP); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * shmob_drm_crtc_start - Configure and start the LCDC | ||
150 | * @scrtc: the SH Mobile CRTC | ||
151 | * | ||
152 | * Configure and start the LCDC device. External devices (clocks, MERAM, panels, | ||
153 | * ...) are not touched by this function. | ||
154 | */ | ||
155 | static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) | ||
156 | { | ||
157 | struct drm_crtc *crtc = &scrtc->crtc; | ||
158 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
159 | const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; | ||
160 | const struct shmob_drm_format_info *format; | ||
161 | struct drm_device *dev = sdev->ddev; | ||
162 | struct drm_plane *plane; | ||
163 | u32 value; | ||
164 | |||
165 | if (scrtc->started) | ||
166 | return; | ||
167 | |||
168 | format = shmob_drm_format_info(crtc->fb->pixel_format); | ||
169 | if (WARN_ON(format == NULL)) | ||
170 | return; | ||
171 | |||
172 | /* Enable clocks before accessing the hardware. */ | ||
173 | shmob_drm_clk_on(sdev); | ||
174 | |||
175 | /* Reset and enable the LCDC. */ | ||
176 | lcdc_write(sdev, LDCNT2R, lcdc_read(sdev, LDCNT2R) | LDCNT2R_BR); | ||
177 | lcdc_wait_bit(sdev, LDCNT2R, LDCNT2R_BR, 0); | ||
178 | lcdc_write(sdev, LDCNT2R, LDCNT2R_ME); | ||
179 | |||
180 | /* Stop the LCDC first and disable all interrupts. */ | ||
181 | shmob_drm_crtc_start_stop(scrtc, false); | ||
182 | lcdc_write(sdev, LDINTR, 0); | ||
183 | |||
184 | /* Configure power supply, dot clocks and start them. */ | ||
185 | lcdc_write(sdev, LDPMR, 0); | ||
186 | |||
187 | value = sdev->lddckr; | ||
188 | if (idata->clk_div) { | ||
189 | /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider | ||
190 | * denominator. | ||
191 | */ | ||
192 | lcdc_write(sdev, LDDCKPAT1R, 0); | ||
193 | lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1); | ||
194 | |||
195 | if (idata->clk_div == 1) | ||
196 | value |= LDDCKR_MOSEL; | ||
197 | else | ||
198 | value |= idata->clk_div; | ||
199 | } | ||
200 | |||
201 | lcdc_write(sdev, LDDCKR, value); | ||
202 | lcdc_write(sdev, LDDCKSTPR, 0); | ||
203 | lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0); | ||
204 | |||
205 | /* TODO: Setup SYS panel */ | ||
206 | |||
207 | /* Setup geometry, format, frame buffer memory and operation mode. */ | ||
208 | shmob_drm_crtc_setup_geometry(scrtc); | ||
209 | |||
210 | /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */ | ||
211 | lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1); | ||
212 | lcdc_write(sdev, LDMLSR, scrtc->line_size); | ||
213 | lcdc_write(sdev, LDSA1R, scrtc->dma[0]); | ||
214 | if (format->yuv) | ||
215 | lcdc_write(sdev, LDSA2R, scrtc->dma[1]); | ||
216 | lcdc_write(sdev, LDSM1R, 0); | ||
217 | |||
218 | /* Word and long word swap. */ | ||
219 | switch (format->fourcc) { | ||
220 | case DRM_FORMAT_RGB565: | ||
221 | case DRM_FORMAT_NV21: | ||
222 | case DRM_FORMAT_NV61: | ||
223 | case DRM_FORMAT_NV42: | ||
224 | value = LDDDSR_LS | LDDDSR_WS; | ||
225 | break; | ||
226 | case DRM_FORMAT_RGB888: | ||
227 | case DRM_FORMAT_NV12: | ||
228 | case DRM_FORMAT_NV16: | ||
229 | case DRM_FORMAT_NV24: | ||
230 | value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; | ||
231 | break; | ||
232 | case DRM_FORMAT_ARGB8888: | ||
233 | default: | ||
234 | value = LDDDSR_LS; | ||
235 | break; | ||
236 | } | ||
237 | lcdc_write(sdev, LDDDSR, value); | ||
238 | |||
239 | /* Setup planes. */ | ||
240 | list_for_each_entry(plane, &dev->mode_config.plane_list, head) { | ||
241 | if (plane->crtc == crtc) | ||
242 | shmob_drm_plane_setup(plane); | ||
243 | } | ||
244 | |||
245 | /* Enable the display output. */ | ||
246 | lcdc_write(sdev, LDCNT1R, LDCNT1R_DE); | ||
247 | |||
248 | shmob_drm_crtc_start_stop(scrtc, true); | ||
249 | |||
250 | scrtc->started = true; | ||
251 | } | ||
252 | |||
253 | static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc) | ||
254 | { | ||
255 | struct drm_crtc *crtc = &scrtc->crtc; | ||
256 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
257 | |||
258 | if (!scrtc->started) | ||
259 | return; | ||
260 | |||
261 | /* Disable the MERAM cache. */ | ||
262 | if (scrtc->cache) { | ||
263 | sh_mobile_meram_cache_free(sdev->meram, scrtc->cache); | ||
264 | scrtc->cache = NULL; | ||
265 | } | ||
266 | |||
267 | /* Stop the LCDC. */ | ||
268 | shmob_drm_crtc_start_stop(scrtc, false); | ||
269 | |||
270 | /* Disable the display output. */ | ||
271 | lcdc_write(sdev, LDCNT1R, 0); | ||
272 | |||
273 | /* Stop clocks. */ | ||
274 | shmob_drm_clk_off(sdev); | ||
275 | |||
276 | scrtc->started = false; | ||
277 | } | ||
278 | |||
279 | void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc) | ||
280 | { | ||
281 | shmob_drm_crtc_stop(scrtc); | ||
282 | } | ||
283 | |||
284 | void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc) | ||
285 | { | ||
286 | if (scrtc->dpms != DRM_MODE_DPMS_ON) | ||
287 | return; | ||
288 | |||
289 | shmob_drm_crtc_start(scrtc); | ||
290 | } | ||
291 | |||
292 | static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc, | ||
293 | int x, int y) | ||
294 | { | ||
295 | struct drm_crtc *crtc = &scrtc->crtc; | ||
296 | struct drm_framebuffer *fb = crtc->fb; | ||
297 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
298 | struct drm_gem_cma_object *gem; | ||
299 | unsigned int bpp; | ||
300 | |||
301 | bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp; | ||
302 | gem = drm_fb_cma_get_gem_obj(fb, 0); | ||
303 | scrtc->dma[0] = gem->paddr + fb->offsets[0] | ||
304 | + y * fb->pitches[0] + x * bpp / 8; | ||
305 | |||
306 | if (scrtc->format->yuv) { | ||
307 | bpp = scrtc->format->bpp - 8; | ||
308 | gem = drm_fb_cma_get_gem_obj(fb, 1); | ||
309 | scrtc->dma[1] = gem->paddr + fb->offsets[1] | ||
310 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] | ||
311 | + x * (bpp == 16 ? 2 : 1); | ||
312 | } | ||
313 | |||
314 | if (scrtc->cache) | ||
315 | sh_mobile_meram_cache_update(sdev->meram, scrtc->cache, | ||
316 | scrtc->dma[0], scrtc->dma[1], | ||
317 | &scrtc->dma[0], &scrtc->dma[1]); | ||
318 | } | ||
319 | |||
320 | static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc) | ||
321 | { | ||
322 | struct drm_crtc *crtc = &scrtc->crtc; | ||
323 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
324 | |||
325 | shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y); | ||
326 | |||
327 | lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]); | ||
328 | if (scrtc->format->yuv) | ||
329 | lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]); | ||
330 | |||
331 | lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS); | ||
332 | } | ||
333 | |||
334 | #define to_shmob_crtc(c) container_of(c, struct shmob_drm_crtc, crtc) | ||
335 | |||
336 | static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
337 | { | ||
338 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | ||
339 | |||
340 | if (scrtc->dpms == mode) | ||
341 | return; | ||
342 | |||
343 | if (mode == DRM_MODE_DPMS_ON) | ||
344 | shmob_drm_crtc_start(scrtc); | ||
345 | else | ||
346 | shmob_drm_crtc_stop(scrtc); | ||
347 | |||
348 | scrtc->dpms = mode; | ||
349 | } | ||
350 | |||
351 | static bool shmob_drm_crtc_mode_fixup(struct drm_crtc *crtc, | ||
352 | const struct drm_display_mode *mode, | ||
353 | struct drm_display_mode *adjusted_mode) | ||
354 | { | ||
355 | return true; | ||
356 | } | ||
357 | |||
358 | static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc) | ||
359 | { | ||
360 | shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
361 | } | ||
362 | |||
363 | static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc, | ||
364 | struct drm_display_mode *mode, | ||
365 | struct drm_display_mode *adjusted_mode, | ||
366 | int x, int y, | ||
367 | struct drm_framebuffer *old_fb) | ||
368 | { | ||
369 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | ||
370 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
371 | const struct sh_mobile_meram_cfg *mdata = sdev->pdata->meram; | ||
372 | const struct shmob_drm_format_info *format; | ||
373 | void *cache; | ||
374 | |||
375 | format = shmob_drm_format_info(crtc->fb->pixel_format); | ||
376 | if (format == NULL) { | ||
377 | dev_dbg(sdev->dev, "mode_set: unsupported format %08x\n", | ||
378 | crtc->fb->pixel_format); | ||
379 | return -EINVAL; | ||
380 | } | ||
381 | |||
382 | scrtc->format = format; | ||
383 | scrtc->line_size = crtc->fb->pitches[0]; | ||
384 | |||
385 | if (sdev->meram) { | ||
386 | /* Enable MERAM cache if configured. We need to de-init | ||
387 | * configured ICBs before we can re-initialize them. | ||
388 | */ | ||
389 | if (scrtc->cache) { | ||
390 | sh_mobile_meram_cache_free(sdev->meram, scrtc->cache); | ||
391 | scrtc->cache = NULL; | ||
392 | } | ||
393 | |||
394 | cache = sh_mobile_meram_cache_alloc(sdev->meram, mdata, | ||
395 | crtc->fb->pitches[0], | ||
396 | adjusted_mode->vdisplay, | ||
397 | format->meram, | ||
398 | &scrtc->line_size); | ||
399 | if (!IS_ERR(cache)) | ||
400 | scrtc->cache = cache; | ||
401 | } | ||
402 | |||
403 | shmob_drm_crtc_compute_base(scrtc, x, y); | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc) | ||
409 | { | ||
410 | shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | ||
411 | } | ||
412 | |||
413 | static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
414 | struct drm_framebuffer *old_fb) | ||
415 | { | ||
416 | shmob_drm_crtc_update_base(to_shmob_crtc(crtc)); | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static const struct drm_crtc_helper_funcs crtc_helper_funcs = { | ||
422 | .dpms = shmob_drm_crtc_dpms, | ||
423 | .mode_fixup = shmob_drm_crtc_mode_fixup, | ||
424 | .prepare = shmob_drm_crtc_mode_prepare, | ||
425 | .commit = shmob_drm_crtc_mode_commit, | ||
426 | .mode_set = shmob_drm_crtc_mode_set, | ||
427 | .mode_set_base = shmob_drm_crtc_mode_set_base, | ||
428 | }; | ||
429 | |||
430 | void shmob_drm_crtc_cancel_page_flip(struct shmob_drm_crtc *scrtc, | ||
431 | struct drm_file *file) | ||
432 | { | ||
433 | struct drm_pending_vblank_event *event; | ||
434 | struct drm_device *dev = scrtc->crtc.dev; | ||
435 | unsigned long flags; | ||
436 | |||
437 | /* Destroy the pending vertical blanking event associated with the | ||
438 | * pending page flip, if any, and disable vertical blanking interrupts. | ||
439 | */ | ||
440 | spin_lock_irqsave(&dev->event_lock, flags); | ||
441 | event = scrtc->event; | ||
442 | if (event && event->base.file_priv == file) { | ||
443 | scrtc->event = NULL; | ||
444 | event->base.destroy(&event->base); | ||
445 | drm_vblank_put(dev, 0); | ||
446 | } | ||
447 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
448 | } | ||
449 | |||
450 | void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc) | ||
451 | { | ||
452 | struct drm_pending_vblank_event *event; | ||
453 | struct drm_device *dev = scrtc->crtc.dev; | ||
454 | struct timeval vblanktime; | ||
455 | unsigned long flags; | ||
456 | |||
457 | spin_lock_irqsave(&dev->event_lock, flags); | ||
458 | event = scrtc->event; | ||
459 | scrtc->event = NULL; | ||
460 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
461 | |||
462 | if (event == NULL) | ||
463 | return; | ||
464 | |||
465 | event->event.sequence = drm_vblank_count_and_time(dev, 0, &vblanktime); | ||
466 | event->event.tv_sec = vblanktime.tv_sec; | ||
467 | event->event.tv_usec = vblanktime.tv_usec; | ||
468 | |||
469 | spin_lock_irqsave(&dev->event_lock, flags); | ||
470 | list_add_tail(&event->base.link, &event->base.file_priv->event_list); | ||
471 | wake_up_interruptible(&event->base.file_priv->event_wait); | ||
472 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
473 | |||
474 | drm_vblank_put(dev, 0); | ||
475 | } | ||
476 | |||
477 | static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, | ||
478 | struct drm_framebuffer *fb, | ||
479 | struct drm_pending_vblank_event *event) | ||
480 | { | ||
481 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | ||
482 | struct drm_device *dev = scrtc->crtc.dev; | ||
483 | unsigned long flags; | ||
484 | |||
485 | spin_lock_irqsave(&dev->event_lock, flags); | ||
486 | if (scrtc->event != NULL) { | ||
487 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
488 | return -EBUSY; | ||
489 | } | ||
490 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
491 | |||
492 | crtc->fb = fb; | ||
493 | shmob_drm_crtc_update_base(scrtc); | ||
494 | |||
495 | if (event) { | ||
496 | event->pipe = 0; | ||
497 | spin_lock_irqsave(&dev->event_lock, flags); | ||
498 | scrtc->event = event; | ||
499 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
500 | drm_vblank_get(dev, 0); | ||
501 | } | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static const struct drm_crtc_funcs crtc_funcs = { | ||
507 | .destroy = drm_crtc_cleanup, | ||
508 | .set_config = drm_crtc_helper_set_config, | ||
509 | .page_flip = shmob_drm_crtc_page_flip, | ||
510 | }; | ||
511 | |||
512 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev) | ||
513 | { | ||
514 | struct drm_crtc *crtc = &sdev->crtc.crtc; | ||
515 | int ret; | ||
516 | |||
517 | sdev->crtc.dpms = DRM_MODE_DPMS_OFF; | ||
518 | |||
519 | ret = drm_crtc_init(sdev->ddev, crtc, &crtc_funcs); | ||
520 | if (ret < 0) | ||
521 | return ret; | ||
522 | |||
523 | drm_crtc_helper_add(crtc, &crtc_helper_funcs); | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /* ----------------------------------------------------------------------------- | ||
529 | * Encoder | ||
530 | */ | ||
531 | |||
532 | #define to_shmob_encoder(e) \ | ||
533 | container_of(e, struct shmob_drm_encoder, encoder) | ||
534 | |||
535 | static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
536 | { | ||
537 | struct shmob_drm_encoder *senc = to_shmob_encoder(encoder); | ||
538 | struct shmob_drm_device *sdev = encoder->dev->dev_private; | ||
539 | struct shmob_drm_connector *scon = &sdev->connector; | ||
540 | |||
541 | if (senc->dpms == mode) | ||
542 | return; | ||
543 | |||
544 | shmob_drm_backlight_dpms(scon, mode); | ||
545 | |||
546 | senc->dpms = mode; | ||
547 | } | ||
548 | |||
549 | static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder, | ||
550 | const struct drm_display_mode *mode, | ||
551 | struct drm_display_mode *adjusted_mode) | ||
552 | { | ||
553 | struct drm_device *dev = encoder->dev; | ||
554 | struct shmob_drm_device *sdev = dev->dev_private; | ||
555 | struct drm_connector *connector = &sdev->connector.connector; | ||
556 | const struct drm_display_mode *panel_mode; | ||
557 | |||
558 | if (list_empty(&connector->modes)) { | ||
559 | dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); | ||
560 | return false; | ||
561 | } | ||
562 | |||
563 | /* The flat panel mode is fixed, just copy it to the adjusted mode. */ | ||
564 | panel_mode = list_first_entry(&connector->modes, | ||
565 | struct drm_display_mode, head); | ||
566 | drm_mode_copy(adjusted_mode, panel_mode); | ||
567 | |||
568 | return true; | ||
569 | } | ||
570 | |||
571 | static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder) | ||
572 | { | ||
573 | /* No-op, everything is handled in the CRTC code. */ | ||
574 | } | ||
575 | |||
576 | static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder, | ||
577 | struct drm_display_mode *mode, | ||
578 | struct drm_display_mode *adjusted_mode) | ||
579 | { | ||
580 | /* No-op, everything is handled in the CRTC code. */ | ||
581 | } | ||
582 | |||
583 | static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder) | ||
584 | { | ||
585 | /* No-op, everything is handled in the CRTC code. */ | ||
586 | } | ||
587 | |||
588 | static const struct drm_encoder_helper_funcs encoder_helper_funcs = { | ||
589 | .dpms = shmob_drm_encoder_dpms, | ||
590 | .mode_fixup = shmob_drm_encoder_mode_fixup, | ||
591 | .prepare = shmob_drm_encoder_mode_prepare, | ||
592 | .commit = shmob_drm_encoder_mode_commit, | ||
593 | .mode_set = shmob_drm_encoder_mode_set, | ||
594 | }; | ||
595 | |||
596 | static void shmob_drm_encoder_destroy(struct drm_encoder *encoder) | ||
597 | { | ||
598 | drm_encoder_cleanup(encoder); | ||
599 | } | ||
600 | |||
601 | static const struct drm_encoder_funcs encoder_funcs = { | ||
602 | .destroy = shmob_drm_encoder_destroy, | ||
603 | }; | ||
604 | |||
605 | int shmob_drm_encoder_create(struct shmob_drm_device *sdev) | ||
606 | { | ||
607 | struct drm_encoder *encoder = &sdev->encoder.encoder; | ||
608 | int ret; | ||
609 | |||
610 | sdev->encoder.dpms = DRM_MODE_DPMS_OFF; | ||
611 | |||
612 | encoder->possible_crtcs = 1; | ||
613 | |||
614 | ret = drm_encoder_init(sdev->ddev, encoder, &encoder_funcs, | ||
615 | DRM_MODE_ENCODER_LVDS); | ||
616 | if (ret < 0) | ||
617 | return ret; | ||
618 | |||
619 | drm_encoder_helper_add(encoder, &encoder_helper_funcs); | ||
620 | |||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev, bool enable) | ||
625 | { | ||
626 | unsigned long flags; | ||
627 | u32 ldintr; | ||
628 | |||
629 | /* Be careful not to acknowledge any pending interrupt. */ | ||
630 | spin_lock_irqsave(&sdev->irq_lock, flags); | ||
631 | ldintr = lcdc_read(sdev, LDINTR) | LDINTR_STATUS_MASK; | ||
632 | if (enable) | ||
633 | ldintr |= LDINTR_VEE; | ||
634 | else | ||
635 | ldintr &= ~LDINTR_VEE; | ||
636 | lcdc_write(sdev, LDINTR, ldintr); | ||
637 | spin_unlock_irqrestore(&sdev->irq_lock, flags); | ||
638 | } | ||
639 | |||
640 | /* ----------------------------------------------------------------------------- | ||
641 | * Connector | ||
642 | */ | ||
643 | |||
644 | #define to_shmob_connector(c) \ | ||
645 | container_of(c, struct shmob_drm_connector, connector) | ||
646 | |||
647 | static int shmob_drm_connector_get_modes(struct drm_connector *connector) | ||
648 | { | ||
649 | struct shmob_drm_device *sdev = connector->dev->dev_private; | ||
650 | struct drm_display_mode *mode; | ||
651 | |||
652 | mode = drm_mode_create(connector->dev); | ||
653 | if (mode == NULL) | ||
654 | return 0; | ||
655 | |||
656 | mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; | ||
657 | mode->clock = sdev->pdata->panel.mode.clock; | ||
658 | mode->hdisplay = sdev->pdata->panel.mode.hdisplay; | ||
659 | mode->hsync_start = sdev->pdata->panel.mode.hsync_start; | ||
660 | mode->hsync_end = sdev->pdata->panel.mode.hsync_end; | ||
661 | mode->htotal = sdev->pdata->panel.mode.htotal; | ||
662 | mode->vdisplay = sdev->pdata->panel.mode.vdisplay; | ||
663 | mode->vsync_start = sdev->pdata->panel.mode.vsync_start; | ||
664 | mode->vsync_end = sdev->pdata->panel.mode.vsync_end; | ||
665 | mode->vtotal = sdev->pdata->panel.mode.vtotal; | ||
666 | mode->flags = sdev->pdata->panel.mode.flags; | ||
667 | |||
668 | drm_mode_set_name(mode); | ||
669 | drm_mode_probed_add(connector, mode); | ||
670 | |||
671 | connector->display_info.width_mm = sdev->pdata->panel.width_mm; | ||
672 | connector->display_info.height_mm = sdev->pdata->panel.height_mm; | ||
673 | |||
674 | return 1; | ||
675 | } | ||
676 | |||
677 | static int shmob_drm_connector_mode_valid(struct drm_connector *connector, | ||
678 | struct drm_display_mode *mode) | ||
679 | { | ||
680 | return MODE_OK; | ||
681 | } | ||
682 | |||
683 | static struct drm_encoder * | ||
684 | shmob_drm_connector_best_encoder(struct drm_connector *connector) | ||
685 | { | ||
686 | struct shmob_drm_connector *scon = to_shmob_connector(connector); | ||
687 | |||
688 | return scon->encoder; | ||
689 | } | ||
690 | |||
691 | static const struct drm_connector_helper_funcs connector_helper_funcs = { | ||
692 | .get_modes = shmob_drm_connector_get_modes, | ||
693 | .mode_valid = shmob_drm_connector_mode_valid, | ||
694 | .best_encoder = shmob_drm_connector_best_encoder, | ||
695 | }; | ||
696 | |||
697 | static void shmob_drm_connector_destroy(struct drm_connector *connector) | ||
698 | { | ||
699 | struct shmob_drm_connector *scon = to_shmob_connector(connector); | ||
700 | |||
701 | shmob_drm_backlight_exit(scon); | ||
702 | drm_sysfs_connector_remove(connector); | ||
703 | drm_connector_cleanup(connector); | ||
704 | } | ||
705 | |||
706 | static enum drm_connector_status | ||
707 | shmob_drm_connector_detect(struct drm_connector *connector, bool force) | ||
708 | { | ||
709 | return connector_status_connected; | ||
710 | } | ||
711 | |||
712 | static const struct drm_connector_funcs connector_funcs = { | ||
713 | .dpms = drm_helper_connector_dpms, | ||
714 | .detect = shmob_drm_connector_detect, | ||
715 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
716 | .destroy = shmob_drm_connector_destroy, | ||
717 | }; | ||
718 | |||
719 | int shmob_drm_connector_create(struct shmob_drm_device *sdev, | ||
720 | struct drm_encoder *encoder) | ||
721 | { | ||
722 | struct drm_connector *connector = &sdev->connector.connector; | ||
723 | int ret; | ||
724 | |||
725 | sdev->connector.encoder = encoder; | ||
726 | |||
727 | connector->display_info.width_mm = sdev->pdata->panel.width_mm; | ||
728 | connector->display_info.height_mm = sdev->pdata->panel.height_mm; | ||
729 | |||
730 | ret = drm_connector_init(sdev->ddev, connector, &connector_funcs, | ||
731 | DRM_MODE_CONNECTOR_LVDS); | ||
732 | if (ret < 0) | ||
733 | return ret; | ||
734 | |||
735 | drm_connector_helper_add(connector, &connector_helper_funcs); | ||
736 | ret = drm_sysfs_connector_add(connector); | ||
737 | if (ret < 0) | ||
738 | goto err_cleanup; | ||
739 | |||
740 | ret = shmob_drm_backlight_init(&sdev->connector); | ||
741 | if (ret < 0) | ||
742 | goto err_sysfs; | ||
743 | |||
744 | ret = drm_mode_connector_attach_encoder(connector, encoder); | ||
745 | if (ret < 0) | ||
746 | goto err_backlight; | ||
747 | |||
748 | connector->encoder = encoder; | ||
749 | |||
750 | drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); | ||
751 | drm_connector_property_set_value(connector, | ||
752 | sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); | ||
753 | |||
754 | return 0; | ||
755 | |||
756 | err_backlight: | ||
757 | shmob_drm_backlight_exit(&sdev->connector); | ||
758 | err_sysfs: | ||
759 | drm_sysfs_connector_remove(connector); | ||
760 | err_cleanup: | ||
761 | drm_connector_cleanup(connector); | ||
762 | return ret; | ||
763 | } | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h new file mode 100644 index 00000000000..e5bd109c4c3 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * shmob_drm_crtc.h -- SH Mobile DRM CRTCs | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #ifndef __SHMOB_DRM_CRTC_H__ | ||
15 | #define __SHMOB_DRM_CRTC_H__ | ||
16 | |||
17 | #include <drm/drmP.h> | ||
18 | #include <drm/drm_crtc.h> | ||
19 | |||
20 | struct backlight_device; | ||
21 | struct shmob_drm_device; | ||
22 | |||
23 | struct shmob_drm_crtc { | ||
24 | struct drm_crtc crtc; | ||
25 | |||
26 | struct drm_pending_vblank_event *event; | ||
27 | int dpms; | ||
28 | |||
29 | const struct shmob_drm_format_info *format; | ||
30 | void *cache; | ||
31 | unsigned long dma[2]; | ||
32 | unsigned int line_size; | ||
33 | bool started; | ||
34 | }; | ||
35 | |||
36 | struct shmob_drm_encoder { | ||
37 | struct drm_encoder encoder; | ||
38 | int dpms; | ||
39 | }; | ||
40 | |||
41 | struct shmob_drm_connector { | ||
42 | struct drm_connector connector; | ||
43 | struct drm_encoder *encoder; | ||
44 | |||
45 | struct backlight_device *backlight; | ||
46 | }; | ||
47 | |||
48 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev); | ||
49 | void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev, bool enable); | ||
50 | void shmob_drm_crtc_cancel_page_flip(struct shmob_drm_crtc *scrtc, | ||
51 | struct drm_file *file); | ||
52 | void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc); | ||
53 | void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc); | ||
54 | void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc); | ||
55 | |||
56 | int shmob_drm_encoder_create(struct shmob_drm_device *sdev); | ||
57 | int shmob_drm_connector_create(struct shmob_drm_device *sdev, | ||
58 | struct drm_encoder *encoder); | ||
59 | |||
60 | #endif /* __SHMOB_DRM_CRTC_H__ */ | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c new file mode 100644 index 00000000000..c71d493fd0c --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /* | ||
2 | * shmob_drm_drv.c -- SH Mobile DRM driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/pm.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | #include <drm/drmP.h> | ||
23 | #include <drm/drm_crtc_helper.h> | ||
24 | #include <drm/drm_gem_cma_helper.h> | ||
25 | |||
26 | #include "shmob_drm_crtc.h" | ||
27 | #include "shmob_drm_drv.h" | ||
28 | #include "shmob_drm_kms.h" | ||
29 | #include "shmob_drm_plane.h" | ||
30 | #include "shmob_drm_regs.h" | ||
31 | |||
32 | /* ----------------------------------------------------------------------------- | ||
33 | * Hardware initialization | ||
34 | */ | ||
35 | |||
36 | static int __devinit shmob_drm_init_interface(struct shmob_drm_device *sdev) | ||
37 | { | ||
38 | static const u32 ldmt1r[] = { | ||
39 | [SHMOB_DRM_IFACE_RGB8] = LDMT1R_MIFTYP_RGB8, | ||
40 | [SHMOB_DRM_IFACE_RGB9] = LDMT1R_MIFTYP_RGB9, | ||
41 | [SHMOB_DRM_IFACE_RGB12A] = LDMT1R_MIFTYP_RGB12A, | ||
42 | [SHMOB_DRM_IFACE_RGB12B] = LDMT1R_MIFTYP_RGB12B, | ||
43 | [SHMOB_DRM_IFACE_RGB16] = LDMT1R_MIFTYP_RGB16, | ||
44 | [SHMOB_DRM_IFACE_RGB18] = LDMT1R_MIFTYP_RGB18, | ||
45 | [SHMOB_DRM_IFACE_RGB24] = LDMT1R_MIFTYP_RGB24, | ||
46 | [SHMOB_DRM_IFACE_YUV422] = LDMT1R_MIFTYP_YCBCR, | ||
47 | [SHMOB_DRM_IFACE_SYS8A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A, | ||
48 | [SHMOB_DRM_IFACE_SYS8B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B, | ||
49 | [SHMOB_DRM_IFACE_SYS8C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C, | ||
50 | [SHMOB_DRM_IFACE_SYS8D] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D, | ||
51 | [SHMOB_DRM_IFACE_SYS9] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9, | ||
52 | [SHMOB_DRM_IFACE_SYS12] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12, | ||
53 | [SHMOB_DRM_IFACE_SYS16A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A, | ||
54 | [SHMOB_DRM_IFACE_SYS16B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B, | ||
55 | [SHMOB_DRM_IFACE_SYS16C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C, | ||
56 | [SHMOB_DRM_IFACE_SYS18] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18, | ||
57 | [SHMOB_DRM_IFACE_SYS24] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24, | ||
58 | }; | ||
59 | |||
60 | if (sdev->pdata->iface.interface >= ARRAY_SIZE(ldmt1r)) { | ||
61 | dev_err(sdev->dev, "invalid interface type %u\n", | ||
62 | sdev->pdata->iface.interface); | ||
63 | return -EINVAL; | ||
64 | } | ||
65 | |||
66 | sdev->ldmt1r = ldmt1r[sdev->pdata->iface.interface]; | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static int __devinit shmob_drm_setup_clocks(struct shmob_drm_device *sdev, | ||
71 | enum shmob_drm_clk_source clksrc) | ||
72 | { | ||
73 | struct clk *clk; | ||
74 | char *clkname; | ||
75 | |||
76 | switch (clksrc) { | ||
77 | case SHMOB_DRM_CLK_BUS: | ||
78 | clkname = "bus_clk"; | ||
79 | sdev->lddckr = LDDCKR_ICKSEL_BUS; | ||
80 | break; | ||
81 | case SHMOB_DRM_CLK_PERIPHERAL: | ||
82 | clkname = "peripheral_clk"; | ||
83 | sdev->lddckr = LDDCKR_ICKSEL_MIPI; | ||
84 | break; | ||
85 | case SHMOB_DRM_CLK_EXTERNAL: | ||
86 | clkname = NULL; | ||
87 | sdev->lddckr = LDDCKR_ICKSEL_HDMI; | ||
88 | break; | ||
89 | default: | ||
90 | return -EINVAL; | ||
91 | } | ||
92 | |||
93 | clk = clk_get(sdev->dev, clkname); | ||
94 | if (IS_ERR(clk)) { | ||
95 | dev_err(sdev->dev, "cannot get dot clock %s\n", clkname); | ||
96 | return PTR_ERR(clk); | ||
97 | } | ||
98 | |||
99 | sdev->clock = clk; | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | /* ----------------------------------------------------------------------------- | ||
104 | * DRM operations | ||
105 | */ | ||
106 | |||
107 | static int shmob_drm_unload(struct drm_device *dev) | ||
108 | { | ||
109 | struct shmob_drm_device *sdev = dev->dev_private; | ||
110 | |||
111 | drm_kms_helper_poll_fini(dev); | ||
112 | drm_mode_config_cleanup(dev); | ||
113 | drm_vblank_cleanup(dev); | ||
114 | drm_irq_uninstall(dev); | ||
115 | |||
116 | if (sdev->clock) | ||
117 | clk_put(sdev->clock); | ||
118 | |||
119 | if (sdev->mmio) | ||
120 | iounmap(sdev->mmio); | ||
121 | |||
122 | dev->dev_private = NULL; | ||
123 | kfree(sdev); | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int shmob_drm_load(struct drm_device *dev, unsigned long flags) | ||
129 | { | ||
130 | struct shmob_drm_platform_data *pdata = dev->dev->platform_data; | ||
131 | struct platform_device *pdev = dev->platformdev; | ||
132 | struct shmob_drm_device *sdev; | ||
133 | struct resource *res; | ||
134 | unsigned int i; | ||
135 | int ret; | ||
136 | |||
137 | if (pdata == NULL) { | ||
138 | dev_err(dev->dev, "no platform data\n"); | ||
139 | return -EINVAL; | ||
140 | } | ||
141 | |||
142 | sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); | ||
143 | if (sdev == NULL) { | ||
144 | dev_err(dev->dev, "failed to allocate private data\n"); | ||
145 | return -ENOMEM; | ||
146 | } | ||
147 | |||
148 | sdev->dev = &pdev->dev; | ||
149 | sdev->pdata = pdata; | ||
150 | spin_lock_init(&sdev->irq_lock); | ||
151 | |||
152 | sdev->ddev = dev; | ||
153 | dev->dev_private = sdev; | ||
154 | |||
155 | /* I/O resources and clocks */ | ||
156 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
157 | if (res == NULL) { | ||
158 | dev_err(&pdev->dev, "failed to get memory resource\n"); | ||
159 | ret = -EINVAL; | ||
160 | goto done; | ||
161 | } | ||
162 | |||
163 | sdev->mmio = ioremap_nocache(res->start, resource_size(res)); | ||
164 | if (sdev->mmio == NULL) { | ||
165 | dev_err(&pdev->dev, "failed to remap memory resource\n"); | ||
166 | ret = -ENOMEM; | ||
167 | goto done; | ||
168 | } | ||
169 | |||
170 | ret = shmob_drm_setup_clocks(sdev, pdata->clk_source); | ||
171 | if (ret < 0) | ||
172 | goto done; | ||
173 | |||
174 | ret = shmob_drm_init_interface(sdev); | ||
175 | if (ret < 0) | ||
176 | goto done; | ||
177 | |||
178 | ret = shmob_drm_modeset_init(sdev); | ||
179 | if (ret < 0) { | ||
180 | dev_err(&pdev->dev, "failed to initialize mode setting\n"); | ||
181 | goto done; | ||
182 | } | ||
183 | |||
184 | for (i = 0; i < 4; ++i) { | ||
185 | ret = shmob_drm_plane_create(sdev, i); | ||
186 | if (ret < 0) { | ||
187 | dev_err(&pdev->dev, "failed to create plane %u\n", i); | ||
188 | goto done; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | ret = drm_vblank_init(dev, 1); | ||
193 | if (ret < 0) { | ||
194 | dev_err(&pdev->dev, "failed to initialize vblank\n"); | ||
195 | goto done; | ||
196 | } | ||
197 | |||
198 | ret = drm_irq_install(dev); | ||
199 | if (ret < 0) { | ||
200 | dev_err(&pdev->dev, "failed to install IRQ handler\n"); | ||
201 | goto done; | ||
202 | } | ||
203 | |||
204 | done: | ||
205 | if (ret) | ||
206 | shmob_drm_unload(dev); | ||
207 | |||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | static void shmob_drm_preclose(struct drm_device *dev, struct drm_file *file) | ||
212 | { | ||
213 | struct shmob_drm_device *sdev = dev->dev_private; | ||
214 | |||
215 | shmob_drm_crtc_cancel_page_flip(&sdev->crtc, file); | ||
216 | } | ||
217 | |||
218 | static irqreturn_t shmob_drm_irq(int irq, void *arg) | ||
219 | { | ||
220 | struct drm_device *dev = arg; | ||
221 | struct shmob_drm_device *sdev = dev->dev_private; | ||
222 | unsigned long flags; | ||
223 | u32 status; | ||
224 | |||
225 | /* Acknowledge interrupts. Putting interrupt enable and interrupt flag | ||
226 | * bits in the same register is really brain-dead design and requires | ||
227 | * taking a spinlock. | ||
228 | */ | ||
229 | spin_lock_irqsave(&sdev->irq_lock, flags); | ||
230 | status = lcdc_read(sdev, LDINTR); | ||
231 | lcdc_write(sdev, LDINTR, status ^ LDINTR_STATUS_MASK); | ||
232 | spin_unlock_irqrestore(&sdev->irq_lock, flags); | ||
233 | |||
234 | if (status & LDINTR_VES) { | ||
235 | drm_handle_vblank(dev, 0); | ||
236 | shmob_drm_crtc_finish_page_flip(&sdev->crtc); | ||
237 | } | ||
238 | |||
239 | return IRQ_HANDLED; | ||
240 | } | ||
241 | |||
242 | static int shmob_drm_enable_vblank(struct drm_device *dev, int crtc) | ||
243 | { | ||
244 | struct shmob_drm_device *sdev = dev->dev_private; | ||
245 | |||
246 | shmob_drm_crtc_enable_vblank(sdev, true); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static void shmob_drm_disable_vblank(struct drm_device *dev, int crtc) | ||
252 | { | ||
253 | struct shmob_drm_device *sdev = dev->dev_private; | ||
254 | |||
255 | shmob_drm_crtc_enable_vblank(sdev, false); | ||
256 | } | ||
257 | |||
258 | static const struct file_operations shmob_drm_fops = { | ||
259 | .owner = THIS_MODULE, | ||
260 | .open = drm_open, | ||
261 | .release = drm_release, | ||
262 | .unlocked_ioctl = drm_ioctl, | ||
263 | #ifdef CONFIG_COMPAT | ||
264 | .compat_ioctl = drm_compat_ioctl, | ||
265 | #endif | ||
266 | .poll = drm_poll, | ||
267 | .read = drm_read, | ||
268 | .fasync = drm_fasync, | ||
269 | .llseek = no_llseek, | ||
270 | .mmap = drm_gem_cma_mmap, | ||
271 | }; | ||
272 | |||
273 | static struct drm_driver shmob_drm_driver = { | ||
274 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, | ||
275 | .load = shmob_drm_load, | ||
276 | .unload = shmob_drm_unload, | ||
277 | .preclose = shmob_drm_preclose, | ||
278 | .irq_handler = shmob_drm_irq, | ||
279 | .get_vblank_counter = drm_vblank_count, | ||
280 | .enable_vblank = shmob_drm_enable_vblank, | ||
281 | .disable_vblank = shmob_drm_disable_vblank, | ||
282 | .gem_free_object = drm_gem_cma_free_object, | ||
283 | .gem_vm_ops = &drm_gem_cma_vm_ops, | ||
284 | .dumb_create = drm_gem_cma_dumb_create, | ||
285 | .dumb_map_offset = drm_gem_cma_dumb_map_offset, | ||
286 | .dumb_destroy = drm_gem_cma_dumb_destroy, | ||
287 | .fops = &shmob_drm_fops, | ||
288 | .name = "shmob-drm", | ||
289 | .desc = "Renesas SH Mobile DRM", | ||
290 | .date = "20120424", | ||
291 | .major = 1, | ||
292 | .minor = 0, | ||
293 | }; | ||
294 | |||
295 | /* ----------------------------------------------------------------------------- | ||
296 | * Power management | ||
297 | */ | ||
298 | |||
299 | #if CONFIG_PM_SLEEP | ||
300 | static int shmob_drm_pm_suspend(struct device *dev) | ||
301 | { | ||
302 | struct platform_device *pdev = to_platform_device(dev); | ||
303 | struct drm_device *ddev = platform_get_drvdata(pdev); | ||
304 | struct shmob_drm_device *sdev = ddev->dev_private; | ||
305 | |||
306 | drm_kms_helper_poll_disable(ddev); | ||
307 | shmob_drm_crtc_suspend(&sdev->crtc); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int shmob_drm_pm_resume(struct device *dev) | ||
313 | { | ||
314 | struct platform_device *pdev = to_platform_device(dev); | ||
315 | struct drm_device *ddev = platform_get_drvdata(pdev); | ||
316 | struct shmob_drm_device *sdev = ddev->dev_private; | ||
317 | |||
318 | mutex_lock(&sdev->ddev->mode_config.mutex); | ||
319 | shmob_drm_crtc_resume(&sdev->crtc); | ||
320 | mutex_unlock(&sdev->ddev->mode_config.mutex); | ||
321 | |||
322 | drm_kms_helper_poll_enable(sdev->ddev); | ||
323 | return 0; | ||
324 | } | ||
325 | #endif | ||
326 | |||
327 | static const struct dev_pm_ops shmob_drm_pm_ops = { | ||
328 | SET_SYSTEM_SLEEP_PM_OPS(shmob_drm_pm_suspend, shmob_drm_pm_resume) | ||
329 | }; | ||
330 | |||
331 | /* ----------------------------------------------------------------------------- | ||
332 | * Platform driver | ||
333 | */ | ||
334 | |||
335 | static int __devinit shmob_drm_probe(struct platform_device *pdev) | ||
336 | { | ||
337 | return drm_platform_init(&shmob_drm_driver, pdev); | ||
338 | } | ||
339 | |||
340 | static int __devexit shmob_drm_remove(struct platform_device *pdev) | ||
341 | { | ||
342 | drm_platform_exit(&shmob_drm_driver, pdev); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static struct platform_driver shmob_drm_platform_driver = { | ||
348 | .probe = shmob_drm_probe, | ||
349 | .remove = __devexit_p(shmob_drm_remove), | ||
350 | .driver = { | ||
351 | .owner = THIS_MODULE, | ||
352 | .name = "shmob-drm", | ||
353 | .pm = &shmob_drm_pm_ops, | ||
354 | }, | ||
355 | }; | ||
356 | |||
357 | module_platform_driver(shmob_drm_platform_driver); | ||
358 | |||
359 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
360 | MODULE_DESCRIPTION("Renesas SH Mobile DRM Driver"); | ||
361 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.h b/drivers/gpu/drm/shmobile/shmob_drm_drv.h new file mode 100644 index 00000000000..4d46b811b5a --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * shmob_drm.h -- SH Mobile DRM driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #ifndef __SHMOB_DRM_DRV_H__ | ||
15 | #define __SHMOB_DRM_DRV_H__ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/platform_data/shmob_drm.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | |||
21 | #include "shmob_drm_crtc.h" | ||
22 | |||
23 | struct clk; | ||
24 | struct device; | ||
25 | struct drm_device; | ||
26 | struct sh_mobile_meram_info; | ||
27 | |||
28 | struct shmob_drm_device { | ||
29 | struct device *dev; | ||
30 | const struct shmob_drm_platform_data *pdata; | ||
31 | |||
32 | void __iomem *mmio; | ||
33 | struct clk *clock; | ||
34 | struct sh_mobile_meram_info *meram; | ||
35 | u32 lddckr; | ||
36 | u32 ldmt1r; | ||
37 | |||
38 | spinlock_t irq_lock; /* Protects hardware LDINTR register */ | ||
39 | |||
40 | struct drm_device *ddev; | ||
41 | |||
42 | struct shmob_drm_crtc crtc; | ||
43 | struct shmob_drm_encoder encoder; | ||
44 | struct shmob_drm_connector connector; | ||
45 | }; | ||
46 | |||
47 | #endif /* __SHMOB_DRM_DRV_H__ */ | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.c b/drivers/gpu/drm/shmobile/shmob_drm_kms.c new file mode 100644 index 00000000000..c291ee385b4 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_kms.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * shmob_drm_kms.c -- SH Mobile DRM Mode Setting | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <drm/drmP.h> | ||
15 | #include <drm/drm_crtc.h> | ||
16 | #include <drm/drm_crtc_helper.h> | ||
17 | #include <drm/drm_fb_cma_helper.h> | ||
18 | #include <drm/drm_gem_cma_helper.h> | ||
19 | |||
20 | #include <video/sh_mobile_meram.h> | ||
21 | |||
22 | #include "shmob_drm_crtc.h" | ||
23 | #include "shmob_drm_drv.h" | ||
24 | #include "shmob_drm_kms.h" | ||
25 | #include "shmob_drm_regs.h" | ||
26 | |||
27 | /* ----------------------------------------------------------------------------- | ||
28 | * Format helpers | ||
29 | */ | ||
30 | |||
31 | static const struct shmob_drm_format_info shmob_drm_format_infos[] = { | ||
32 | { | ||
33 | .fourcc = DRM_FORMAT_RGB565, | ||
34 | .bpp = 16, | ||
35 | .yuv = false, | ||
36 | .lddfr = LDDFR_PKF_RGB16, | ||
37 | .meram = SH_MOBILE_MERAM_PF_RGB, | ||
38 | }, { | ||
39 | .fourcc = DRM_FORMAT_RGB888, | ||
40 | .bpp = 24, | ||
41 | .yuv = false, | ||
42 | .lddfr = LDDFR_PKF_RGB24, | ||
43 | .meram = SH_MOBILE_MERAM_PF_RGB, | ||
44 | }, { | ||
45 | .fourcc = DRM_FORMAT_ARGB8888, | ||
46 | .bpp = 32, | ||
47 | .yuv = false, | ||
48 | .lddfr = LDDFR_PKF_ARGB32, | ||
49 | .meram = SH_MOBILE_MERAM_PF_RGB, | ||
50 | }, { | ||
51 | .fourcc = DRM_FORMAT_NV12, | ||
52 | .bpp = 12, | ||
53 | .yuv = true, | ||
54 | .lddfr = LDDFR_CC | LDDFR_YF_420, | ||
55 | .meram = SH_MOBILE_MERAM_PF_NV, | ||
56 | }, { | ||
57 | .fourcc = DRM_FORMAT_NV21, | ||
58 | .bpp = 12, | ||
59 | .yuv = true, | ||
60 | .lddfr = LDDFR_CC | LDDFR_YF_420, | ||
61 | .meram = SH_MOBILE_MERAM_PF_NV, | ||
62 | }, { | ||
63 | .fourcc = DRM_FORMAT_NV16, | ||
64 | .bpp = 16, | ||
65 | .yuv = true, | ||
66 | .lddfr = LDDFR_CC | LDDFR_YF_422, | ||
67 | .meram = SH_MOBILE_MERAM_PF_NV, | ||
68 | }, { | ||
69 | .fourcc = DRM_FORMAT_NV61, | ||
70 | .bpp = 16, | ||
71 | .yuv = true, | ||
72 | .lddfr = LDDFR_CC | LDDFR_YF_422, | ||
73 | .meram = SH_MOBILE_MERAM_PF_NV, | ||
74 | }, { | ||
75 | .fourcc = DRM_FORMAT_NV24, | ||
76 | .bpp = 24, | ||
77 | .yuv = true, | ||
78 | .lddfr = LDDFR_CC | LDDFR_YF_444, | ||
79 | .meram = SH_MOBILE_MERAM_PF_NV24, | ||
80 | }, { | ||
81 | .fourcc = DRM_FORMAT_NV42, | ||
82 | .bpp = 24, | ||
83 | .yuv = true, | ||
84 | .lddfr = LDDFR_CC | LDDFR_YF_444, | ||
85 | .meram = SH_MOBILE_MERAM_PF_NV24, | ||
86 | }, | ||
87 | }; | ||
88 | |||
89 | const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc) | ||
90 | { | ||
91 | unsigned int i; | ||
92 | |||
93 | for (i = 0; i < ARRAY_SIZE(shmob_drm_format_infos); ++i) { | ||
94 | if (shmob_drm_format_infos[i].fourcc == fourcc) | ||
95 | return &shmob_drm_format_infos[i]; | ||
96 | } | ||
97 | |||
98 | return NULL; | ||
99 | } | ||
100 | |||
101 | /* ----------------------------------------------------------------------------- | ||
102 | * Frame buffer | ||
103 | */ | ||
104 | |||
105 | static struct drm_framebuffer * | ||
106 | shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv, | ||
107 | struct drm_mode_fb_cmd2 *mode_cmd) | ||
108 | { | ||
109 | const struct shmob_drm_format_info *format; | ||
110 | |||
111 | format = shmob_drm_format_info(mode_cmd->pixel_format); | ||
112 | if (format == NULL) { | ||
113 | dev_dbg(dev->dev, "unsupported pixel format %08x\n", | ||
114 | mode_cmd->pixel_format); | ||
115 | return ERR_PTR(-EINVAL); | ||
116 | } | ||
117 | |||
118 | if (mode_cmd->pitches[0] & 7 || mode_cmd->pitches[0] >= 65536) { | ||
119 | dev_dbg(dev->dev, "valid pitch value %u\n", | ||
120 | mode_cmd->pitches[0]); | ||
121 | return ERR_PTR(-EINVAL); | ||
122 | } | ||
123 | |||
124 | if (format->yuv) { | ||
125 | unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1; | ||
126 | |||
127 | if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) { | ||
128 | dev_dbg(dev->dev, | ||
129 | "luma and chroma pitches do not match\n"); | ||
130 | return ERR_PTR(-EINVAL); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return drm_fb_cma_create(dev, file_priv, mode_cmd); | ||
135 | } | ||
136 | |||
137 | static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = { | ||
138 | .fb_create = shmob_drm_fb_create, | ||
139 | }; | ||
140 | |||
141 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev) | ||
142 | { | ||
143 | drm_mode_config_init(sdev->ddev); | ||
144 | |||
145 | shmob_drm_crtc_create(sdev); | ||
146 | shmob_drm_encoder_create(sdev); | ||
147 | shmob_drm_connector_create(sdev, &sdev->encoder.encoder); | ||
148 | |||
149 | drm_kms_helper_poll_init(sdev->ddev); | ||
150 | |||
151 | sdev->ddev->mode_config.min_width = 0; | ||
152 | sdev->ddev->mode_config.min_height = 0; | ||
153 | sdev->ddev->mode_config.max_width = 4095; | ||
154 | sdev->ddev->mode_config.max_height = 4095; | ||
155 | sdev->ddev->mode_config.funcs = &shmob_drm_mode_config_funcs; | ||
156 | |||
157 | drm_helper_disable_unused_functions(sdev->ddev); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.h b/drivers/gpu/drm/shmobile/shmob_drm_kms.h new file mode 100644 index 00000000000..9495c911130 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_kms.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * shmob_drm_kms.h -- SH Mobile DRM Mode Setting | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #ifndef __SHMOB_DRM_KMS_H__ | ||
15 | #define __SHMOB_DRM_KMS_H__ | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | |||
19 | struct drm_gem_cma_object; | ||
20 | struct shmob_drm_device; | ||
21 | |||
22 | struct shmob_drm_format_info { | ||
23 | u32 fourcc; | ||
24 | unsigned int bpp; | ||
25 | bool yuv; | ||
26 | u32 lddfr; | ||
27 | unsigned int meram; | ||
28 | }; | ||
29 | |||
30 | const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc); | ||
31 | |||
32 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev); | ||
33 | |||
34 | #endif /* __SHMOB_DRM_KMS_H__ */ | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c new file mode 100644 index 00000000000..e1eb899b028 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* | ||
2 | * shmob_drm_plane.c -- SH Mobile DRM Planes | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <drm/drmP.h> | ||
15 | #include <drm/drm_crtc.h> | ||
16 | #include <drm/drm_crtc_helper.h> | ||
17 | #include <drm/drm_fb_cma_helper.h> | ||
18 | #include <drm/drm_gem_cma_helper.h> | ||
19 | |||
20 | #include <video/sh_mobile_meram.h> | ||
21 | |||
22 | #include "shmob_drm_drv.h" | ||
23 | #include "shmob_drm_kms.h" | ||
24 | #include "shmob_drm_plane.h" | ||
25 | #include "shmob_drm_regs.h" | ||
26 | |||
27 | struct shmob_drm_plane { | ||
28 | struct drm_plane plane; | ||
29 | unsigned int index; | ||
30 | unsigned int alpha; | ||
31 | |||
32 | const struct shmob_drm_format_info *format; | ||
33 | unsigned long dma[2]; | ||
34 | |||
35 | unsigned int src_x; | ||
36 | unsigned int src_y; | ||
37 | unsigned int crtc_x; | ||
38 | unsigned int crtc_y; | ||
39 | unsigned int crtc_w; | ||
40 | unsigned int crtc_h; | ||
41 | }; | ||
42 | |||
43 | #define to_shmob_plane(p) container_of(p, struct shmob_drm_plane, plane) | ||
44 | |||
45 | static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane, | ||
46 | struct drm_framebuffer *fb, | ||
47 | int x, int y) | ||
48 | { | ||
49 | struct drm_gem_cma_object *gem; | ||
50 | unsigned int bpp; | ||
51 | |||
52 | bpp = splane->format->yuv ? 8 : splane->format->bpp; | ||
53 | gem = drm_fb_cma_get_gem_obj(fb, 0); | ||
54 | splane->dma[0] = gem->paddr + fb->offsets[0] | ||
55 | + y * fb->pitches[0] + x * bpp / 8; | ||
56 | |||
57 | if (splane->format->yuv) { | ||
58 | bpp = splane->format->bpp - 8; | ||
59 | gem = drm_fb_cma_get_gem_obj(fb, 1); | ||
60 | splane->dma[1] = gem->paddr + fb->offsets[1] | ||
61 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] | ||
62 | + x * (bpp == 16 ? 2 : 1); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, | ||
67 | struct drm_framebuffer *fb) | ||
68 | { | ||
69 | struct shmob_drm_device *sdev = splane->plane.dev->dev_private; | ||
70 | u32 format; | ||
71 | |||
72 | /* TODO: Support ROP3 mode */ | ||
73 | format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT); | ||
74 | |||
75 | switch (splane->format->fourcc) { | ||
76 | case DRM_FORMAT_RGB565: | ||
77 | case DRM_FORMAT_NV21: | ||
78 | case DRM_FORMAT_NV61: | ||
79 | case DRM_FORMAT_NV42: | ||
80 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; | ||
81 | break; | ||
82 | case DRM_FORMAT_RGB888: | ||
83 | case DRM_FORMAT_NV12: | ||
84 | case DRM_FORMAT_NV16: | ||
85 | case DRM_FORMAT_NV24: | ||
86 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; | ||
87 | break; | ||
88 | case DRM_FORMAT_ARGB8888: | ||
89 | default: | ||
90 | format |= LDBBSIFR_SWPL; | ||
91 | break; | ||
92 | } | ||
93 | |||
94 | switch (splane->format->fourcc) { | ||
95 | case DRM_FORMAT_RGB565: | ||
96 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; | ||
97 | break; | ||
98 | case DRM_FORMAT_RGB888: | ||
99 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; | ||
100 | break; | ||
101 | case DRM_FORMAT_ARGB8888: | ||
102 | format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; | ||
103 | break; | ||
104 | case DRM_FORMAT_NV12: | ||
105 | case DRM_FORMAT_NV21: | ||
106 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; | ||
107 | break; | ||
108 | case DRM_FORMAT_NV16: | ||
109 | case DRM_FORMAT_NV61: | ||
110 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; | ||
111 | break; | ||
112 | case DRM_FORMAT_NV24: | ||
113 | case DRM_FORMAT_NV42: | ||
114 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; | ||
115 | break; | ||
116 | } | ||
117 | |||
118 | #define plane_reg_dump(sdev, splane, reg) \ | ||
119 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \ | ||
120 | splane->index, #reg, \ | ||
121 | lcdc_read(sdev, reg(splane->index)), \ | ||
122 | lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET)) | ||
123 | |||
124 | plane_reg_dump(sdev, splane, LDBnBSIFR); | ||
125 | plane_reg_dump(sdev, splane, LDBnBSSZR); | ||
126 | plane_reg_dump(sdev, splane, LDBnBLOCR); | ||
127 | plane_reg_dump(sdev, splane, LDBnBSMWR); | ||
128 | plane_reg_dump(sdev, splane, LDBnBSAYR); | ||
129 | plane_reg_dump(sdev, splane, LDBnBSACR); | ||
130 | |||
131 | lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); | ||
132 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | ||
133 | "LDBCR", lcdc_read(sdev, LDBCR)); | ||
134 | |||
135 | lcdc_write(sdev, LDBnBSIFR(splane->index), format); | ||
136 | |||
137 | lcdc_write(sdev, LDBnBSSZR(splane->index), | ||
138 | (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) | | ||
139 | (splane->crtc_w << LDBBSSZR_BHSS_SHIFT)); | ||
140 | lcdc_write(sdev, LDBnBLOCR(splane->index), | ||
141 | (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) | | ||
142 | (splane->crtc_x << LDBBLOCR_CHLC_SHIFT)); | ||
143 | lcdc_write(sdev, LDBnBSMWR(splane->index), | ||
144 | fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); | ||
145 | |||
146 | shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y); | ||
147 | |||
148 | lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]); | ||
149 | if (splane->format->yuv) | ||
150 | lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]); | ||
151 | |||
152 | lcdc_write(sdev, LDBCR, | ||
153 | LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); | ||
154 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | ||
155 | "LDBCR", lcdc_read(sdev, LDBCR)); | ||
156 | |||
157 | plane_reg_dump(sdev, splane, LDBnBSIFR); | ||
158 | plane_reg_dump(sdev, splane, LDBnBSSZR); | ||
159 | plane_reg_dump(sdev, splane, LDBnBLOCR); | ||
160 | plane_reg_dump(sdev, splane, LDBnBSMWR); | ||
161 | plane_reg_dump(sdev, splane, LDBnBSAYR); | ||
162 | plane_reg_dump(sdev, splane, LDBnBSACR); | ||
163 | } | ||
164 | |||
165 | void shmob_drm_plane_setup(struct drm_plane *plane) | ||
166 | { | ||
167 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
168 | |||
169 | if (plane->fb == NULL || !plane->enabled) | ||
170 | return; | ||
171 | |||
172 | __shmob_drm_plane_setup(splane, plane->fb); | ||
173 | } | ||
174 | |||
175 | static int | ||
176 | shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | ||
177 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
178 | unsigned int crtc_w, unsigned int crtc_h, | ||
179 | uint32_t src_x, uint32_t src_y, | ||
180 | uint32_t src_w, uint32_t src_h) | ||
181 | { | ||
182 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
183 | struct shmob_drm_device *sdev = plane->dev->dev_private; | ||
184 | const struct shmob_drm_format_info *format; | ||
185 | |||
186 | format = shmob_drm_format_info(fb->pixel_format); | ||
187 | if (format == NULL) { | ||
188 | dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n", | ||
189 | fb->pixel_format); | ||
190 | return -EINVAL; | ||
191 | } | ||
192 | |||
193 | if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { | ||
194 | dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__); | ||
195 | return -EINVAL; | ||
196 | } | ||
197 | |||
198 | splane->format = format; | ||
199 | |||
200 | splane->src_x = src_x >> 16; | ||
201 | splane->src_y = src_y >> 16; | ||
202 | splane->crtc_x = crtc_x; | ||
203 | splane->crtc_y = crtc_y; | ||
204 | splane->crtc_w = crtc_w; | ||
205 | splane->crtc_h = crtc_h; | ||
206 | |||
207 | __shmob_drm_plane_setup(splane, fb); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int shmob_drm_plane_disable(struct drm_plane *plane) | ||
212 | { | ||
213 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
214 | struct shmob_drm_device *sdev = plane->dev->dev_private; | ||
215 | |||
216 | splane->format = NULL; | ||
217 | |||
218 | lcdc_write(sdev, LDBnBSIFR(splane->index), 0); | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static void shmob_drm_plane_destroy(struct drm_plane *plane) | ||
223 | { | ||
224 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
225 | |||
226 | shmob_drm_plane_disable(plane); | ||
227 | drm_plane_cleanup(plane); | ||
228 | kfree(splane); | ||
229 | } | ||
230 | |||
231 | static const struct drm_plane_funcs shmob_drm_plane_funcs = { | ||
232 | .update_plane = shmob_drm_plane_update, | ||
233 | .disable_plane = shmob_drm_plane_disable, | ||
234 | .destroy = shmob_drm_plane_destroy, | ||
235 | }; | ||
236 | |||
237 | static const uint32_t formats[] = { | ||
238 | DRM_FORMAT_RGB565, | ||
239 | DRM_FORMAT_RGB888, | ||
240 | DRM_FORMAT_ARGB8888, | ||
241 | DRM_FORMAT_NV12, | ||
242 | DRM_FORMAT_NV21, | ||
243 | DRM_FORMAT_NV16, | ||
244 | DRM_FORMAT_NV61, | ||
245 | DRM_FORMAT_NV24, | ||
246 | DRM_FORMAT_NV42, | ||
247 | }; | ||
248 | |||
249 | int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index) | ||
250 | { | ||
251 | struct shmob_drm_plane *splane; | ||
252 | int ret; | ||
253 | |||
254 | splane = kzalloc(sizeof(*splane), GFP_KERNEL); | ||
255 | if (splane == NULL) | ||
256 | return -ENOMEM; | ||
257 | |||
258 | splane->index = index; | ||
259 | splane->alpha = 255; | ||
260 | |||
261 | ret = drm_plane_init(sdev->ddev, &splane->plane, 1, | ||
262 | &shmob_drm_plane_funcs, formats, | ||
263 | ARRAY_SIZE(formats), false); | ||
264 | if (ret < 0) | ||
265 | kfree(splane); | ||
266 | |||
267 | return ret; | ||
268 | } | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.h b/drivers/gpu/drm/shmobile/shmob_drm_plane.h new file mode 100644 index 00000000000..99623d05e3b --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * shmob_drm_plane.h -- SH Mobile DRM Planes | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #ifndef __SHMOB_DRM_PLANE_H__ | ||
15 | #define __SHMOB_DRM_PLANE_H__ | ||
16 | |||
17 | struct shmob_drm_device; | ||
18 | |||
19 | int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index); | ||
20 | void shmob_drm_plane_setup(struct drm_plane *plane); | ||
21 | |||
22 | #endif /* __SHMOB_DRM_PLANE_H__ */ | ||
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_regs.h b/drivers/gpu/drm/shmobile/shmob_drm_regs.h new file mode 100644 index 00000000000..7923cdd6368 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_regs.h | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * shmob_drm_regs.h -- SH Mobile DRM registers | ||
3 | * | ||
4 | * Copyright (C) 2012 Renesas Corporation | ||
5 | * | ||
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #ifndef __SHMOB_DRM_REGS_H__ | ||
15 | #define __SHMOB_DRM_REGS_H__ | ||
16 | |||
17 | #include <linux/io.h> | ||
18 | |||
19 | /* Register definitions */ | ||
20 | #define LDDCKPAT1R 0x400 | ||
21 | #define LDDCKPAT2R 0x404 | ||
22 | #define LDDCKR 0x410 | ||
23 | #define LDDCKR_ICKSEL_BUS (0 << 16) | ||
24 | #define LDDCKR_ICKSEL_MIPI (1 << 16) | ||
25 | #define LDDCKR_ICKSEL_HDMI (2 << 16) | ||
26 | #define LDDCKR_ICKSEL_EXT (3 << 16) | ||
27 | #define LDDCKR_ICKSEL_MASK (7 << 16) | ||
28 | #define LDDCKR_MOSEL (1 << 6) | ||
29 | #define LDDCKSTPR 0x414 | ||
30 | #define LDDCKSTPR_DCKSTS (1 << 16) | ||
31 | #define LDDCKSTPR_DCKSTP (1 << 0) | ||
32 | #define LDMT1R 0x418 | ||
33 | #define LDMT1R_VPOL (1 << 28) | ||
34 | #define LDMT1R_HPOL (1 << 27) | ||
35 | #define LDMT1R_DWPOL (1 << 26) | ||
36 | #define LDMT1R_DIPOL (1 << 25) | ||
37 | #define LDMT1R_DAPOL (1 << 24) | ||
38 | #define LDMT1R_HSCNT (1 << 17) | ||
39 | #define LDMT1R_DWCNT (1 << 16) | ||
40 | #define LDMT1R_IFM (1 << 12) | ||
41 | #define LDMT1R_MIFTYP_RGB8 (0x0 << 0) | ||
42 | #define LDMT1R_MIFTYP_RGB9 (0x4 << 0) | ||
43 | #define LDMT1R_MIFTYP_RGB12A (0x5 << 0) | ||
44 | #define LDMT1R_MIFTYP_RGB12B (0x6 << 0) | ||
45 | #define LDMT1R_MIFTYP_RGB16 (0x7 << 0) | ||
46 | #define LDMT1R_MIFTYP_RGB18 (0xa << 0) | ||
47 | #define LDMT1R_MIFTYP_RGB24 (0xb << 0) | ||
48 | #define LDMT1R_MIFTYP_YCBCR (0xf << 0) | ||
49 | #define LDMT1R_MIFTYP_SYS8A (0x0 << 0) | ||
50 | #define LDMT1R_MIFTYP_SYS8B (0x1 << 0) | ||
51 | #define LDMT1R_MIFTYP_SYS8C (0x2 << 0) | ||
52 | #define LDMT1R_MIFTYP_SYS8D (0x3 << 0) | ||
53 | #define LDMT1R_MIFTYP_SYS9 (0x4 << 0) | ||
54 | #define LDMT1R_MIFTYP_SYS12 (0x5 << 0) | ||
55 | #define LDMT1R_MIFTYP_SYS16A (0x7 << 0) | ||
56 | #define LDMT1R_MIFTYP_SYS16B (0x8 << 0) | ||
57 | #define LDMT1R_MIFTYP_SYS16C (0x9 << 0) | ||
58 | #define LDMT1R_MIFTYP_SYS18 (0xa << 0) | ||
59 | #define LDMT1R_MIFTYP_SYS24 (0xb << 0) | ||
60 | #define LDMT1R_MIFTYP_MASK (0xf << 0) | ||
61 | #define LDMT2R 0x41c | ||
62 | #define LDMT2R_CSUP_MASK (7 << 26) | ||
63 | #define LDMT2R_CSUP_SHIFT 26 | ||
64 | #define LDMT2R_RSV (1 << 25) | ||
65 | #define LDMT2R_VSEL (1 << 24) | ||
66 | #define LDMT2R_WCSC_MASK (0xff << 16) | ||
67 | #define LDMT2R_WCSC_SHIFT 16 | ||
68 | #define LDMT2R_WCEC_MASK (0xff << 8) | ||
69 | #define LDMT2R_WCEC_SHIFT 8 | ||
70 | #define LDMT2R_WCLW_MASK (0xff << 0) | ||
71 | #define LDMT2R_WCLW_SHIFT 0 | ||
72 | #define LDMT3R 0x420 | ||
73 | #define LDMT3R_RDLC_MASK (0x3f << 24) | ||
74 | #define LDMT3R_RDLC_SHIFT 24 | ||
75 | #define LDMT3R_RCSC_MASK (0xff << 16) | ||
76 | #define LDMT3R_RCSC_SHIFT 16 | ||
77 | #define LDMT3R_RCEC_MASK (0xff << 8) | ||
78 | #define LDMT3R_RCEC_SHIFT 8 | ||
79 | #define LDMT3R_RCLW_MASK (0xff << 0) | ||
80 | #define LDMT3R_RCLW_SHIFT 0 | ||
81 | #define LDDFR 0x424 | ||
82 | #define LDDFR_CF1 (1 << 18) | ||
83 | #define LDDFR_CF0 (1 << 17) | ||
84 | #define LDDFR_CC (1 << 16) | ||
85 | #define LDDFR_YF_420 (0 << 8) | ||
86 | #define LDDFR_YF_422 (1 << 8) | ||
87 | #define LDDFR_YF_444 (2 << 8) | ||
88 | #define LDDFR_YF_MASK (3 << 8) | ||
89 | #define LDDFR_PKF_ARGB32 (0x00 << 0) | ||
90 | #define LDDFR_PKF_RGB16 (0x03 << 0) | ||
91 | #define LDDFR_PKF_RGB24 (0x0b << 0) | ||
92 | #define LDDFR_PKF_MASK (0x1f << 0) | ||
93 | #define LDSM1R 0x428 | ||
94 | #define LDSM1R_OS (1 << 0) | ||
95 | #define LDSM2R 0x42c | ||
96 | #define LDSM2R_OSTRG (1 << 0) | ||
97 | #define LDSA1R 0x430 | ||
98 | #define LDSA2R 0x434 | ||
99 | #define LDMLSR 0x438 | ||
100 | #define LDWBFR 0x43c | ||
101 | #define LDWBCNTR 0x440 | ||
102 | #define LDWBAR 0x444 | ||
103 | #define LDHCNR 0x448 | ||
104 | #define LDHSYNR 0x44c | ||
105 | #define LDVLNR 0x450 | ||
106 | #define LDVSYNR 0x454 | ||
107 | #define LDHPDR 0x458 | ||
108 | #define LDVPDR 0x45c | ||
109 | #define LDPMR 0x460 | ||
110 | #define LDPMR_LPS (3 << 0) | ||
111 | #define LDINTR 0x468 | ||
112 | #define LDINTR_FE (1 << 10) | ||
113 | #define LDINTR_VSE (1 << 9) | ||
114 | #define LDINTR_VEE (1 << 8) | ||
115 | #define LDINTR_FS (1 << 2) | ||
116 | #define LDINTR_VSS (1 << 1) | ||
117 | #define LDINTR_VES (1 << 0) | ||
118 | #define LDINTR_STATUS_MASK (0xff << 0) | ||
119 | #define LDSR 0x46c | ||
120 | #define LDSR_MSS (1 << 10) | ||
121 | #define LDSR_MRS (1 << 8) | ||
122 | #define LDSR_AS (1 << 1) | ||
123 | #define LDCNT1R 0x470 | ||
124 | #define LDCNT1R_DE (1 << 0) | ||
125 | #define LDCNT2R 0x474 | ||
126 | #define LDCNT2R_BR (1 << 8) | ||
127 | #define LDCNT2R_MD (1 << 3) | ||
128 | #define LDCNT2R_SE (1 << 2) | ||
129 | #define LDCNT2R_ME (1 << 1) | ||
130 | #define LDCNT2R_DO (1 << 0) | ||
131 | #define LDRCNTR 0x478 | ||
132 | #define LDRCNTR_SRS (1 << 17) | ||
133 | #define LDRCNTR_SRC (1 << 16) | ||
134 | #define LDRCNTR_MRS (1 << 1) | ||
135 | #define LDRCNTR_MRC (1 << 0) | ||
136 | #define LDDDSR 0x47c | ||
137 | #define LDDDSR_LS (1 << 2) | ||
138 | #define LDDDSR_WS (1 << 1) | ||
139 | #define LDDDSR_BS (1 << 0) | ||
140 | #define LDHAJR 0x4a0 | ||
141 | |||
142 | #define LDDWD0R 0x800 | ||
143 | #define LDDWDxR_WDACT (1 << 28) | ||
144 | #define LDDWDxR_RSW (1 << 24) | ||
145 | #define LDDRDR 0x840 | ||
146 | #define LDDRDR_RSR (1 << 24) | ||
147 | #define LDDRDR_DRD_MASK (0x3ffff << 0) | ||
148 | #define LDDWAR 0x900 | ||
149 | #define LDDWAR_WA (1 << 0) | ||
150 | #define LDDRAR 0x904 | ||
151 | #define LDDRAR_RA (1 << 0) | ||
152 | |||
153 | #define LDBCR 0xb00 | ||
154 | #define LDBCR_UPC(n) (1 << ((n) + 16)) | ||
155 | #define LDBCR_UPF(n) (1 << ((n) + 8)) | ||
156 | #define LDBCR_UPD(n) (1 << ((n) + 0)) | ||
157 | #define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00) | ||
158 | #define LDBBSIFR_EN (1 << 31) | ||
159 | #define LDBBSIFR_VS (1 << 29) | ||
160 | #define LDBBSIFR_BRSEL (1 << 28) | ||
161 | #define LDBBSIFR_MX (1 << 27) | ||
162 | #define LDBBSIFR_MY (1 << 26) | ||
163 | #define LDBBSIFR_CV3 (3 << 24) | ||
164 | #define LDBBSIFR_CV2 (2 << 24) | ||
165 | #define LDBBSIFR_CV1 (1 << 24) | ||
166 | #define LDBBSIFR_CV0 (0 << 24) | ||
167 | #define LDBBSIFR_CV_MASK (3 << 24) | ||
168 | #define LDBBSIFR_LAY_MASK (0xff << 16) | ||
169 | #define LDBBSIFR_LAY_SHIFT 16 | ||
170 | #define LDBBSIFR_ROP3_MASK (0xff << 16) | ||
171 | #define LDBBSIFR_ROP3_SHIFT 16 | ||
172 | #define LDBBSIFR_AL_PL8 (3 << 14) | ||
173 | #define LDBBSIFR_AL_PL1 (2 << 14) | ||
174 | #define LDBBSIFR_AL_PK (1 << 14) | ||
175 | #define LDBBSIFR_AL_1 (0 << 14) | ||
176 | #define LDBBSIFR_AL_MASK (3 << 14) | ||
177 | #define LDBBSIFR_SWPL (1 << 10) | ||
178 | #define LDBBSIFR_SWPW (1 << 9) | ||
179 | #define LDBBSIFR_SWPB (1 << 8) | ||
180 | #define LDBBSIFR_RY (1 << 7) | ||
181 | #define LDBBSIFR_CHRR_420 (2 << 0) | ||
182 | #define LDBBSIFR_CHRR_422 (1 << 0) | ||
183 | #define LDBBSIFR_CHRR_444 (0 << 0) | ||
184 | #define LDBBSIFR_RPKF_ARGB32 (0x00 << 0) | ||
185 | #define LDBBSIFR_RPKF_RGB16 (0x03 << 0) | ||
186 | #define LDBBSIFR_RPKF_RGB24 (0x0b << 0) | ||
187 | #define LDBBSIFR_RPKF_MASK (0x1f << 0) | ||
188 | #define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04) | ||
189 | #define LDBBSSZR_BVSS_MASK (0xfff << 16) | ||
190 | #define LDBBSSZR_BVSS_SHIFT 16 | ||
191 | #define LDBBSSZR_BHSS_MASK (0xfff << 0) | ||
192 | #define LDBBSSZR_BHSS_SHIFT 0 | ||
193 | #define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08) | ||
194 | #define LDBBLOCR_CVLC_MASK (0xfff << 16) | ||
195 | #define LDBBLOCR_CVLC_SHIFT 16 | ||
196 | #define LDBBLOCR_CHLC_MASK (0xfff << 0) | ||
197 | #define LDBBLOCR_CHLC_SHIFT 0 | ||
198 | #define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c) | ||
199 | #define LDBBSMWR_BSMWA_MASK (0xffff << 16) | ||
200 | #define LDBBSMWR_BSMWA_SHIFT 16 | ||
201 | #define LDBBSMWR_BSMW_MASK (0xffff << 0) | ||
202 | #define LDBBSMWR_BSMW_SHIFT 0 | ||
203 | #define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10) | ||
204 | #define LDBBSAYR_FG1A_MASK (0xff << 24) | ||
205 | #define LDBBSAYR_FG1A_SHIFT 24 | ||
206 | #define LDBBSAYR_FG1R_MASK (0xff << 16) | ||
207 | #define LDBBSAYR_FG1R_SHIFT 16 | ||
208 | #define LDBBSAYR_FG1G_MASK (0xff << 8) | ||
209 | #define LDBBSAYR_FG1G_SHIFT 8 | ||
210 | #define LDBBSAYR_FG1B_MASK (0xff << 0) | ||
211 | #define LDBBSAYR_FG1B_SHIFT 0 | ||
212 | #define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14) | ||
213 | #define LDBBSACR_FG2A_MASK (0xff << 24) | ||
214 | #define LDBBSACR_FG2A_SHIFT 24 | ||
215 | #define LDBBSACR_FG2R_MASK (0xff << 16) | ||
216 | #define LDBBSACR_FG2R_SHIFT 16 | ||
217 | #define LDBBSACR_FG2G_MASK (0xff << 8) | ||
218 | #define LDBBSACR_FG2G_SHIFT 8 | ||
219 | #define LDBBSACR_FG2B_MASK (0xff << 0) | ||
220 | #define LDBBSACR_FG2B_SHIFT 0 | ||
221 | #define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18) | ||
222 | #define LDBBSAAR_AP_MASK (0xff << 24) | ||
223 | #define LDBBSAAR_AP_SHIFT 24 | ||
224 | #define LDBBSAAR_R_MASK (0xff << 16) | ||
225 | #define LDBBSAAR_R_SHIFT 16 | ||
226 | #define LDBBSAAR_GY_MASK (0xff << 8) | ||
227 | #define LDBBSAAR_GY_SHIFT 8 | ||
228 | #define LDBBSAAR_B_MASK (0xff << 0) | ||
229 | #define LDBBSAAR_B_SHIFT 0 | ||
230 | #define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c) | ||
231 | #define LDBBPPCR_AP_MASK (0xff << 24) | ||
232 | #define LDBBPPCR_AP_SHIFT 24 | ||
233 | #define LDBBPPCR_R_MASK (0xff << 16) | ||
234 | #define LDBBPPCR_R_SHIFT 16 | ||
235 | #define LDBBPPCR_GY_MASK (0xff << 8) | ||
236 | #define LDBBPPCR_GY_SHIFT 8 | ||
237 | #define LDBBPPCR_B_MASK (0xff << 0) | ||
238 | #define LDBBPPCR_B_SHIFT 0 | ||
239 | #define LDBnBBGCL(n) (0xb10 + (n) * 0x04) | ||
240 | #define LDBBBGCL_BGA_MASK (0xff << 24) | ||
241 | #define LDBBBGCL_BGA_SHIFT 24 | ||
242 | #define LDBBBGCL_BGR_MASK (0xff << 16) | ||
243 | #define LDBBBGCL_BGR_SHIFT 16 | ||
244 | #define LDBBBGCL_BGG_MASK (0xff << 8) | ||
245 | #define LDBBBGCL_BGG_SHIFT 8 | ||
246 | #define LDBBBGCL_BGB_MASK (0xff << 0) | ||
247 | #define LDBBBGCL_BGB_SHIFT 0 | ||
248 | |||
249 | #define LCDC_SIDE_B_OFFSET 0x1000 | ||
250 | #define LCDC_MIRROR_OFFSET 0x2000 | ||
251 | |||
252 | static inline bool lcdc_is_banked(u32 reg) | ||
253 | { | ||
254 | switch (reg) { | ||
255 | case LDMT1R: | ||
256 | case LDMT2R: | ||
257 | case LDMT3R: | ||
258 | case LDDFR: | ||
259 | case LDSM1R: | ||
260 | case LDSA1R: | ||
261 | case LDSA2R: | ||
262 | case LDMLSR: | ||
263 | case LDWBFR: | ||
264 | case LDWBCNTR: | ||
265 | case LDWBAR: | ||
266 | case LDHCNR: | ||
267 | case LDHSYNR: | ||
268 | case LDVLNR: | ||
269 | case LDVSYNR: | ||
270 | case LDHPDR: | ||
271 | case LDVPDR: | ||
272 | case LDHAJR: | ||
273 | return true; | ||
274 | default: | ||
275 | return reg >= LDBnBBGCL(0) && reg <= LDBnBPPCR(3); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | static inline void lcdc_write_mirror(struct shmob_drm_device *sdev, u32 reg, | ||
280 | u32 data) | ||
281 | { | ||
282 | iowrite32(data, sdev->mmio + reg + LCDC_MIRROR_OFFSET); | ||
283 | } | ||
284 | |||
285 | static inline void lcdc_write(struct shmob_drm_device *sdev, u32 reg, u32 data) | ||
286 | { | ||
287 | iowrite32(data, sdev->mmio + reg); | ||
288 | if (lcdc_is_banked(reg)) | ||
289 | iowrite32(data, sdev->mmio + reg + LCDC_SIDE_B_OFFSET); | ||
290 | } | ||
291 | |||
292 | static inline u32 lcdc_read(struct shmob_drm_device *sdev, u32 reg) | ||
293 | { | ||
294 | return ioread32(sdev->mmio + reg); | ||
295 | } | ||
296 | |||
297 | static inline int lcdc_wait_bit(struct shmob_drm_device *sdev, u32 reg, | ||
298 | u32 mask, u32 until) | ||
299 | { | ||
300 | unsigned long timeout = jiffies + msecs_to_jiffies(5); | ||
301 | |||
302 | while ((lcdc_read(sdev, reg) & mask) != until) { | ||
303 | if (time_after(jiffies, timeout)) | ||
304 | return -ETIMEDOUT; | ||
305 | cpu_relax(); | ||
306 | } | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | #endif /* __SHMOB_DRM_REGS_H__ */ | ||