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_mode_fb_cmd2 *mode_cmd) 121 { 122 const struct shmob_drm_format_info *format; 123 124 format = shmob_drm_format_info(mode_cmd->pixel_format); 125 if (format == NULL) { 126 dev_dbg(dev->dev, "unsupported pixel format %p4cc\n", 127 &mode_cmd->pixel_format); 128 return ERR_PTR(-EINVAL); 129 } 130 131 if (mode_cmd->pitches[0] & 7 || mode_cmd->pitches[0] >= 65536) { 132 dev_dbg(dev->dev, "invalid pitch value %u\n", 133 mode_cmd->pitches[0]); 134 return ERR_PTR(-EINVAL); 135 } 136 137 if (shmob_drm_format_is_yuv(format)) { 138 unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1; 139 140 if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) { 141 dev_dbg(dev->dev, 142 "luma and chroma pitches do not match\n"); 143 return ERR_PTR(-EINVAL); 144 } 145 } 146 147 return drm_gem_fb_create(dev, file_priv, mode_cmd); 148 } 149 150 static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = { 151 .fb_create = shmob_drm_fb_create, 152 .atomic_check = drm_atomic_helper_check, 153 .atomic_commit = drm_atomic_helper_commit, 154 }; 155 156 int shmob_drm_modeset_init(struct shmob_drm_device *sdev) 157 { 158 struct drm_device *dev = &sdev->ddev; 159 int ret; 160 161 ret = drmm_mode_config_init(dev); 162 if (ret) 163 return ret; 164 165 ret = shmob_drm_crtc_create(sdev); 166 if (ret < 0) 167 return ret; 168 169 ret = shmob_drm_encoder_create(sdev); 170 if (ret < 0) 171 return ret; 172 173 ret = shmob_drm_connector_create(sdev, &sdev->encoder); 174 if (ret < 0) 175 return ret; 176 177 drm_mode_config_reset(dev); 178 179 drm_kms_helper_poll_init(dev); 180 181 sdev->ddev.mode_config.min_width = 0; 182 sdev->ddev.mode_config.min_height = 0; 183 sdev->ddev.mode_config.max_width = 4095; 184 sdev->ddev.mode_config.max_height = 4095; 185 sdev->ddev.mode_config.funcs = &shmob_drm_mode_config_funcs; 186 187 return 0; 188 } 189