1314c45e3SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only 2314c45e3SThomas Zimmermann 3314c45e3SThomas Zimmermann #include <linux/export.h> 4314c45e3SThomas Zimmermann #include <linux/slab.h> 5314c45e3SThomas Zimmermann 6314c45e3SThomas Zimmermann #include <drm/drm_atomic.h> 7314c45e3SThomas Zimmermann #include <drm/drm_atomic_helper.h> 8314c45e3SThomas Zimmermann #include <drm/drm_atomic_state_helper.h> 9314c45e3SThomas Zimmermann #include <drm/drm_damage_helper.h> 10314c45e3SThomas Zimmermann #include <drm/drm_drv.h> 11314c45e3SThomas Zimmermann #include <drm/drm_edid.h> 12314c45e3SThomas Zimmermann #include <drm/drm_fourcc.h> 13314c45e3SThomas Zimmermann #include <drm/drm_framebuffer.h> 14314c45e3SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h> 15314c45e3SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h> 16314c45e3SThomas Zimmermann #include <drm/drm_panic.h> 17314c45e3SThomas Zimmermann #include <drm/drm_print.h> 18314c45e3SThomas Zimmermann #include <drm/drm_probe_helper.h> 19314c45e3SThomas Zimmermann 20314c45e3SThomas Zimmermann #include "drm_sysfb_helper.h" 21314c45e3SThomas Zimmermann 22314c45e3SThomas Zimmermann struct drm_display_mode drm_sysfb_mode(unsigned int width, 23314c45e3SThomas Zimmermann unsigned int height, 24314c45e3SThomas Zimmermann unsigned int width_mm, 25314c45e3SThomas Zimmermann unsigned int height_mm) 26314c45e3SThomas Zimmermann { 27314c45e3SThomas Zimmermann /* 28314c45e3SThomas Zimmermann * Assume a monitor resolution of 96 dpi to 29314c45e3SThomas Zimmermann * get a somewhat reasonable screen size. 30314c45e3SThomas Zimmermann */ 31314c45e3SThomas Zimmermann if (!width_mm) 32314c45e3SThomas Zimmermann width_mm = DRM_MODE_RES_MM(width, 96ul); 33314c45e3SThomas Zimmermann if (!height_mm) 34314c45e3SThomas Zimmermann height_mm = DRM_MODE_RES_MM(height, 96ul); 35314c45e3SThomas Zimmermann 36314c45e3SThomas Zimmermann { 37314c45e3SThomas Zimmermann const struct drm_display_mode mode = { 38314c45e3SThomas Zimmermann DRM_MODE_INIT(60, width, height, width_mm, height_mm) 39314c45e3SThomas Zimmermann }; 40314c45e3SThomas Zimmermann 41314c45e3SThomas Zimmermann return mode; 42314c45e3SThomas Zimmermann } 43314c45e3SThomas Zimmermann } 44314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_mode); 45314c45e3SThomas Zimmermann 46314c45e3SThomas Zimmermann /* 47314c45e3SThomas Zimmermann * Plane 48314c45e3SThomas Zimmermann */ 49314c45e3SThomas Zimmermann 50*1a45ef02SThomas Zimmermann static u32 to_nonalpha_fourcc(u32 fourcc) 51*1a45ef02SThomas Zimmermann { 52*1a45ef02SThomas Zimmermann /* only handle formats with depth != 0 and alpha channel */ 53*1a45ef02SThomas Zimmermann switch (fourcc) { 54*1a45ef02SThomas Zimmermann case DRM_FORMAT_ARGB1555: 55*1a45ef02SThomas Zimmermann return DRM_FORMAT_XRGB1555; 56*1a45ef02SThomas Zimmermann case DRM_FORMAT_ABGR1555: 57*1a45ef02SThomas Zimmermann return DRM_FORMAT_XBGR1555; 58*1a45ef02SThomas Zimmermann case DRM_FORMAT_RGBA5551: 59*1a45ef02SThomas Zimmermann return DRM_FORMAT_RGBX5551; 60*1a45ef02SThomas Zimmermann case DRM_FORMAT_BGRA5551: 61*1a45ef02SThomas Zimmermann return DRM_FORMAT_BGRX5551; 62*1a45ef02SThomas Zimmermann case DRM_FORMAT_ARGB8888: 63*1a45ef02SThomas Zimmermann return DRM_FORMAT_XRGB8888; 64*1a45ef02SThomas Zimmermann case DRM_FORMAT_ABGR8888: 65*1a45ef02SThomas Zimmermann return DRM_FORMAT_XBGR8888; 66*1a45ef02SThomas Zimmermann case DRM_FORMAT_RGBA8888: 67*1a45ef02SThomas Zimmermann return DRM_FORMAT_RGBX8888; 68*1a45ef02SThomas Zimmermann case DRM_FORMAT_BGRA8888: 69*1a45ef02SThomas Zimmermann return DRM_FORMAT_BGRX8888; 70*1a45ef02SThomas Zimmermann case DRM_FORMAT_ARGB2101010: 71*1a45ef02SThomas Zimmermann return DRM_FORMAT_XRGB2101010; 72*1a45ef02SThomas Zimmermann case DRM_FORMAT_ABGR2101010: 73*1a45ef02SThomas Zimmermann return DRM_FORMAT_XBGR2101010; 74*1a45ef02SThomas Zimmermann case DRM_FORMAT_RGBA1010102: 75*1a45ef02SThomas Zimmermann return DRM_FORMAT_RGBX1010102; 76*1a45ef02SThomas Zimmermann case DRM_FORMAT_BGRA1010102: 77*1a45ef02SThomas Zimmermann return DRM_FORMAT_BGRX1010102; 78*1a45ef02SThomas Zimmermann } 79*1a45ef02SThomas Zimmermann 80*1a45ef02SThomas Zimmermann return fourcc; 81*1a45ef02SThomas Zimmermann } 82*1a45ef02SThomas Zimmermann 83*1a45ef02SThomas Zimmermann static bool is_listed_fourcc(const u32 *fourccs, size_t nfourccs, u32 fourcc) 84*1a45ef02SThomas Zimmermann { 85*1a45ef02SThomas Zimmermann const u32 *fourccs_end = fourccs + nfourccs; 86*1a45ef02SThomas Zimmermann 87*1a45ef02SThomas Zimmermann while (fourccs < fourccs_end) { 88*1a45ef02SThomas Zimmermann if (*fourccs == fourcc) 89*1a45ef02SThomas Zimmermann return true; 90*1a45ef02SThomas Zimmermann ++fourccs; 91*1a45ef02SThomas Zimmermann } 92*1a45ef02SThomas Zimmermann return false; 93*1a45ef02SThomas Zimmermann } 94*1a45ef02SThomas Zimmermann 95*1a45ef02SThomas Zimmermann /** 96*1a45ef02SThomas Zimmermann * drm_sysfb_build_fourcc_list - Filters a list of supported color formats against 97*1a45ef02SThomas Zimmermann * the device's native formats 98*1a45ef02SThomas Zimmermann * @dev: DRM device 99*1a45ef02SThomas Zimmermann * @native_fourccs: 4CC codes of natively supported color formats 100*1a45ef02SThomas Zimmermann * @native_nfourccs: The number of entries in @native_fourccs 101*1a45ef02SThomas Zimmermann * @fourccs_out: Returns 4CC codes of supported color formats 102*1a45ef02SThomas Zimmermann * @nfourccs_out: The number of available entries in @fourccs_out 103*1a45ef02SThomas Zimmermann * 104*1a45ef02SThomas Zimmermann * This function create a list of supported color format from natively 105*1a45ef02SThomas Zimmermann * supported formats and additional emulated formats. 106*1a45ef02SThomas Zimmermann * At a minimum, most userspace programs expect at least support for 107*1a45ef02SThomas Zimmermann * XRGB8888 on the primary plane. Sysfb devices that have to emulate 108*1a45ef02SThomas Zimmermann * the format should use drm_sysfb_build_fourcc_list() to create a list 109*1a45ef02SThomas Zimmermann * of supported color formats. The returned list can be handed over to 110*1a45ef02SThomas Zimmermann * drm_universal_plane_init() et al. Native formats will go before 111*1a45ef02SThomas Zimmermann * emulated formats. Native formats with alpha channel will be replaced 112*1a45ef02SThomas Zimmermann * by equal formats without alpha channel, as primary planes usually 113*1a45ef02SThomas Zimmermann * don't support alpha. Other heuristics might be applied to optimize 114*1a45ef02SThomas Zimmermann * the sorting order. Formats near the beginning of the list are usually 115*1a45ef02SThomas Zimmermann * preferred over formats near the end of the list. 116*1a45ef02SThomas Zimmermann * 117*1a45ef02SThomas Zimmermann * Returns: 118*1a45ef02SThomas Zimmermann * The number of color-formats 4CC codes returned in @fourccs_out. 119*1a45ef02SThomas Zimmermann */ 120*1a45ef02SThomas Zimmermann size_t drm_sysfb_build_fourcc_list(struct drm_device *dev, 121*1a45ef02SThomas Zimmermann const u32 *native_fourccs, size_t native_nfourccs, 122*1a45ef02SThomas Zimmermann u32 *fourccs_out, size_t nfourccs_out) 123*1a45ef02SThomas Zimmermann { 124*1a45ef02SThomas Zimmermann /* 125*1a45ef02SThomas Zimmermann * XRGB8888 is the default fallback format for most of userspace 126*1a45ef02SThomas Zimmermann * and it's currently the only format that should be emulated for 127*1a45ef02SThomas Zimmermann * the primary plane. Only if there's ever another default fallback, 128*1a45ef02SThomas Zimmermann * it should be added here. 129*1a45ef02SThomas Zimmermann */ 130*1a45ef02SThomas Zimmermann static const u32 extra_fourccs[] = { 131*1a45ef02SThomas Zimmermann DRM_FORMAT_XRGB8888, 132*1a45ef02SThomas Zimmermann }; 133*1a45ef02SThomas Zimmermann static const size_t extra_nfourccs = ARRAY_SIZE(extra_fourccs); 134*1a45ef02SThomas Zimmermann 135*1a45ef02SThomas Zimmermann u32 *fourccs = fourccs_out; 136*1a45ef02SThomas Zimmermann const u32 *fourccs_end = fourccs_out + nfourccs_out; 137*1a45ef02SThomas Zimmermann size_t i; 138*1a45ef02SThomas Zimmermann 139*1a45ef02SThomas Zimmermann /* 140*1a45ef02SThomas Zimmermann * The device's native formats go first. 141*1a45ef02SThomas Zimmermann */ 142*1a45ef02SThomas Zimmermann 143*1a45ef02SThomas Zimmermann for (i = 0; i < native_nfourccs; ++i) { 144*1a45ef02SThomas Zimmermann /* 145*1a45ef02SThomas Zimmermann * Several DTs, boot loaders and firmware report native 146*1a45ef02SThomas Zimmermann * alpha formats that are non-alpha formats instead. So 147*1a45ef02SThomas Zimmermann * replace alpha formats by non-alpha formats. 148*1a45ef02SThomas Zimmermann */ 149*1a45ef02SThomas Zimmermann u32 fourcc = to_nonalpha_fourcc(native_fourccs[i]); 150*1a45ef02SThomas Zimmermann 151*1a45ef02SThomas Zimmermann if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) { 152*1a45ef02SThomas Zimmermann continue; /* skip duplicate entries */ 153*1a45ef02SThomas Zimmermann } else if (fourccs == fourccs_end) { 154*1a45ef02SThomas Zimmermann drm_warn(dev, "Ignoring native format %p4cc\n", &fourcc); 155*1a45ef02SThomas Zimmermann continue; /* end of available output buffer */ 156*1a45ef02SThomas Zimmermann } 157*1a45ef02SThomas Zimmermann 158*1a45ef02SThomas Zimmermann drm_dbg_kms(dev, "adding native format %p4cc\n", &fourcc); 159*1a45ef02SThomas Zimmermann 160*1a45ef02SThomas Zimmermann *fourccs = fourcc; 161*1a45ef02SThomas Zimmermann ++fourccs; 162*1a45ef02SThomas Zimmermann } 163*1a45ef02SThomas Zimmermann 164*1a45ef02SThomas Zimmermann /* 165*1a45ef02SThomas Zimmermann * The extra formats, emulated by the driver, go second. 166*1a45ef02SThomas Zimmermann */ 167*1a45ef02SThomas Zimmermann 168*1a45ef02SThomas Zimmermann for (i = 0; (i < extra_nfourccs) && (fourccs < fourccs_end); ++i) { 169*1a45ef02SThomas Zimmermann u32 fourcc = extra_fourccs[i]; 170*1a45ef02SThomas Zimmermann 171*1a45ef02SThomas Zimmermann if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) { 172*1a45ef02SThomas Zimmermann continue; /* skip duplicate and native entries */ 173*1a45ef02SThomas Zimmermann } else if (fourccs == fourccs_end) { 174*1a45ef02SThomas Zimmermann drm_warn(dev, "Ignoring emulated format %p4cc\n", &fourcc); 175*1a45ef02SThomas Zimmermann continue; /* end of available output buffer */ 176*1a45ef02SThomas Zimmermann } 177*1a45ef02SThomas Zimmermann 178*1a45ef02SThomas Zimmermann drm_dbg_kms(dev, "adding emulated format %p4cc\n", &fourcc); 179*1a45ef02SThomas Zimmermann 180*1a45ef02SThomas Zimmermann *fourccs = fourcc; 181*1a45ef02SThomas Zimmermann ++fourccs; 182*1a45ef02SThomas Zimmermann } 183*1a45ef02SThomas Zimmermann 184*1a45ef02SThomas Zimmermann return fourccs - fourccs_out; 185*1a45ef02SThomas Zimmermann } 186*1a45ef02SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_build_fourcc_list); 187*1a45ef02SThomas Zimmermann 188314c45e3SThomas Zimmermann int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, 189314c45e3SThomas Zimmermann struct drm_atomic_state *new_state) 190314c45e3SThomas Zimmermann { 191314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 192314c45e3SThomas Zimmermann struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); 193314c45e3SThomas Zimmermann struct drm_shadow_plane_state *new_shadow_plane_state = 194314c45e3SThomas Zimmermann to_drm_shadow_plane_state(new_plane_state); 195314c45e3SThomas Zimmermann struct drm_framebuffer *new_fb = new_plane_state->fb; 196314c45e3SThomas Zimmermann struct drm_crtc *new_crtc = new_plane_state->crtc; 197314c45e3SThomas Zimmermann struct drm_crtc_state *new_crtc_state = NULL; 198314c45e3SThomas Zimmermann struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 199314c45e3SThomas Zimmermann int ret; 200314c45e3SThomas Zimmermann 201314c45e3SThomas Zimmermann if (new_crtc) 202314c45e3SThomas Zimmermann new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 203314c45e3SThomas Zimmermann 204314c45e3SThomas Zimmermann ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 205314c45e3SThomas Zimmermann DRM_PLANE_NO_SCALING, 206314c45e3SThomas Zimmermann DRM_PLANE_NO_SCALING, 207314c45e3SThomas Zimmermann false, false); 208314c45e3SThomas Zimmermann if (ret) 209314c45e3SThomas Zimmermann return ret; 210314c45e3SThomas Zimmermann else if (!new_plane_state->visible) 211314c45e3SThomas Zimmermann return 0; 212314c45e3SThomas Zimmermann 213314c45e3SThomas Zimmermann if (new_fb->format != sysfb->fb_format) { 214314c45e3SThomas Zimmermann void *buf; 215314c45e3SThomas Zimmermann 216314c45e3SThomas Zimmermann /* format conversion necessary; reserve buffer */ 217314c45e3SThomas Zimmermann buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state, 218314c45e3SThomas Zimmermann sysfb->fb_pitch, GFP_KERNEL); 219314c45e3SThomas Zimmermann if (!buf) 220314c45e3SThomas Zimmermann return -ENOMEM; 221314c45e3SThomas Zimmermann } 222314c45e3SThomas Zimmermann 223314c45e3SThomas Zimmermann new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 224314c45e3SThomas Zimmermann 225314c45e3SThomas Zimmermann new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state); 226314c45e3SThomas Zimmermann new_sysfb_crtc_state->format = new_fb->format; 227314c45e3SThomas Zimmermann 228314c45e3SThomas Zimmermann return 0; 229314c45e3SThomas Zimmermann } 230314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check); 231314c45e3SThomas Zimmermann 232314c45e3SThomas Zimmermann void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) 233314c45e3SThomas Zimmermann { 234314c45e3SThomas Zimmermann struct drm_device *dev = plane->dev; 235314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 236314c45e3SThomas Zimmermann struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 237314c45e3SThomas Zimmermann struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 238314c45e3SThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 239314c45e3SThomas Zimmermann struct drm_framebuffer *fb = plane_state->fb; 240314c45e3SThomas Zimmermann unsigned int dst_pitch = sysfb->fb_pitch; 241314c45e3SThomas Zimmermann const struct drm_format_info *dst_format = sysfb->fb_format; 242314c45e3SThomas Zimmermann struct drm_atomic_helper_damage_iter iter; 243314c45e3SThomas Zimmermann struct drm_rect damage; 244314c45e3SThomas Zimmermann int ret, idx; 245314c45e3SThomas Zimmermann 246314c45e3SThomas Zimmermann ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 247314c45e3SThomas Zimmermann if (ret) 248314c45e3SThomas Zimmermann return; 249314c45e3SThomas Zimmermann 250314c45e3SThomas Zimmermann if (!drm_dev_enter(dev, &idx)) 251314c45e3SThomas Zimmermann goto out_drm_gem_fb_end_cpu_access; 252314c45e3SThomas Zimmermann 253314c45e3SThomas Zimmermann drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 254314c45e3SThomas Zimmermann drm_atomic_for_each_plane_damage(&iter, &damage) { 255314c45e3SThomas Zimmermann struct iosys_map dst = sysfb->fb_addr; 256314c45e3SThomas Zimmermann struct drm_rect dst_clip = plane_state->dst; 257314c45e3SThomas Zimmermann 258314c45e3SThomas Zimmermann if (!drm_rect_intersect(&dst_clip, &damage)) 259314c45e3SThomas Zimmermann continue; 260314c45e3SThomas Zimmermann 261314c45e3SThomas Zimmermann iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); 262314c45e3SThomas Zimmermann drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, 263314c45e3SThomas Zimmermann &damage, &shadow_plane_state->fmtcnv_state); 264314c45e3SThomas Zimmermann } 265314c45e3SThomas Zimmermann 266314c45e3SThomas Zimmermann drm_dev_exit(idx); 267314c45e3SThomas Zimmermann out_drm_gem_fb_end_cpu_access: 268314c45e3SThomas Zimmermann drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 269314c45e3SThomas Zimmermann } 270314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update); 271314c45e3SThomas Zimmermann 272314c45e3SThomas Zimmermann void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane, 273314c45e3SThomas Zimmermann struct drm_atomic_state *state) 274314c45e3SThomas Zimmermann { 275314c45e3SThomas Zimmermann struct drm_device *dev = plane->dev; 276314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 277314c45e3SThomas Zimmermann struct iosys_map dst = sysfb->fb_addr; 278314c45e3SThomas Zimmermann struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 279314c45e3SThomas Zimmermann void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ 280314c45e3SThomas Zimmermann unsigned int dst_pitch = sysfb->fb_pitch; 281314c45e3SThomas Zimmermann const struct drm_format_info *dst_format = sysfb->fb_format; 282314c45e3SThomas Zimmermann struct drm_rect dst_clip; 283314c45e3SThomas Zimmermann unsigned long lines, linepixels, i; 284314c45e3SThomas Zimmermann int idx; 285314c45e3SThomas Zimmermann 286314c45e3SThomas Zimmermann drm_rect_init(&dst_clip, 287314c45e3SThomas Zimmermann plane_state->src_x >> 16, plane_state->src_y >> 16, 288314c45e3SThomas Zimmermann plane_state->src_w >> 16, plane_state->src_h >> 16); 289314c45e3SThomas Zimmermann 290314c45e3SThomas Zimmermann lines = drm_rect_height(&dst_clip); 291314c45e3SThomas Zimmermann linepixels = drm_rect_width(&dst_clip); 292314c45e3SThomas Zimmermann 293314c45e3SThomas Zimmermann if (!drm_dev_enter(dev, &idx)) 294314c45e3SThomas Zimmermann return; 295314c45e3SThomas Zimmermann 296314c45e3SThomas Zimmermann /* Clear buffer to black if disabled */ 297314c45e3SThomas Zimmermann dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); 298314c45e3SThomas Zimmermann for (i = 0; i < lines; ++i) { 299314c45e3SThomas Zimmermann memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); 300314c45e3SThomas Zimmermann dst_vmap += dst_pitch; 301314c45e3SThomas Zimmermann } 302314c45e3SThomas Zimmermann 303314c45e3SThomas Zimmermann drm_dev_exit(idx); 304314c45e3SThomas Zimmermann } 305314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable); 306314c45e3SThomas Zimmermann 307314c45e3SThomas Zimmermann int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, 308314c45e3SThomas Zimmermann struct drm_scanout_buffer *sb) 309314c45e3SThomas Zimmermann { 310314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 311314c45e3SThomas Zimmermann 312314c45e3SThomas Zimmermann sb->width = sysfb->fb_mode.hdisplay; 313314c45e3SThomas Zimmermann sb->height = sysfb->fb_mode.vdisplay; 314314c45e3SThomas Zimmermann sb->format = sysfb->fb_format; 315314c45e3SThomas Zimmermann sb->pitch[0] = sysfb->fb_pitch; 316314c45e3SThomas Zimmermann sb->map[0] = sysfb->fb_addr; 317314c45e3SThomas Zimmermann 318314c45e3SThomas Zimmermann return 0; 319314c45e3SThomas Zimmermann } 320314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer); 321314c45e3SThomas Zimmermann 322314c45e3SThomas Zimmermann /* 323314c45e3SThomas Zimmermann * CRTC 324314c45e3SThomas Zimmermann */ 325314c45e3SThomas Zimmermann 326314c45e3SThomas Zimmermann static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state) 327314c45e3SThomas Zimmermann { 328314c45e3SThomas Zimmermann __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base); 329314c45e3SThomas Zimmermann 330314c45e3SThomas Zimmermann kfree(sysfb_crtc_state); 331314c45e3SThomas Zimmermann } 332314c45e3SThomas Zimmermann 333314c45e3SThomas Zimmermann enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc, 334314c45e3SThomas Zimmermann const struct drm_display_mode *mode) 335314c45e3SThomas Zimmermann { 336314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 337314c45e3SThomas Zimmermann 338314c45e3SThomas Zimmermann return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode); 339314c45e3SThomas Zimmermann } 340314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid); 341314c45e3SThomas Zimmermann 342314c45e3SThomas Zimmermann int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) 343314c45e3SThomas Zimmermann { 344314c45e3SThomas Zimmermann struct drm_device *dev = crtc->dev; 345314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 346314c45e3SThomas Zimmermann struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); 347314c45e3SThomas Zimmermann int ret; 348314c45e3SThomas Zimmermann 349314c45e3SThomas Zimmermann if (!new_crtc_state->enable) 350314c45e3SThomas Zimmermann return 0; 351314c45e3SThomas Zimmermann 352314c45e3SThomas Zimmermann ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); 353314c45e3SThomas Zimmermann if (ret) 354314c45e3SThomas Zimmermann return ret; 355314c45e3SThomas Zimmermann 356314c45e3SThomas Zimmermann if (new_crtc_state->color_mgmt_changed) { 357314c45e3SThomas Zimmermann const size_t gamma_lut_length = 358314c45e3SThomas Zimmermann sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut); 359314c45e3SThomas Zimmermann const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; 360314c45e3SThomas Zimmermann 361314c45e3SThomas Zimmermann if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { 362314c45e3SThomas Zimmermann drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length); 363314c45e3SThomas Zimmermann return -EINVAL; 364314c45e3SThomas Zimmermann } 365314c45e3SThomas Zimmermann } 366314c45e3SThomas Zimmermann 367314c45e3SThomas Zimmermann return 0; 368314c45e3SThomas Zimmermann } 369314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check); 370314c45e3SThomas Zimmermann 371314c45e3SThomas Zimmermann void drm_sysfb_crtc_reset(struct drm_crtc *crtc) 372314c45e3SThomas Zimmermann { 373314c45e3SThomas Zimmermann struct drm_sysfb_crtc_state *sysfb_crtc_state; 374314c45e3SThomas Zimmermann 375314c45e3SThomas Zimmermann if (crtc->state) 376314c45e3SThomas Zimmermann drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state)); 377314c45e3SThomas Zimmermann 378314c45e3SThomas Zimmermann sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL); 379314c45e3SThomas Zimmermann if (sysfb_crtc_state) 380314c45e3SThomas Zimmermann __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base); 381314c45e3SThomas Zimmermann else 382314c45e3SThomas Zimmermann __drm_atomic_helper_crtc_reset(crtc, NULL); 383314c45e3SThomas Zimmermann } 384314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_reset); 385314c45e3SThomas Zimmermann 386314c45e3SThomas Zimmermann struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 387314c45e3SThomas Zimmermann { 388314c45e3SThomas Zimmermann struct drm_device *dev = crtc->dev; 389314c45e3SThomas Zimmermann struct drm_crtc_state *crtc_state = crtc->state; 390314c45e3SThomas Zimmermann struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 391314c45e3SThomas Zimmermann struct drm_sysfb_crtc_state *sysfb_crtc_state; 392314c45e3SThomas Zimmermann 393314c45e3SThomas Zimmermann if (drm_WARN_ON(dev, !crtc_state)) 394314c45e3SThomas Zimmermann return NULL; 395314c45e3SThomas Zimmermann 396314c45e3SThomas Zimmermann new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL); 397314c45e3SThomas Zimmermann if (!new_sysfb_crtc_state) 398314c45e3SThomas Zimmermann return NULL; 399314c45e3SThomas Zimmermann 400314c45e3SThomas Zimmermann sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 401314c45e3SThomas Zimmermann 402314c45e3SThomas Zimmermann __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base); 403314c45e3SThomas Zimmermann new_sysfb_crtc_state->format = sysfb_crtc_state->format; 404314c45e3SThomas Zimmermann 405314c45e3SThomas Zimmermann return &new_sysfb_crtc_state->base; 406314c45e3SThomas Zimmermann } 407314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state); 408314c45e3SThomas Zimmermann 409314c45e3SThomas Zimmermann void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) 410314c45e3SThomas Zimmermann { 411314c45e3SThomas Zimmermann drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state)); 412314c45e3SThomas Zimmermann } 413314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state); 414314c45e3SThomas Zimmermann 415314c45e3SThomas Zimmermann /* 416314c45e3SThomas Zimmermann * Connector 417314c45e3SThomas Zimmermann */ 418314c45e3SThomas Zimmermann 419314c45e3SThomas Zimmermann static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) 420314c45e3SThomas Zimmermann { 421314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = data; 422314c45e3SThomas Zimmermann const u8 *edid = sysfb->edid; 423314c45e3SThomas Zimmermann size_t off = block * EDID_LENGTH; 424314c45e3SThomas Zimmermann size_t end = off + len; 425314c45e3SThomas Zimmermann 426314c45e3SThomas Zimmermann if (!edid) 427314c45e3SThomas Zimmermann return -EINVAL; 428314c45e3SThomas Zimmermann if (end > EDID_LENGTH) 429314c45e3SThomas Zimmermann return -EINVAL; 430314c45e3SThomas Zimmermann memcpy(buf, &edid[off], len); 431314c45e3SThomas Zimmermann 432314c45e3SThomas Zimmermann /* 433314c45e3SThomas Zimmermann * We don't have EDID extensions available and reporting them 434314c45e3SThomas Zimmermann * will upset DRM helpers. Thus clear the extension field and 435314c45e3SThomas Zimmermann * update the checksum. Adding the extension flag to the checksum 436314c45e3SThomas Zimmermann * does this. 437314c45e3SThomas Zimmermann */ 438314c45e3SThomas Zimmermann buf[127] += buf[126]; 439314c45e3SThomas Zimmermann buf[126] = 0; 440314c45e3SThomas Zimmermann 441314c45e3SThomas Zimmermann return 0; 442314c45e3SThomas Zimmermann } 443314c45e3SThomas Zimmermann 444314c45e3SThomas Zimmermann int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector) 445314c45e3SThomas Zimmermann { 446314c45e3SThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev); 447314c45e3SThomas Zimmermann const struct drm_edid *drm_edid; 448314c45e3SThomas Zimmermann 449314c45e3SThomas Zimmermann if (sysfb->edid) { 450314c45e3SThomas Zimmermann drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb); 451314c45e3SThomas Zimmermann drm_edid_connector_update(connector, drm_edid); 452314c45e3SThomas Zimmermann drm_edid_free(drm_edid); 453314c45e3SThomas Zimmermann } 454314c45e3SThomas Zimmermann 455314c45e3SThomas Zimmermann /* Return the fixed mode even with EDID */ 456314c45e3SThomas Zimmermann return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode); 457314c45e3SThomas Zimmermann } 458314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes); 459