Skip to content

Commit b31ecd3

Browse files
chadmedjannau
authored andcommitted
drm: apple: Support YCbCr formats
Support 8-bit YCbCr planar and semi-planar formats in 4:2:0, 4:2:2 and 4:4:4 sub-sampling with limited and full range. Use the signalled color space and transfer functions for YCbCr formats. DCP allows a unique colour space to be specified for each surface. The firmware then tonemaps this to the connected display's native colour space. KMS sets color_encoding and color_range only for YCbCr formats. Signed-off-by: James Calligeros <jcalligeros99@gmail.com> Co-developed-by: Janne Grunau <j@jannau.net> Signed-off-by: Janne Grunau <j@jannau.net>
1 parent b6a8d6b commit b31ecd3

3 files changed

Lines changed: 160 additions & 15 deletions

File tree

drivers/gpu/drm/apple/iomfb.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,6 @@ enum iomfb_property_id {
8080
/* We have 4 surfaces, but we can only ever blend two */
8181
#define MAX_BLEND_SURFACES 2
8282

83-
enum dcp_colorspace {
84-
DCP_COLORSPACE_BG_SRGB = 0,
85-
DCP_COLORSPACE_BG_BT2020 = 9,
86-
DCP_COLORSPACE_NATIVE = 12,
87-
};
88-
89-
enum dcp_xfer_func {
90-
DCP_XFER_FUNC_SDR = 13,
91-
DCP_XFER_FUNC_HDR = 16,
92-
};
93-
9483
struct dcp_iouserclient {
9584
/* Handle for the IOUserClient. macOS sets this to a kernel VA. */
9685
u64 handle;

drivers/gpu/drm/apple/iomfb_plane.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,35 @@
1919
#define DCP_FORMAT_W30R fourcc_code('r', '0', '3', 'w') // wide gamut packed 10-bit RGB without alpha
2020
#define DCP_FORMAT_L10R fourcc_code('r', '0', '1', 'l') // full range packed 10-bit RGB with alpha
2121

22+
#define DCP_FORMAT_420V fourcc_code('v', '0', '2', '4') // NV12 video range 2 plane 8-bit YCbCr
23+
#define DCP_FORMAT_420F fourcc_code('f', '0', '2', '4') // NV12 full range 2 plane 8-bit YCbCr
24+
#define DCP_FORMAT_422V fourcc_code('v', '2', '2', '4') // NV16 video range 2 plane 8-bit YCbCr
25+
#define DCP_FORMAT_422F fourcc_code('f', '2', '2', '4') // NV16 full range 2 plane 8-bit YCbCr
26+
#define DCP_FORMAT_444V fourcc_code('v', '4', '4', '4') // NV24 video range 2 plane 8-bit YCbCr
27+
#define DCP_FORMAT_444F fourcc_code('f', '4', '4', '4') // NV24 full range 2 plane 8-bit YCbCr
2228

29+
#define DCP_FORMAT_X420 fourcc_code('0', '2', '4', 'x') // P010 video range 2 plane 10-bit YCbCR
30+
#define DCP_FORMAT_X422 fourcc_code('2', '2', '4', 'x') // P210 video range 2 plane 10-bit YCbCR
31+
#define DCP_FORMAT_X444 fourcc_code('4', '4', '4', 'x') // P410 video range 2 plane 10-bit YCbCR
32+
33+
#define DCP_FORMAT_XF20 fourcc_code('0', '2', 'f', 'x') // P010 full range 2 plane 10-bit YCbCR
34+
#define DCP_FORMAT_XF22 fourcc_code('2', '2', 'f', 'x') // P210 full range 2 plane 10-bit YCbCR
35+
#define DCP_FORMAT_XF44 fourcc_code('4', '4', 'f', 'x') // P410 full range 2 plane 10-bit YCbCR
36+
37+
enum dcp_colorspace {
38+
DCP_COLORSPACE_BG_SRGB = 0,
39+
DCP_COLORSPACE_BT601 = 1,
40+
DCP_COLORSPACE_BT709 = 2,
41+
DCP_COLORSPACE_BG_BT2020 = 9,
42+
DCP_COLORSPACE_NATIVE = 12,
43+
};
44+
45+
enum dcp_xfer_func {
46+
DCP_XFER_FUNC_BT601 = 1,
47+
DCP_XFER_FUNC_BT1886 = 2,
48+
DCP_XFER_FUNC_SDR = 13,
49+
DCP_XFER_FUNC_HDR = 16,
50+
};
2351

2452
struct dcp_rect {
2553
u32 x;

drivers/gpu/drm/apple/plane.c

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <drm/drm_fourcc.h>
1313
#include <drm/drm_fb_dma_helper.h>
1414
#include <drm/drm_framebuffer.h>
15+
#include <drm/drm_gem.h>
1516
#include <drm/drm_gem_dma_helper.h>
1617
#include <drm/drm_plane.h>
1718

@@ -81,6 +82,29 @@ static int apple_plane_atomic_check(struct drm_plane *plane,
8182
return -EINVAL;
8283
}
8384

85+
/*
86+
* Pitches have to be 64-byte aligned.
87+
*/
88+
for (u32 i = 0; i < new_plane_state->fb->format->num_planes; i++)
89+
if (new_plane_state->fb->pitches[i] & 63)
90+
return -EINVAL;
91+
92+
/*
93+
* FIXME: dcp can currently only use multi-planar buffers using the same
94+
* object for all planes. It has a mandatory iommu so it should
95+
* be no problem to map multiple objects "linearly" into DCP
96+
* virtual address space and calculate the offsets accordingly.
97+
* Or maybe it can accept multiple BOs via the per plane field
98+
* `base`.
99+
*/
100+
if (new_plane_state->fb->format->num_planes > 1) {
101+
const struct drm_gem_object *first = new_plane_state->fb->obj[0];
102+
for (u32 i = 1; i < new_plane_state->fb->format->num_planes; i++)
103+
if (new_plane_state->fb->obj[i] != NULL &&
104+
new_plane_state->fb->obj[i] != first)
105+
return -EINVAL;
106+
}
107+
84108
return 0;
85109
}
86110

@@ -105,8 +129,9 @@ static struct dcp_rect drm_to_dcp_rect_fp(const struct drm_rect *fp_rect)
105129
return drm_to_dcp_rect(&rect);
106130
}
107131

108-
static u32 drm_format_to_dcp(u32 drm)
132+
static u32 drm_format_to_dcp(u32 drm, enum drm_color_range range)
109133
{
134+
bool fr = range == DRM_COLOR_YCBCR_FULL_RANGE;
110135
switch (drm) {
111136
case DRM_FORMAT_XRGB8888:
112137
case DRM_FORMAT_ARGB8888:
@@ -119,12 +144,67 @@ static u32 drm_format_to_dcp(u32 drm)
119144
case DRM_FORMAT_XRGB2101010:
120145
case DRM_FORMAT_ARGB2101010:
121146
return DCP_FORMAT_L10R;
147+
148+
/* semi planar YCbCr formats, limited and full range */
149+
case DRM_FORMAT_NV12:
150+
return fr ? DCP_FORMAT_420F : DCP_FORMAT_420V;
151+
case DRM_FORMAT_NV16:
152+
return fr ? DCP_FORMAT_422F : DCP_FORMAT_422V;
153+
case DRM_FORMAT_NV24:
154+
return fr ? DCP_FORMAT_444F : DCP_FORMAT_444V;
155+
156+
/* semi planar 10-bit YCbCr formats, limited and full range */
157+
case DRM_FORMAT_P010:
158+
return fr ? DCP_FORMAT_XF20 : DCP_FORMAT_X420;
159+
case DRM_FORMAT_P210:
160+
return fr ? DCP_FORMAT_XF22 : DCP_FORMAT_X422;
161+
/*
162+
* TODO: missing DRM fourcc for P410
163+
*/
164+
#if defined(DRM_FORMAT_P410)
165+
case DRM_FORMAT_P410:
166+
return fr ? DCP_FORMAT_XF44 : DCP_FORMAT_X444;
167+
#endif
122168
}
123169

124170
pr_warn("DRM format %X not supported in DCP\n", drm);
125171
return 0;
126172
}
127173

174+
static enum dcp_xfer_func get_xfer_func(bool is_yuv, enum drm_color_encoding enc)
175+
{
176+
if (!is_yuv)
177+
return DCP_XFER_FUNC_SDR;
178+
179+
switch (enc) {
180+
case DRM_COLOR_YCBCR_BT601:
181+
return DCP_XFER_FUNC_BT601;
182+
case DRM_COLOR_YCBCR_BT709:
183+
case DRM_COLOR_YCBCR_BT2020:
184+
return DCP_XFER_FUNC_BT1886;
185+
default:
186+
return DCP_XFER_FUNC_SDR;
187+
}
188+
}
189+
190+
static enum dcp_colorspace get_colorspace(bool is_yuv,
191+
enum drm_color_encoding enc)
192+
{
193+
if (!is_yuv)
194+
return DCP_COLORSPACE_NATIVE;
195+
196+
switch (enc) {
197+
case DRM_COLOR_YCBCR_BT601:
198+
return DCP_COLORSPACE_BT601;
199+
case DRM_COLOR_YCBCR_BT709:
200+
return DCP_COLORSPACE_BT709;
201+
case DRM_COLOR_YCBCR_BT2020:
202+
return DCP_COLORSPACE_BG_BT2020;
203+
default:
204+
return DCP_COLORSPACE_NATIVE;
205+
}
206+
}
207+
128208
static void apple_plane_atomic_update(struct drm_plane *plane,
129209
struct drm_atomic_state *state)
130210
{
@@ -160,9 +240,11 @@ static void apple_plane_atomic_update(struct drm_plane *plane,
160240

161241
new_state->surf = (struct dcp_surface){
162242
.is_premultiplied = is_premultiplied,
163-
.format = drm_format_to_dcp(fb->format->format),
164-
.xfer_func = DCP_XFER_FUNC_SDR,
165-
.colorspace = DCP_COLORSPACE_NATIVE,
243+
.plane_cnt = fb->format->num_planes,
244+
.plane_cnt2 = fb->format->num_planes,
245+
.format = drm_format_to_dcp(fmt->format, base->color_range),
246+
.xfer_func = get_xfer_func(fmt->is_yuv, base->color_encoding),
247+
.colorspace = get_colorspace(fmt->is_yuv, base->color_encoding),
166248
.stride = fb->pitches[0],
167249
.width = fb->width,
168250
.height = fb->height,
@@ -177,6 +259,30 @@ static void apple_plane_atomic_update(struct drm_plane *plane,
177259
.has_planes = 1,
178260
};
179261

262+
/* Populate plane information for planar formats */
263+
struct dcp_surface *surf = &new_state->surf;
264+
for (int i = 0; fb->format->num_planes && i < fb->format->num_planes; i++) {
265+
u32 width = drm_format_info_plane_width(fb->format, fb->width, i);
266+
u32 height = drm_format_info_plane_height(fb->format, fb->height, i);
267+
u32 bh = drm_format_info_block_height(fb->format, i);
268+
u32 bw = drm_format_info_block_width(fb->format, i);
269+
270+
surf->planes[i] = (struct dcp_plane_info){
271+
.width = width,
272+
.height = height,
273+
.base = fb->offsets[i] - fb->offsets[0],
274+
.offset = fb->offsets[i] - fb->offsets[0],
275+
.stride = fb->pitches[i],
276+
.size = height * fb->pitches[i],
277+
.tile_size = bw * bh,
278+
.tile_w = bw,
279+
.tile_h = bh,
280+
};
281+
282+
if (i > 0)
283+
surf->buf_size += surf->planes[i].size;
284+
}
285+
180286
/* the obvious helper call drm_fb_dma_get_gem_addr() adjusts
181287
* the address for source x/y offsets. Since IOMFB has a direct
182288
* support source position prefer that.
@@ -247,12 +353,28 @@ static const u32 dcp_primary_formats[] = {
247353
DRM_FORMAT_ARGB8888,
248354
DRM_FORMAT_XBGR8888,
249355
DRM_FORMAT_ABGR8888,
356+
DRM_FORMAT_NV12,
357+
DRM_FORMAT_NV16,
358+
DRM_FORMAT_NV24,
359+
DRM_FORMAT_P010,
360+
DRM_FORMAT_P210,
361+
#if defined(DRM_FORMAT_P410)
362+
DRM_FORMAT_P410,
363+
#endif
250364
};
251365

252366
static const u32 dcp_overlay_formats[] = {
253367
DRM_FORMAT_ARGB2101010,
254368
DRM_FORMAT_ARGB8888,
255369
DRM_FORMAT_ABGR8888,
370+
DRM_FORMAT_NV12,
371+
DRM_FORMAT_NV16,
372+
DRM_FORMAT_NV24,
373+
DRM_FORMAT_P010,
374+
DRM_FORMAT_P210,
375+
#if defined(DRM_FORMAT_P410)
376+
DRM_FORMAT_P410,
377+
#endif
256378
};
257379

258380
u64 apple_format_modifiers[] = {
@@ -291,6 +413,12 @@ struct drm_plane *apple_plane_init(struct drm_device *dev,
291413
if (IS_ERR(plane))
292414
return ERR_PTR(PTR_ERR(plane));
293415

416+
drm_plane_create_color_properties(&plane->base,
417+
(1 << DRM_COLOR_ENCODING_MAX) - 1,
418+
(1 << DRM_COLOR_RANGE_MAX) - 1,
419+
DRM_COLOR_YCBCR_BT709,
420+
DRM_COLOR_YCBCR_LIMITED_RANGE);
421+
294422
if (type == DRM_PLANE_TYPE_PRIMARY)
295423
drm_plane_helper_add(&plane->base, &apple_primary_plane_helper_funcs);
296424
else

0 commit comments

Comments
 (0)