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
9 #include <drm/drm_atomic.h>
10 #include <drm/drm_atomic_helper.h>
11 #include <drm/drm_crtc.h>
12 #include <drm/drm_fb_cma_helper.h>
13 #include <drm/drm_fourcc.h>
14 #include <drm/drm_gem_cma_helper.h>
15 #include <drm/drm_probe_helper.h>
16 #include <drm/drm_writeback.h>
18 #include "malidp_drv.h"
19 #include "malidp_hw.h"
20 #include "malidp_mw.h"
22 #define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)
24 struct malidp_mw_connector_state
{
25 struct drm_connector_state base
;
30 bool rgb2yuv_initialized
;
31 const s16
*rgb2yuv_coeffs
;
34 static int malidp_mw_connector_get_modes(struct drm_connector
*connector
)
36 struct drm_device
*dev
= connector
->dev
;
38 return drm_add_modes_noedid(connector
, dev
->mode_config
.max_width
,
39 dev
->mode_config
.max_height
);
42 static enum drm_mode_status
43 malidp_mw_connector_mode_valid(struct drm_connector
*connector
,
44 struct drm_display_mode
*mode
)
46 struct drm_device
*dev
= connector
->dev
;
47 struct drm_mode_config
*mode_config
= &dev
->mode_config
;
48 int w
= mode
->hdisplay
, h
= mode
->vdisplay
;
50 if ((w
< mode_config
->min_width
) || (w
> mode_config
->max_width
))
51 return MODE_BAD_HVALUE
;
53 if ((h
< mode_config
->min_height
) || (h
> mode_config
->max_height
))
54 return MODE_BAD_VVALUE
;
59 static const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs
= {
60 .get_modes
= malidp_mw_connector_get_modes
,
61 .mode_valid
= malidp_mw_connector_mode_valid
,
64 static void malidp_mw_connector_reset(struct drm_connector
*connector
)
66 struct malidp_mw_connector_state
*mw_state
=
67 kzalloc(sizeof(*mw_state
), GFP_KERNEL
);
70 __drm_atomic_helper_connector_destroy_state(connector
->state
);
72 kfree(connector
->state
);
73 __drm_atomic_helper_connector_reset(connector
, &mw_state
->base
);
76 static enum drm_connector_status
77 malidp_mw_connector_detect(struct drm_connector
*connector
, bool force
)
79 return connector_status_connected
;
82 static void malidp_mw_connector_destroy(struct drm_connector
*connector
)
84 drm_connector_cleanup(connector
);
87 static struct drm_connector_state
*
88 malidp_mw_connector_duplicate_state(struct drm_connector
*connector
)
90 struct malidp_mw_connector_state
*mw_state
, *mw_current_state
;
92 if (WARN_ON(!connector
->state
))
95 mw_state
= kzalloc(sizeof(*mw_state
), GFP_KERNEL
);
99 mw_current_state
= to_mw_state(connector
->state
);
100 mw_state
->rgb2yuv_coeffs
= mw_current_state
->rgb2yuv_coeffs
;
101 mw_state
->rgb2yuv_initialized
= mw_current_state
->rgb2yuv_initialized
;
103 __drm_atomic_helper_connector_duplicate_state(connector
, &mw_state
->base
);
105 return &mw_state
->base
;
108 static const struct drm_connector_funcs malidp_mw_connector_funcs
= {
109 .reset
= malidp_mw_connector_reset
,
110 .detect
= malidp_mw_connector_detect
,
111 .fill_modes
= drm_helper_probe_single_connector_modes
,
112 .destroy
= malidp_mw_connector_destroy
,
113 .atomic_duplicate_state
= malidp_mw_connector_duplicate_state
,
114 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
117 static const s16 rgb2yuv_coeffs_bt709_limited
[MALIDP_COLORADJ_NUM_COEFFS
] = {
125 malidp_mw_encoder_atomic_check(struct drm_encoder
*encoder
,
126 struct drm_crtc_state
*crtc_state
,
127 struct drm_connector_state
*conn_state
)
129 struct malidp_mw_connector_state
*mw_state
= to_mw_state(conn_state
);
130 struct malidp_drm
*malidp
= encoder
->dev
->dev_private
;
131 struct drm_framebuffer
*fb
;
134 if (!conn_state
->writeback_job
)
137 fb
= conn_state
->writeback_job
->fb
;
138 if ((fb
->width
!= crtc_state
->mode
.hdisplay
) ||
139 (fb
->height
!= crtc_state
->mode
.vdisplay
)) {
140 DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
141 fb
->width
, fb
->height
);
146 DRM_DEBUG_KMS("Writeback framebuffer does not support modifiers\n");
151 malidp_hw_get_format_id(&malidp
->dev
->hw
->map
, SE_MEMWRITE
,
152 fb
->format
->format
, !!fb
->modifier
);
153 if (mw_state
->format
== MALIDP_INVALID_FORMAT_ID
) {
154 struct drm_format_name_buf format_name
;
156 DRM_DEBUG_KMS("Invalid pixel format %s\n",
157 drm_get_format_name(fb
->format
->format
,
162 n_planes
= fb
->format
->num_planes
;
163 for (i
= 0; i
< n_planes
; i
++) {
164 struct drm_gem_cma_object
*obj
= drm_fb_cma_get_gem_obj(fb
, i
);
165 /* memory write buffers are never rotated */
166 u8 alignment
= malidp_hw_get_pitch_align(malidp
->dev
, 0);
168 if (fb
->pitches
[i
] & (alignment
- 1)) {
169 DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
173 mw_state
->pitches
[i
] = fb
->pitches
[i
];
174 mw_state
->addrs
[i
] = obj
->paddr
+ fb
->offsets
[i
];
176 mw_state
->n_planes
= n_planes
;
178 if (fb
->format
->is_yuv
)
179 mw_state
->rgb2yuv_coeffs
= rgb2yuv_coeffs_bt709_limited
;
184 static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs
= {
185 .atomic_check
= malidp_mw_encoder_atomic_check
,
188 static u32
*get_writeback_formats(struct malidp_drm
*malidp
, int *n_formats
)
190 const struct malidp_hw_regmap
*map
= &malidp
->dev
->hw
->map
;
194 formats
= kcalloc(map
->n_pixel_formats
, sizeof(*formats
),
199 for (n
= 0, i
= 0; i
< map
->n_pixel_formats
; i
++) {
200 if (map
->pixel_formats
[i
].layer
& SE_MEMWRITE
)
201 formats
[n
++] = map
->pixel_formats
[i
].format
;
209 int malidp_mw_connector_init(struct drm_device
*drm
)
211 struct malidp_drm
*malidp
= drm
->dev_private
;
215 if (!malidp
->dev
->hw
->enable_memwrite
)
218 malidp
->mw_connector
.encoder
.possible_crtcs
= 1 << drm_crtc_index(&malidp
->crtc
);
219 drm_connector_helper_add(&malidp
->mw_connector
.base
,
220 &malidp_mw_connector_helper_funcs
);
222 formats
= get_writeback_formats(malidp
, &n_formats
);
226 ret
= drm_writeback_connector_init(drm
, &malidp
->mw_connector
,
227 &malidp_mw_connector_funcs
,
228 &malidp_mw_encoder_helper_funcs
,
237 void malidp_mw_atomic_commit(struct drm_device
*drm
,
238 struct drm_atomic_state
*old_state
)
240 struct malidp_drm
*malidp
= drm
->dev_private
;
241 struct drm_writeback_connector
*mw_conn
= &malidp
->mw_connector
;
242 struct drm_connector_state
*conn_state
= mw_conn
->base
.state
;
243 struct malidp_hw_device
*hwdev
= malidp
->dev
;
244 struct malidp_mw_connector_state
*mw_state
;
249 mw_state
= to_mw_state(conn_state
);
251 if (conn_state
->writeback_job
) {
252 struct drm_framebuffer
*fb
= conn_state
->writeback_job
->fb
;
254 DRM_DEV_DEBUG_DRIVER(drm
->dev
,
255 "Enable memwrite %ux%u:%d %pad fmt: %u\n",
256 fb
->width
, fb
->height
,
257 mw_state
->pitches
[0],
261 drm_writeback_queue_job(mw_conn
, conn_state
);
262 hwdev
->hw
->enable_memwrite(hwdev
, mw_state
->addrs
,
263 mw_state
->pitches
, mw_state
->n_planes
,
264 fb
->width
, fb
->height
, mw_state
->format
,
265 !mw_state
->rgb2yuv_initialized
?
266 mw_state
->rgb2yuv_coeffs
: NULL
);
267 mw_state
->rgb2yuv_initialized
= !!mw_state
->rgb2yuv_coeffs
;
269 DRM_DEV_DEBUG_DRIVER(drm
->dev
, "Disable memwrite\n");
270 hwdev
->hw
->disable_memwrite(hwdev
);