Skip to content

Commit 8b9f089

Browse files
committed
Merge git://linux-arm.org/linux-ld into drm-next
Pull upstream HDLCD driver. * git://linux-arm.org/linux-ld: MAINTAINERS: Add Liviu Dudau as maintainer for ARM HDLCD driver. drm: Add support for ARM's HDLCD controller.
2 parents 4102a9e + c5a906a commit 8b9f089

File tree

9 files changed

+1044
-0
lines changed

9 files changed

+1044
-0
lines changed

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,12 @@ S: Maintained
827827
F: drivers/net/arcnet/
828828
F: include/uapi/linux/if_arcnet.h
829829

830+
ARM HDLCD DRM DRIVER
831+
M: Liviu Dudau <liviu.dudau@arm.com>
832+
S: Supported
833+
F: drivers/gpu/drm/arm/
834+
F: Documentation/devicetree/bindings/display/arm,hdlcd.txt
835+
830836
ARM MFM AND FLOPPY DRIVERS
831837
M: Ian Molton <spyro@f2s.com>
832838
S: Maintained

drivers/gpu/drm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ config DRM_TDFX
106106
Choose this option if you have a 3dfx Banshee or Voodoo3 (or later),
107107
graphics card. If M is selected, the module will be called tdfx.
108108

109+
source "drivers/gpu/drm/arm/Kconfig"
110+
109111
config DRM_R128
110112
tristate "ATI Rage 128"
111113
depends on DRM && PCI

drivers/gpu/drm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ CFLAGS_drm_trace_points.o := -I$(src)
3333

3434
obj-$(CONFIG_DRM) += drm.o
3535
obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
36+
obj-$(CONFIG_DRM_ARM) += arm/
3637
obj-$(CONFIG_DRM_TTM) += ttm/
3738
obj-$(CONFIG_DRM_TDFX) += tdfx/
3839
obj-$(CONFIG_DRM_R128) += r128/

drivers/gpu/drm/arm/Kconfig

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
config DRM_ARM
2+
bool
3+
help
4+
Choose this option to select drivers for ARM's devices
5+
6+
config DRM_HDLCD
7+
tristate "ARM HDLCD"
8+
depends on DRM && OF && (ARM || ARM64)
9+
depends on COMMON_CLK
10+
select DRM_ARM
11+
select DRM_KMS_HELPER
12+
select DRM_KMS_FB_HELPER
13+
select DRM_KMS_CMA_HELPER
14+
help
15+
Choose this option if you have an ARM High Definition Colour LCD
16+
controller.
17+
18+
If M is selected the module will be called hdlcd.
19+
20+
config DRM_HDLCD_SHOW_UNDERRUN
21+
bool "Show underrun conditions"
22+
depends on DRM_HDLCD
23+
default n
24+
help
25+
Enable this option to show in red colour the pixels that the
26+
HDLCD device did not fetch from framebuffer due to underrun
27+
conditions.

drivers/gpu/drm/arm/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
hdlcd-y := hdlcd_drv.o hdlcd_crtc.o
2+
obj-$(CONFIG_DRM_HDLCD) += hdlcd.o

drivers/gpu/drm/arm/hdlcd_crtc.c

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/*
2+
* Copyright (C) 2013-2015 ARM Limited
3+
* Author: Liviu Dudau <Liviu.Dudau@arm.com>
4+
*
5+
* This file is subject to the terms and conditions of the GNU General Public
6+
* License. See the file COPYING in the main directory of this archive
7+
* for more details.
8+
*
9+
* Implementation of a CRTC class for the HDLCD driver.
10+
*/
11+
12+
#include <drm/drmP.h>
13+
#include <drm/drm_atomic_helper.h>
14+
#include <drm/drm_crtc.h>
15+
#include <drm/drm_crtc_helper.h>
16+
#include <drm/drm_fb_helper.h>
17+
#include <drm/drm_fb_cma_helper.h>
18+
#include <drm/drm_gem_cma_helper.h>
19+
#include <drm/drm_of.h>
20+
#include <drm/drm_plane_helper.h>
21+
#include <linux/clk.h>
22+
#include <linux/of_graph.h>
23+
#include <linux/platform_data/simplefb.h>
24+
#include <video/videomode.h>
25+
26+
#include "hdlcd_drv.h"
27+
#include "hdlcd_regs.h"
28+
29+
/*
30+
* The HDLCD controller is a dumb RGB streamer that gets connected to
31+
* a single HDMI transmitter or in the case of the ARM Models it gets
32+
* emulated by the software that does the actual rendering.
33+
*
34+
*/
35+
36+
static const struct drm_crtc_funcs hdlcd_crtc_funcs = {
37+
.destroy = drm_crtc_cleanup,
38+
.set_config = drm_atomic_helper_set_config,
39+
.page_flip = drm_atomic_helper_page_flip,
40+
.reset = drm_atomic_helper_crtc_reset,
41+
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
42+
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
43+
};
44+
45+
static struct simplefb_format supported_formats[] = SIMPLEFB_FORMATS;
46+
47+
/*
48+
* Setup the HDLCD registers for decoding the pixels out of the framebuffer
49+
*/
50+
static int hdlcd_set_pxl_fmt(struct drm_crtc *crtc)
51+
{
52+
unsigned int btpp;
53+
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
54+
uint32_t pixel_format;
55+
struct simplefb_format *format = NULL;
56+
int i;
57+
58+
pixel_format = crtc->primary->state->fb->pixel_format;
59+
60+
for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
61+
if (supported_formats[i].fourcc == pixel_format)
62+
format = &supported_formats[i];
63+
}
64+
65+
if (WARN_ON(!format))
66+
return 0;
67+
68+
/* HDLCD uses 'bytes per pixel', zero means 1 byte */
69+
btpp = (format->bits_per_pixel + 7) / 8;
70+
hdlcd_write(hdlcd, HDLCD_REG_PIXEL_FORMAT, (btpp - 1) << 3);
71+
72+
/*
73+
* The format of the HDLCD_REG_<color>_SELECT register is:
74+
* - bits[23:16] - default value for that color component
75+
* - bits[11:8] - number of bits to extract for each color component
76+
* - bits[4:0] - index of the lowest bit to extract
77+
*
78+
* The default color value is used when bits[11:8] are zero, when the
79+
* pixel is outside the visible frame area or when there is a
80+
* buffer underrun.
81+
*/
82+
hdlcd_write(hdlcd, HDLCD_REG_RED_SELECT, format->red.offset |
83+
#ifdef CONFIG_DRM_HDLCD_SHOW_UNDERRUN
84+
0x00ff0000 | /* show underruns in red */
85+
#endif
86+
((format->red.length & 0xf) << 8));
87+
hdlcd_write(hdlcd, HDLCD_REG_GREEN_SELECT, format->green.offset |
88+
((format->green.length & 0xf) << 8));
89+
hdlcd_write(hdlcd, HDLCD_REG_BLUE_SELECT, format->blue.offset |
90+
((format->blue.length & 0xf) << 8));
91+
92+
return 0;
93+
}
94+
95+
static void hdlcd_crtc_mode_set_nofb(struct drm_crtc *crtc)
96+
{
97+
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
98+
struct drm_display_mode *m = &crtc->state->adjusted_mode;
99+
struct videomode vm;
100+
unsigned int polarities, line_length, err;
101+
102+
vm.vfront_porch = m->crtc_vsync_start - m->crtc_vdisplay;
103+
vm.vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
104+
vm.vsync_len = m->crtc_vsync_end - m->crtc_vsync_start;
105+
vm.hfront_porch = m->crtc_hsync_start - m->crtc_hdisplay;
106+
vm.hback_porch = m->crtc_htotal - m->crtc_hsync_end;
107+
vm.hsync_len = m->crtc_hsync_end - m->crtc_hsync_start;
108+
109+
polarities = HDLCD_POLARITY_DATAEN | HDLCD_POLARITY_DATA;
110+
111+
if (m->flags & DRM_MODE_FLAG_PHSYNC)
112+
polarities |= HDLCD_POLARITY_HSYNC;
113+
if (m->flags & DRM_MODE_FLAG_PVSYNC)
114+
polarities |= HDLCD_POLARITY_VSYNC;
115+
116+
line_length = crtc->primary->state->fb->pitches[0];
117+
118+
/* Allow max number of outstanding requests and largest burst size */
119+
hdlcd_write(hdlcd, HDLCD_REG_BUS_OPTIONS,
120+
HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
121+
122+
hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_LENGTH, line_length);
123+
hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_PITCH, line_length);
124+
hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_COUNT, m->crtc_vdisplay - 1);
125+
hdlcd_write(hdlcd, HDLCD_REG_V_DATA, m->crtc_vdisplay - 1);
126+
hdlcd_write(hdlcd, HDLCD_REG_V_BACK_PORCH, vm.vback_porch - 1);
127+
hdlcd_write(hdlcd, HDLCD_REG_V_FRONT_PORCH, vm.vfront_porch - 1);
128+
hdlcd_write(hdlcd, HDLCD_REG_V_SYNC, vm.vsync_len - 1);
129+
hdlcd_write(hdlcd, HDLCD_REG_H_BACK_PORCH, vm.hback_porch - 1);
130+
hdlcd_write(hdlcd, HDLCD_REG_H_FRONT_PORCH, vm.hfront_porch - 1);
131+
hdlcd_write(hdlcd, HDLCD_REG_H_SYNC, vm.hsync_len - 1);
132+
hdlcd_write(hdlcd, HDLCD_REG_H_DATA, m->crtc_hdisplay - 1);
133+
hdlcd_write(hdlcd, HDLCD_REG_POLARITIES, polarities);
134+
135+
err = hdlcd_set_pxl_fmt(crtc);
136+
if (err)
137+
return;
138+
139+
clk_set_rate(hdlcd->clk, m->crtc_clock * 1000);
140+
}
141+
142+
static void hdlcd_crtc_enable(struct drm_crtc *crtc)
143+
{
144+
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
145+
146+
clk_prepare_enable(hdlcd->clk);
147+
hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 1);
148+
drm_crtc_vblank_on(crtc);
149+
}
150+
151+
static void hdlcd_crtc_disable(struct drm_crtc *crtc)
152+
{
153+
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
154+
155+
if (!crtc->primary->fb)
156+
return;
157+
158+
clk_disable_unprepare(hdlcd->clk);
159+
hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0);
160+
drm_crtc_vblank_off(crtc);
161+
}
162+
163+
static int hdlcd_crtc_atomic_check(struct drm_crtc *crtc,
164+
struct drm_crtc_state *state)
165+
{
166+
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
167+
struct drm_display_mode *mode = &state->adjusted_mode;
168+
long rate, clk_rate = mode->clock * 1000;
169+
170+
rate = clk_round_rate(hdlcd->clk, clk_rate);
171+
if (rate != clk_rate) {
172+
/* clock required by mode not supported by hardware */
173+
return -EINVAL;
174+
}
175+
176+
return 0;
177+
}
178+
179+
static void hdlcd_crtc_atomic_begin(struct drm_crtc *crtc,
180+
struct drm_crtc_state *state)
181+
{
182+
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
183+
unsigned long flags;
184+
185+
if (crtc->state->event) {
186+
struct drm_pending_vblank_event *event = crtc->state->event;
187+
188+
crtc->state->event = NULL;
189+
event->pipe = drm_crtc_index(crtc);
190+
191+
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
192+
193+
spin_lock_irqsave(&crtc->dev->event_lock, flags);
194+
list_add_tail(&event->base.link, &hdlcd->event_list);
195+
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
196+
}
197+
}
198+
199+
static void hdlcd_crtc_atomic_flush(struct drm_crtc *crtc,
200+
struct drm_crtc_state *state)
201+
{
202+
}
203+
204+
static bool hdlcd_crtc_mode_fixup(struct drm_crtc *crtc,
205+
const struct drm_display_mode *mode,
206+
struct drm_display_mode *adjusted_mode)
207+
{
208+
return true;
209+
}
210+
211+
static const struct drm_crtc_helper_funcs hdlcd_crtc_helper_funcs = {
212+
.mode_fixup = hdlcd_crtc_mode_fixup,
213+
.mode_set = drm_helper_crtc_mode_set,
214+
.mode_set_base = drm_helper_crtc_mode_set_base,
215+
.mode_set_nofb = hdlcd_crtc_mode_set_nofb,
216+
.enable = hdlcd_crtc_enable,
217+
.disable = hdlcd_crtc_disable,
218+
.prepare = hdlcd_crtc_disable,
219+
.commit = hdlcd_crtc_enable,
220+
.atomic_check = hdlcd_crtc_atomic_check,
221+
.atomic_begin = hdlcd_crtc_atomic_begin,
222+
.atomic_flush = hdlcd_crtc_atomic_flush,
223+
};
224+
225+
static int hdlcd_plane_atomic_check(struct drm_plane *plane,
226+
struct drm_plane_state *state)
227+
{
228+
return 0;
229+
}
230+
231+
static void hdlcd_plane_atomic_update(struct drm_plane *plane,
232+
struct drm_plane_state *state)
233+
{
234+
struct hdlcd_drm_private *hdlcd;
235+
struct drm_gem_cma_object *gem;
236+
dma_addr_t scanout_start;
237+
238+
if (!plane->state->crtc || !plane->state->fb)
239+
return;
240+
241+
hdlcd = crtc_to_hdlcd_priv(plane->state->crtc);
242+
gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
243+
scanout_start = gem->paddr;
244+
hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start);
245+
}
246+
247+
static const struct drm_plane_helper_funcs hdlcd_plane_helper_funcs = {
248+
.prepare_fb = NULL,
249+
.cleanup_fb = NULL,
250+
.atomic_check = hdlcd_plane_atomic_check,
251+
.atomic_update = hdlcd_plane_atomic_update,
252+
};
253+
254+
static void hdlcd_plane_destroy(struct drm_plane *plane)
255+
{
256+
drm_plane_helper_disable(plane);
257+
drm_plane_cleanup(plane);
258+
}
259+
260+
static const struct drm_plane_funcs hdlcd_plane_funcs = {
261+
.update_plane = drm_atomic_helper_update_plane,
262+
.disable_plane = drm_atomic_helper_disable_plane,
263+
.destroy = hdlcd_plane_destroy,
264+
.reset = drm_atomic_helper_plane_reset,
265+
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
266+
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
267+
};
268+
269+
static struct drm_plane *hdlcd_plane_init(struct drm_device *drm)
270+
{
271+
struct hdlcd_drm_private *hdlcd = drm->dev_private;
272+
struct drm_plane *plane = NULL;
273+
u32 formats[ARRAY_SIZE(supported_formats)], i;
274+
int ret;
275+
276+
plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
277+
if (!plane)
278+
return ERR_PTR(-ENOMEM);
279+
280+
for (i = 0; i < ARRAY_SIZE(supported_formats); i++)
281+
formats[i] = supported_formats[i].fourcc;
282+
283+
ret = drm_universal_plane_init(drm, plane, 0xff, &hdlcd_plane_funcs,
284+
formats, ARRAY_SIZE(formats),
285+
DRM_PLANE_TYPE_PRIMARY, NULL);
286+
if (ret) {
287+
devm_kfree(drm->dev, plane);
288+
return ERR_PTR(ret);
289+
}
290+
291+
drm_plane_helper_add(plane, &hdlcd_plane_helper_funcs);
292+
hdlcd->plane = plane;
293+
294+
return plane;
295+
}
296+
297+
void hdlcd_crtc_suspend(struct drm_crtc *crtc)
298+
{
299+
hdlcd_crtc_disable(crtc);
300+
}
301+
302+
void hdlcd_crtc_resume(struct drm_crtc *crtc)
303+
{
304+
hdlcd_crtc_enable(crtc);
305+
}
306+
307+
int hdlcd_setup_crtc(struct drm_device *drm)
308+
{
309+
struct hdlcd_drm_private *hdlcd = drm->dev_private;
310+
struct drm_plane *primary;
311+
int ret;
312+
313+
primary = hdlcd_plane_init(drm);
314+
if (IS_ERR(primary))
315+
return PTR_ERR(primary);
316+
317+
ret = drm_crtc_init_with_planes(drm, &hdlcd->crtc, primary, NULL,
318+
&hdlcd_crtc_funcs, NULL);
319+
if (ret) {
320+
hdlcd_plane_destroy(primary);
321+
devm_kfree(drm->dev, primary);
322+
return ret;
323+
}
324+
325+
drm_crtc_helper_add(&hdlcd->crtc, &hdlcd_crtc_helper_funcs);
326+
return 0;
327+
}

0 commit comments

Comments
 (0)