1 // SPDX-License-Identifier: GPL-2.0
3 * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
4 * Author: Brian Starkey <brian.starkey@arm.com>
6 * ARM Mali DP Writeback connector implementation
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>
15 #include <drm/drm_writeback.h>
17 #include "malidp_drv.h"
18 #include "malidp_hw.h"
19 #include "malidp_mw.h"
21 #define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)
23 struct malidp_mw_connector_state
{
24 struct drm_connector_state base
;
29 bool rgb2yuv_initialized
;
30 const s16
*rgb2yuv_coeffs
;
33 static int malidp_mw_connector_get_modes(struct drm_connector
*connector
)
35 struct drm_device
*dev
= connector
->dev
;
37 return drm_add_modes_noedid(connector
, dev
->mode_config
.max_width
,
38 dev
->mode_config
.max_height
);
41 static enum drm_mode_status
42 malidp_mw_connector_mode_valid(struct drm_connector
*connector
,
43 struct drm_display_mode
*mode
)
45 struct drm_device
*dev
= connector
->dev
;
46 struct drm_mode_config
*mode_config
= &dev
->mode_config
;
47 int w
= mode
->hdisplay
, h
= mode
->vdisplay
;
49 if ((w
< mode_config
->min_width
) || (w
> mode_config
->max_width
))
50 return MODE_BAD_HVALUE
;
52 if ((h
< mode_config
->min_height
) || (h
> mode_config
->max_height
))
53 return MODE_BAD_VVALUE
;
58 const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs
= {
59 .get_modes
= malidp_mw_connector_get_modes
,
60 .mode_valid
= malidp_mw_connector_mode_valid
,
63 static void malidp_mw_connector_reset(struct drm_connector
*connector
)
65 struct malidp_mw_connector_state
*mw_state
=
66 kzalloc(sizeof(*mw_state
), GFP_KERNEL
);
69 __drm_atomic_helper_connector_destroy_state(connector
->state
);
71 kfree(connector
->state
);
72 __drm_atomic_helper_connector_reset(connector
, &mw_state
->base
);
75 static enum drm_connector_status
76 malidp_mw_connector_detect(struct drm_connector
*connector
, bool force
)
78 return connector_status_connected
;
81 static void malidp_mw_connector_destroy(struct drm_connector
*connector
)
83 drm_connector_cleanup(connector
);
86 static struct drm_connector_state
*
87 malidp_mw_connector_duplicate_state(struct drm_connector
*connector
)
89 struct malidp_mw_connector_state
*mw_state
, *mw_current_state
;
91 if (WARN_ON(!connector
->state
))
94 mw_state
= kzalloc(sizeof(*mw_state
), GFP_KERNEL
);
98 mw_current_state
= to_mw_state(connector
->state
);
99 mw_state
->rgb2yuv_coeffs
= mw_current_state
->rgb2yuv_coeffs
;
100 mw_state
->rgb2yuv_initialized
= mw_current_state
->rgb2yuv_initialized
;
102 __drm_atomic_helper_connector_duplicate_state(connector
, &mw_state
->base
);
104 return &mw_state
->base
;
107 static const struct drm_connector_funcs malidp_mw_connector_funcs
= {
108 .reset
= malidp_mw_connector_reset
,
109 .detect
= malidp_mw_connector_detect
,
110 .fill_modes
= drm_helper_probe_single_connector_modes
,
111 .destroy
= malidp_mw_connector_destroy
,
112 .atomic_duplicate_state
= malidp_mw_connector_duplicate_state
,
113 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
116 static const s16 rgb2yuv_coeffs_bt709_limited
[MALIDP_COLORADJ_NUM_COEFFS
] = {
124 malidp_mw_encoder_atomic_check(struct drm_encoder
*encoder
,
125 struct drm_crtc_state
*crtc_state
,
126 struct drm_connector_state
*conn_state
)
128 struct malidp_mw_connector_state
*mw_state
= to_mw_state(conn_state
);
129 struct malidp_drm
*malidp
= encoder
->dev
->dev_private
;
130 struct drm_framebuffer
*fb
;
133 if (!conn_state
->writeback_job
|| !conn_state
->writeback_job
->fb
)
136 fb
= conn_state
->writeback_job
->fb
;
137 if ((fb
->width
!= crtc_state
->mode
.hdisplay
) ||
138 (fb
->height
!= crtc_state
->mode
.vdisplay
)) {
139 DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
140 fb
->width
, fb
->height
);
145 malidp_hw_get_format_id(&malidp
->dev
->hw
->map
, SE_MEMWRITE
,
147 if (mw_state
->format
== MALIDP_INVALID_FORMAT_ID
) {
148 struct drm_format_name_buf format_name
;
150 DRM_DEBUG_KMS("Invalid pixel format %s\n",
151 drm_get_format_name(fb
->format
->format
,
156 n_planes
= drm_format_num_planes(fb
->format
->format
);
157 for (i
= 0; i
< n_planes
; i
++) {
158 struct drm_gem_cma_object
*obj
= drm_fb_cma_get_gem_obj(fb
, i
);
159 /* memory write buffers are never rotated */
160 u8 alignment
= malidp_hw_get_pitch_align(malidp
->dev
, 0);
162 if (fb
->pitches
[i
] & (alignment
- 1)) {
163 DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
167 mw_state
->pitches
[i
] = fb
->pitches
[i
];
168 mw_state
->addrs
[i
] = obj
->paddr
+ fb
->offsets
[i
];
170 mw_state
->n_planes
= n_planes
;
172 if (fb
->format
->is_yuv
)
173 mw_state
->rgb2yuv_coeffs
= rgb2yuv_coeffs_bt709_limited
;
178 static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs
= {
179 .atomic_check
= malidp_mw_encoder_atomic_check
,
182 static u32
*get_writeback_formats(struct malidp_drm
*malidp
, int *n_formats
)
184 const struct malidp_hw_regmap
*map
= &malidp
->dev
->hw
->map
;
188 formats
= kcalloc(map
->n_pixel_formats
, sizeof(*formats
),
193 for (n
= 0, i
= 0; i
< map
->n_pixel_formats
; i
++) {
194 if (map
->pixel_formats
[i
].layer
& SE_MEMWRITE
)
195 formats
[n
++] = map
->pixel_formats
[i
].format
;
203 int malidp_mw_connector_init(struct drm_device
*drm
)
205 struct malidp_drm
*malidp
= drm
->dev_private
;
209 if (!malidp
->dev
->hw
->enable_memwrite
)
212 malidp
->mw_connector
.encoder
.possible_crtcs
= 1 << drm_crtc_index(&malidp
->crtc
);
213 drm_connector_helper_add(&malidp
->mw_connector
.base
,
214 &malidp_mw_connector_helper_funcs
);
216 formats
= get_writeback_formats(malidp
, &n_formats
);
220 ret
= drm_writeback_connector_init(drm
, &malidp
->mw_connector
,
221 &malidp_mw_connector_funcs
,
222 &malidp_mw_encoder_helper_funcs
,
231 void malidp_mw_atomic_commit(struct drm_device
*drm
,
232 struct drm_atomic_state
*old_state
)
234 struct malidp_drm
*malidp
= drm
->dev_private
;
235 struct drm_writeback_connector
*mw_conn
= &malidp
->mw_connector
;
236 struct drm_connector_state
*conn_state
= mw_conn
->base
.state
;
237 struct malidp_hw_device
*hwdev
= malidp
->dev
;
238 struct malidp_mw_connector_state
*mw_state
;
243 mw_state
= to_mw_state(conn_state
);
245 if (conn_state
->writeback_job
&& conn_state
->writeback_job
->fb
) {
246 struct drm_framebuffer
*fb
= conn_state
->writeback_job
->fb
;
248 DRM_DEV_DEBUG_DRIVER(drm
->dev
,
249 "Enable memwrite %ux%u:%d %pad fmt: %u\n",
250 fb
->width
, fb
->height
,
251 mw_state
->pitches
[0],
255 drm_writeback_queue_job(mw_conn
, conn_state
->writeback_job
);
256 conn_state
->writeback_job
= NULL
;
257 hwdev
->hw
->enable_memwrite(hwdev
, mw_state
->addrs
,
258 mw_state
->pitches
, mw_state
->n_planes
,
259 fb
->width
, fb
->height
, mw_state
->format
,
260 !mw_state
->rgb2yuv_initialized
?
261 mw_state
->rgb2yuv_coeffs
: NULL
);
262 mw_state
->rgb2yuv_initialized
= !!mw_state
->rgb2yuv_coeffs
;
264 DRM_DEV_DEBUG_DRIVER(drm
->dev
, "Disable memwrite\n");
265 hwdev
->hw
->disable_memwrite(hwdev
);