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