1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * shmob_drm_kms.c -- SH Mobile DRM Mode Setting 4 * 5 * Copyright (C) 2012 Renesas Electronics Corporation 6 * 7 * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10 #include <drm/drm_atomic_helper.h> 11 #include <drm/drm_crtc.h> 12 #include <drm/drm_crtc_helper.h> 13 #include <drm/drm_fourcc.h> 14 #include <drm/drm_gem_dma_helper.h> 15 #include <drm/drm_gem_framebuffer_helper.h> 16 #include <drm/drm_probe_helper.h> 17 18 #include "shmob_drm_crtc.h" 19 #include "shmob_drm_drv.h" 20 #include "shmob_drm_kms.h" 21 #include "shmob_drm_plane.h" 22 #include "shmob_drm_regs.h" 23 24 /* ----------------------------------------------------------------------------- 25 * Format helpers 26 */ 27 28 static const struct shmob_drm_format_info shmob_drm_format_infos[] = { 29 { 30 .fourcc = DRM_FORMAT_RGB565, 31 .bpp = 16, 32 .lddfr = LDDFR_PKF_RGB16, 33 .ldddsr = LDDDSR_LS | LDDDSR_WS, 34 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 35 LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16, 36 }, { 37 .fourcc = DRM_FORMAT_RGB888, 38 .bpp = 24, 39 .lddfr = LDDFR_PKF_RGB24, 40 .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, 41 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 42 LDBBSIFR_SWPB | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24, 43 }, { 44 .fourcc = DRM_FORMAT_ARGB8888, 45 .bpp = 32, 46 .lddfr = LDDFR_PKF_ARGB32, 47 .ldddsr = LDDDSR_LS, 48 .ldbbsifr = LDBBSIFR_AL_PK | LDBBSIFR_SWPL | LDBBSIFR_RY | 49 LDBBSIFR_RPKF_ARGB32, 50 }, { 51 .fourcc = DRM_FORMAT_XRGB8888, 52 .bpp = 32, 53 .lddfr = LDDFR_PKF_ARGB32, 54 .ldddsr = LDDDSR_LS, 55 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_RY | 56 LDBBSIFR_RPKF_ARGB32, 57 }, { 58 .fourcc = DRM_FORMAT_NV12, 59 .bpp = 12, 60 .lddfr = LDDFR_CC | LDDFR_YF_420, 61 .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, 62 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 63 LDBBSIFR_SWPB | LDBBSIFR_CHRR_420, 64 }, { 65 .fourcc = DRM_FORMAT_NV21, 66 .bpp = 12, 67 .lddfr = LDDFR_CC | LDDFR_YF_420, 68 .ldddsr = LDDDSR_LS | LDDDSR_WS, 69 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 70 LDBBSIFR_CHRR_420, 71 }, { 72 .fourcc = DRM_FORMAT_NV16, 73 .bpp = 16, 74 .lddfr = LDDFR_CC | LDDFR_YF_422, 75 .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, 76 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 77 LDBBSIFR_SWPB | LDBBSIFR_CHRR_422, 78 }, { 79 .fourcc = DRM_FORMAT_NV61, 80 .bpp = 16, 81 .lddfr = LDDFR_CC | LDDFR_YF_422, 82 .ldddsr = LDDDSR_LS | LDDDSR_WS, 83 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 84 LDBBSIFR_CHRR_422, 85 }, { 86 .fourcc = DRM_FORMAT_NV24, 87 .bpp = 24, 88 .lddfr = LDDFR_CC | LDDFR_YF_444, 89 .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, 90 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 91 LDBBSIFR_SWPB | LDBBSIFR_CHRR_444, 92 }, { 93 .fourcc = DRM_FORMAT_NV42, 94 .bpp = 24, 95 .lddfr = LDDFR_CC | LDDFR_YF_444, 96 .ldddsr = LDDDSR_LS | LDDDSR_WS, 97 .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | 98 LDBBSIFR_CHRR_444, 99 }, 100 }; 101 102 const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc) 103 { 104 unsigned int i; 105 106 for (i = 0; i < ARRAY_SIZE(shmob_drm_format_infos); ++i) { 107 if (shmob_drm_format_infos[i].fourcc == fourcc) 108 return &shmob_drm_format_infos[i]; 109 } 110 111 return NULL; 112 } 113 114 /* ----------------------------------------------------------------------------- 115 * Frame buffer 116 */ 117 118 static struct drm_framebuffer * 119 shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv, 120 const struct drm_format_info *info, 121 const struct drm_mode_fb_cmd2 *mode_cmd) 122 { 123 const struct shmob_drm_format_info *format; 124 125 format = shmob_drm_format_info(mode_cmd->pixel_format); 126 if (format == NULL) { 127 dev_dbg(dev->dev, "unsupported pixel format %p4cc\n", 128 &mode_cmd->pixel_format); 129 return ERR_PTR(-EINVAL); 130 } 131 132 if (mode_cmd->pitches[0] & 7 || mode_cmd->pitches[0] >= 65536) { 133 dev_dbg(dev->dev, "invalid pitch value %u\n", 134 mode_cmd->pitches[0]); 135 return ERR_PTR(-EINVAL); 136 } 137 138 if (shmob_drm_format_is_yuv(format)) { 139 unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1; 140 141 if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) { 142 dev_dbg(dev->dev, 143 "luma and chroma pitches do not match\n"); 144 return ERR_PTR(-EINVAL); 145 } 146 } 147 148 return drm_gem_fb_create(dev, file_priv, info, mode_cmd); 149 } 150 151 static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = { 152 .fb_create = shmob_drm_fb_create, 153 .atomic_check = drm_atomic_helper_check, 154 .atomic_commit = drm_atomic_helper_commit, 155 }; 156 157 int shmob_drm_modeset_init(struct shmob_drm_device *sdev) 158 { 159 struct drm_device *dev = &sdev->ddev; 160 int ret; 161 162 ret = drmm_mode_config_init(dev); 163 if (ret) 164 return ret; 165 166 ret = shmob_drm_crtc_create(sdev); 167 if (ret < 0) 168 return ret; 169 170 ret = shmob_drm_encoder_create(sdev); 171 if (ret < 0) 172 return ret; 173 174 ret = shmob_drm_connector_create(sdev, &sdev->encoder); 175 if (ret < 0) 176 return ret; 177 178 drm_mode_config_reset(dev); 179 180 drm_kms_helper_poll_init(dev); 181 182 sdev->ddev.mode_config.min_width = 0; 183 sdev->ddev.mode_config.min_height = 0; 184 sdev->ddev.mode_config.max_width = 4095; 185 sdev->ddev.mode_config.max_height = 4095; 186 sdev->ddev.mode_config.funcs = &shmob_drm_mode_config_funcs; 187 188 return 0; 189 } 190