xref: /openbmc/linux/drivers/gpu/drm/arm/malidp_mw.c (revision ba61bb17)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
4  * Author: Brian Starkey <brian.starkey@arm.com>
5  *
6  * ARM Mali DP Writeback connector implementation
7  */
8 #include <drm/drm_atomic.h>
9 #include <drm/drm_atomic_helper.h>
10 #include <drm/drm_crtc.h>
11 #include <drm/drm_crtc_helper.h>
12 #include <drm/drm_fb_cma_helper.h>
13 #include <drm/drm_gem_cma_helper.h>
14 #include <drm/drmP.h>
15 #include <drm/drm_writeback.h>
16 
17 #include "malidp_drv.h"
18 #include "malidp_hw.h"
19 #include "malidp_mw.h"
20 
21 #define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)
22 
23 struct malidp_mw_connector_state {
24 	struct drm_connector_state base;
25 	dma_addr_t addrs[2];
26 	s32 pitches[2];
27 	u8 format;
28 	u8 n_planes;
29 };
30 
31 static int malidp_mw_connector_get_modes(struct drm_connector *connector)
32 {
33 	struct drm_device *dev = connector->dev;
34 
35 	return drm_add_modes_noedid(connector, dev->mode_config.max_width,
36 				    dev->mode_config.max_height);
37 }
38 
39 static enum drm_mode_status
40 malidp_mw_connector_mode_valid(struct drm_connector *connector,
41 			       struct drm_display_mode *mode)
42 {
43 	struct drm_device *dev = connector->dev;
44 	struct drm_mode_config *mode_config = &dev->mode_config;
45 	int w = mode->hdisplay, h = mode->vdisplay;
46 
47 	if ((w < mode_config->min_width) || (w > mode_config->max_width))
48 		return MODE_BAD_HVALUE;
49 
50 	if ((h < mode_config->min_height) || (h > mode_config->max_height))
51 		return MODE_BAD_VVALUE;
52 
53 	return MODE_OK;
54 }
55 
56 const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = {
57 	.get_modes = malidp_mw_connector_get_modes,
58 	.mode_valid = malidp_mw_connector_mode_valid,
59 };
60 
61 static void malidp_mw_connector_reset(struct drm_connector *connector)
62 {
63 	struct malidp_mw_connector_state *mw_state =
64 		kzalloc(sizeof(*mw_state), GFP_KERNEL);
65 
66 	if (connector->state)
67 		__drm_atomic_helper_connector_destroy_state(connector->state);
68 
69 	kfree(connector->state);
70 	__drm_atomic_helper_connector_reset(connector, &mw_state->base);
71 }
72 
73 static enum drm_connector_status
74 malidp_mw_connector_detect(struct drm_connector *connector, bool force)
75 {
76 	return connector_status_disconnected;
77 }
78 
79 static void malidp_mw_connector_destroy(struct drm_connector *connector)
80 {
81 	drm_connector_cleanup(connector);
82 }
83 
84 static struct drm_connector_state *
85 malidp_mw_connector_duplicate_state(struct drm_connector *connector)
86 {
87 	struct malidp_mw_connector_state *mw_state;
88 
89 	if (WARN_ON(!connector->state))
90 		return NULL;
91 
92 	mw_state = kzalloc(sizeof(*mw_state), GFP_KERNEL);
93 	if (!mw_state)
94 		return NULL;
95 
96 	/* No need to preserve any of our driver-local data */
97 	__drm_atomic_helper_connector_duplicate_state(connector, &mw_state->base);
98 
99 	return &mw_state->base;
100 }
101 
102 static const struct drm_connector_funcs malidp_mw_connector_funcs = {
103 	.reset = malidp_mw_connector_reset,
104 	.detect = malidp_mw_connector_detect,
105 	.fill_modes = drm_helper_probe_single_connector_modes,
106 	.destroy = malidp_mw_connector_destroy,
107 	.atomic_duplicate_state = malidp_mw_connector_duplicate_state,
108 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
109 };
110 
111 static int
112 malidp_mw_encoder_atomic_check(struct drm_encoder *encoder,
113 			       struct drm_crtc_state *crtc_state,
114 			       struct drm_connector_state *conn_state)
115 {
116 	struct malidp_mw_connector_state *mw_state = to_mw_state(conn_state);
117 	struct malidp_drm *malidp = encoder->dev->dev_private;
118 	struct drm_framebuffer *fb;
119 	int i, n_planes;
120 
121 	if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
122 		return 0;
123 
124 	fb = conn_state->writeback_job->fb;
125 	if ((fb->width != crtc_state->mode.hdisplay) ||
126 	    (fb->height != crtc_state->mode.vdisplay)) {
127 		DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
128 				fb->width, fb->height);
129 		return -EINVAL;
130 	}
131 
132 	mw_state->format =
133 		malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE,
134 					fb->format->format);
135 	if (mw_state->format == MALIDP_INVALID_FORMAT_ID) {
136 		struct drm_format_name_buf format_name;
137 
138 		DRM_DEBUG_KMS("Invalid pixel format %s\n",
139 			      drm_get_format_name(fb->format->format,
140 						  &format_name));
141 		return -EINVAL;
142 	}
143 
144 	n_planes = drm_format_num_planes(fb->format->format);
145 	for (i = 0; i < n_planes; i++) {
146 		struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i);
147 		/* memory write buffers are never rotated */
148 		u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 0);
149 
150 		if (fb->pitches[i] & (alignment - 1)) {
151 			DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
152 				      fb->pitches[i], i);
153 			return -EINVAL;
154 		}
155 		mw_state->pitches[i] = fb->pitches[i];
156 		mw_state->addrs[i] = obj->paddr + fb->offsets[i];
157 	}
158 	mw_state->n_planes = n_planes;
159 
160 	return 0;
161 }
162 
163 static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = {
164 	.atomic_check = malidp_mw_encoder_atomic_check,
165 };
166 
167 static u32 *get_writeback_formats(struct malidp_drm *malidp, int *n_formats)
168 {
169 	const struct malidp_hw_regmap *map = &malidp->dev->hw->map;
170 	u32 *formats;
171 	int n, i;
172 
173 	formats = kcalloc(map->n_pixel_formats, sizeof(*formats),
174 			  GFP_KERNEL);
175 	if (!formats)
176 		return NULL;
177 
178 	for (n = 0, i = 0;  i < map->n_pixel_formats; i++) {
179 		if (map->pixel_formats[i].layer & SE_MEMWRITE)
180 			formats[n++] = map->pixel_formats[i].format;
181 	}
182 
183 	*n_formats = n;
184 
185 	return formats;
186 }
187 
188 int malidp_mw_connector_init(struct drm_device *drm)
189 {
190 	struct malidp_drm *malidp = drm->dev_private;
191 	u32 *formats;
192 	int ret, n_formats;
193 
194 	if (!malidp->dev->hw->enable_memwrite)
195 		return 0;
196 
197 	malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc);
198 	drm_connector_helper_add(&malidp->mw_connector.base,
199 				 &malidp_mw_connector_helper_funcs);
200 
201 	formats = get_writeback_formats(malidp, &n_formats);
202 	if (!formats)
203 		return -ENOMEM;
204 
205 	ret = drm_writeback_connector_init(drm, &malidp->mw_connector,
206 					   &malidp_mw_connector_funcs,
207 					   &malidp_mw_encoder_helper_funcs,
208 					   formats, n_formats);
209 	kfree(formats);
210 	if (ret)
211 		return ret;
212 
213 	return 0;
214 }
215 
216 void malidp_mw_atomic_commit(struct drm_device *drm,
217 			     struct drm_atomic_state *old_state)
218 {
219 	struct malidp_drm *malidp = drm->dev_private;
220 	struct drm_writeback_connector *mw_conn = &malidp->mw_connector;
221 	struct drm_connector_state *conn_state = mw_conn->base.state;
222 	struct malidp_hw_device *hwdev = malidp->dev;
223 	struct malidp_mw_connector_state *mw_state;
224 
225 	if (!conn_state)
226 		return;
227 
228 	mw_state = to_mw_state(conn_state);
229 
230 	if (conn_state->writeback_job && conn_state->writeback_job->fb) {
231 		struct drm_framebuffer *fb = conn_state->writeback_job->fb;
232 
233 		DRM_DEV_DEBUG_DRIVER(drm->dev,
234 				     "Enable memwrite %ux%u:%d %pad fmt: %u\n",
235 				     fb->width, fb->height,
236 				     mw_state->pitches[0],
237 				     &mw_state->addrs[0],
238 				     mw_state->format);
239 
240 		drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
241 		conn_state->writeback_job = NULL;
242 
243 		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
244 					   mw_state->pitches, mw_state->n_planes,
245 					   fb->width, fb->height, mw_state->format);
246 	} else {
247 		DRM_DEV_DEBUG_DRIVER(drm->dev, "Disable memwrite\n");
248 		hwdev->hw->disable_memwrite(hwdev);
249 	}
250 }
251