xref: /openbmc/linux/drivers/gpu/drm/arm/malidp_drv.c (revision bdecd835)
1ad49f860SLiviu Dudau /*
2ad49f860SLiviu Dudau  * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
3ad49f860SLiviu Dudau  * Author: Liviu Dudau <Liviu.Dudau@arm.com>
4ad49f860SLiviu Dudau  *
5ad49f860SLiviu Dudau  * This program is free software and is provided to you under the terms of the
6ad49f860SLiviu Dudau  * GNU General Public License version 2 as published by the Free Software
7ad49f860SLiviu Dudau  * Foundation, and any use by you of this program is subject to the terms
8ad49f860SLiviu Dudau  * of such GNU licence.
9ad49f860SLiviu Dudau  *
10ad49f860SLiviu Dudau  * ARM Mali DP500/DP550/DP650 KMS/DRM driver
11ad49f860SLiviu Dudau  */
12ad49f860SLiviu Dudau 
13ad49f860SLiviu Dudau #include <linux/module.h>
14ad49f860SLiviu Dudau #include <linux/clk.h>
15ad49f860SLiviu Dudau #include <linux/component.h>
16ad49f860SLiviu Dudau #include <linux/of_device.h>
17ad49f860SLiviu Dudau #include <linux/of_graph.h>
18ad49f860SLiviu Dudau #include <linux/of_reserved_mem.h>
1985f64218SLiviu Dudau #include <linux/pm_runtime.h>
20ad49f860SLiviu Dudau 
21ad49f860SLiviu Dudau #include <drm/drmP.h>
22ad49f860SLiviu Dudau #include <drm/drm_atomic.h>
23ad49f860SLiviu Dudau #include <drm/drm_atomic_helper.h>
24ad49f860SLiviu Dudau #include <drm/drm_crtc.h>
25ad49f860SLiviu Dudau #include <drm/drm_crtc_helper.h>
26bdecd835SNoralf Trønnes #include <drm/drm_fb_helper.h>
27ad49f860SLiviu Dudau #include <drm/drm_fb_cma_helper.h>
28ad49f860SLiviu Dudau #include <drm/drm_gem_cma_helper.h>
29783f7d97SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h>
30194b8799SNoralf Trønnes #include <drm/drm_modeset_helper.h>
31ad49f860SLiviu Dudau #include <drm/drm_of.h>
32ad49f860SLiviu Dudau 
33ad49f860SLiviu Dudau #include "malidp_drv.h"
34ad49f860SLiviu Dudau #include "malidp_regs.h"
35ad49f860SLiviu Dudau #include "malidp_hw.h"
36ad49f860SLiviu Dudau 
37ad49f860SLiviu Dudau #define MALIDP_CONF_VALID_TIMEOUT	250
38ad49f860SLiviu Dudau 
3902725d31SMihail Atanassov static void malidp_write_gamma_table(struct malidp_hw_device *hwdev,
4002725d31SMihail Atanassov 				     u32 data[MALIDP_COEFFTAB_NUM_COEFFS])
4102725d31SMihail Atanassov {
4202725d31SMihail Atanassov 	int i;
4302725d31SMihail Atanassov 	/* Update all channels with a single gamma curve. */
4402725d31SMihail Atanassov 	const u32 gamma_write_mask = GENMASK(18, 16);
4502725d31SMihail Atanassov 	/*
4602725d31SMihail Atanassov 	 * Always write an entire table, so the address field in
4702725d31SMihail Atanassov 	 * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask
4802725d31SMihail Atanassov 	 * directly.
4902725d31SMihail Atanassov 	 */
5002725d31SMihail Atanassov 	malidp_hw_write(hwdev, gamma_write_mask,
51a6993b21SLiviu Dudau 			hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR);
5202725d31SMihail Atanassov 	for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i)
5302725d31SMihail Atanassov 		malidp_hw_write(hwdev, data[i],
54a6993b21SLiviu Dudau 				hwdev->hw->map.coeffs_base +
5502725d31SMihail Atanassov 				MALIDP_COEF_TABLE_DATA);
5602725d31SMihail Atanassov }
5702725d31SMihail Atanassov 
5802725d31SMihail Atanassov static void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc,
5902725d31SMihail Atanassov 					      struct drm_crtc_state *old_state)
6002725d31SMihail Atanassov {
6102725d31SMihail Atanassov 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
6202725d31SMihail Atanassov 	struct malidp_hw_device *hwdev = malidp->dev;
6302725d31SMihail Atanassov 
6402725d31SMihail Atanassov 	if (!crtc->state->color_mgmt_changed)
6502725d31SMihail Atanassov 		return;
6602725d31SMihail Atanassov 
6702725d31SMihail Atanassov 	if (!crtc->state->gamma_lut) {
6802725d31SMihail Atanassov 		malidp_hw_clearbits(hwdev,
6902725d31SMihail Atanassov 				    MALIDP_DISP_FUNC_GAMMA,
7002725d31SMihail Atanassov 				    MALIDP_DE_DISPLAY_FUNC);
7102725d31SMihail Atanassov 	} else {
7202725d31SMihail Atanassov 		struct malidp_crtc_state *mc =
7302725d31SMihail Atanassov 			to_malidp_crtc_state(crtc->state);
7402725d31SMihail Atanassov 
7502725d31SMihail Atanassov 		if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id !=
7602725d31SMihail Atanassov 					      old_state->gamma_lut->base.id))
7702725d31SMihail Atanassov 			malidp_write_gamma_table(hwdev, mc->gamma_coeffs);
7802725d31SMihail Atanassov 
7902725d31SMihail Atanassov 		malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA,
8002725d31SMihail Atanassov 				  MALIDP_DE_DISPLAY_FUNC);
8102725d31SMihail Atanassov 	}
8202725d31SMihail Atanassov }
8302725d31SMihail Atanassov 
846954f245SMihail Atanassov static
856954f245SMihail Atanassov void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc,
866954f245SMihail Atanassov 					  struct drm_crtc_state *old_state)
876954f245SMihail Atanassov {
886954f245SMihail Atanassov 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
896954f245SMihail Atanassov 	struct malidp_hw_device *hwdev = malidp->dev;
906954f245SMihail Atanassov 	int i;
916954f245SMihail Atanassov 
926954f245SMihail Atanassov 	if (!crtc->state->color_mgmt_changed)
936954f245SMihail Atanassov 		return;
946954f245SMihail Atanassov 
956954f245SMihail Atanassov 	if (!crtc->state->ctm) {
966954f245SMihail Atanassov 		malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ,
976954f245SMihail Atanassov 				    MALIDP_DE_DISPLAY_FUNC);
986954f245SMihail Atanassov 	} else {
996954f245SMihail Atanassov 		struct malidp_crtc_state *mc =
1006954f245SMihail Atanassov 			to_malidp_crtc_state(crtc->state);
1016954f245SMihail Atanassov 
1026954f245SMihail Atanassov 		if (!old_state->ctm || (crtc->state->ctm->base.id !=
1036954f245SMihail Atanassov 					old_state->ctm->base.id))
1046954f245SMihail Atanassov 			for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i)
1056954f245SMihail Atanassov 				malidp_hw_write(hwdev,
1066954f245SMihail Atanassov 						mc->coloradj_coeffs[i],
107a6993b21SLiviu Dudau 						hwdev->hw->map.coeffs_base +
1086954f245SMihail Atanassov 						MALIDP_COLOR_ADJ_COEF + 4 * i);
1096954f245SMihail Atanassov 
1106954f245SMihail Atanassov 		malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ,
1116954f245SMihail Atanassov 				  MALIDP_DE_DISPLAY_FUNC);
1126954f245SMihail Atanassov 	}
1136954f245SMihail Atanassov }
1146954f245SMihail Atanassov 
11528ce675bSMihail Atanassov static void malidp_atomic_commit_se_config(struct drm_crtc *crtc,
11628ce675bSMihail Atanassov 					   struct drm_crtc_state *old_state)
11728ce675bSMihail Atanassov {
11828ce675bSMihail Atanassov 	struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state);
11928ce675bSMihail Atanassov 	struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state);
12028ce675bSMihail Atanassov 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
12128ce675bSMihail Atanassov 	struct malidp_hw_device *hwdev = malidp->dev;
12228ce675bSMihail Atanassov 	struct malidp_se_config *s = &cs->scaler_config;
12328ce675bSMihail Atanassov 	struct malidp_se_config *old_s = &old_cs->scaler_config;
124a6993b21SLiviu Dudau 	u32 se_control = hwdev->hw->map.se_base +
125a6993b21SLiviu Dudau 			 ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ?
12628ce675bSMihail Atanassov 			 0x10 : 0xC);
12728ce675bSMihail Atanassov 	u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL;
12828ce675bSMihail Atanassov 	u32 scr = se_control + MALIDP_SE_SCALING_CONTROL;
12928ce675bSMihail Atanassov 	u32 val;
13028ce675bSMihail Atanassov 
13128ce675bSMihail Atanassov 	/* Set SE_CONTROL */
13228ce675bSMihail Atanassov 	if (!s->scale_enable) {
13328ce675bSMihail Atanassov 		val = malidp_hw_read(hwdev, se_control);
13428ce675bSMihail Atanassov 		val &= ~MALIDP_SE_SCALING_EN;
13528ce675bSMihail Atanassov 		malidp_hw_write(hwdev, val, se_control);
13628ce675bSMihail Atanassov 		return;
13728ce675bSMihail Atanassov 	}
13828ce675bSMihail Atanassov 
139a6993b21SLiviu Dudau 	hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s);
14028ce675bSMihail Atanassov 	val = malidp_hw_read(hwdev, se_control);
14128ce675bSMihail Atanassov 	val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN;
14228ce675bSMihail Atanassov 
1430274e6a0SMihail Atanassov 	val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK);
1440274e6a0SMihail Atanassov 	val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0;
1450274e6a0SMihail Atanassov 
14628ce675bSMihail Atanassov 	val |= MALIDP_SE_RGBO_IF_EN;
14728ce675bSMihail Atanassov 	malidp_hw_write(hwdev, val, se_control);
14828ce675bSMihail Atanassov 
14928ce675bSMihail Atanassov 	/* Set IN_SIZE & OUT_SIZE. */
15028ce675bSMihail Atanassov 	val = MALIDP_SE_SET_V_SIZE(s->input_h) |
15128ce675bSMihail Atanassov 	      MALIDP_SE_SET_H_SIZE(s->input_w);
15228ce675bSMihail Atanassov 	malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE);
15328ce675bSMihail Atanassov 	val = MALIDP_SE_SET_V_SIZE(s->output_h) |
15428ce675bSMihail Atanassov 	      MALIDP_SE_SET_H_SIZE(s->output_w);
15528ce675bSMihail Atanassov 	malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE);
15628ce675bSMihail Atanassov 
15728ce675bSMihail Atanassov 	/* Set phase regs. */
15828ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH);
15928ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH);
16028ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH);
16128ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH);
16228ce675bSMihail Atanassov }
16328ce675bSMihail Atanassov 
164ad49f860SLiviu Dudau /*
165ad49f860SLiviu Dudau  * set the "config valid" bit and wait until the hardware acts on it
166ad49f860SLiviu Dudau  */
167ad49f860SLiviu Dudau static int malidp_set_and_wait_config_valid(struct drm_device *drm)
168ad49f860SLiviu Dudau {
169ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
170ad49f860SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
171ad49f860SLiviu Dudau 	int ret;
172ad49f860SLiviu Dudau 
173aad38963SLiviu Dudau 	atomic_set(&malidp->config_valid, 0);
174a6993b21SLiviu Dudau 	hwdev->hw->set_config_valid(hwdev);
175ad49f860SLiviu Dudau 	/* don't wait for config_valid flag if we are in config mode */
176a6993b21SLiviu Dudau 	if (hwdev->hw->in_config_mode(hwdev))
177ad49f860SLiviu Dudau 		return 0;
178ad49f860SLiviu Dudau 
179ad49f860SLiviu Dudau 	ret = wait_event_interruptible_timeout(malidp->wq,
180ad49f860SLiviu Dudau 			atomic_read(&malidp->config_valid) == 1,
181ad49f860SLiviu Dudau 			msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT));
182ad49f860SLiviu Dudau 
183ad49f860SLiviu Dudau 	return (ret > 0) ? 0 : -ETIMEDOUT;
184ad49f860SLiviu Dudau }
185ad49f860SLiviu Dudau 
186ad49f860SLiviu Dudau static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
187ad49f860SLiviu Dudau {
188ad49f860SLiviu Dudau 	struct drm_pending_vblank_event *event;
189ad49f860SLiviu Dudau 	struct drm_device *drm = state->dev;
190ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
191ad49f860SLiviu Dudau 
19246f1d42fSLiviu Dudau 	if (malidp->crtc.enabled) {
19346f1d42fSLiviu Dudau 		/* only set config_valid if the CRTC is enabled */
19446f1d42fSLiviu Dudau 		if (malidp_set_and_wait_config_valid(drm))
195ad49f860SLiviu Dudau 			DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n");
19646f1d42fSLiviu Dudau 	}
197ad49f860SLiviu Dudau 
198ad49f860SLiviu Dudau 	event = malidp->crtc.state->event;
199ad49f860SLiviu Dudau 	if (event) {
200ad49f860SLiviu Dudau 		malidp->crtc.state->event = NULL;
201ad49f860SLiviu Dudau 
202ad49f860SLiviu Dudau 		spin_lock_irq(&drm->event_lock);
203ad49f860SLiviu Dudau 		if (drm_crtc_vblank_get(&malidp->crtc) == 0)
204ad49f860SLiviu Dudau 			drm_crtc_arm_vblank_event(&malidp->crtc, event);
205ad49f860SLiviu Dudau 		else
206ad49f860SLiviu Dudau 			drm_crtc_send_vblank_event(&malidp->crtc, event);
207ad49f860SLiviu Dudau 		spin_unlock_irq(&drm->event_lock);
208ad49f860SLiviu Dudau 	}
209ad49f860SLiviu Dudau 	drm_atomic_helper_commit_hw_done(state);
210ad49f860SLiviu Dudau }
211ad49f860SLiviu Dudau 
212ad49f860SLiviu Dudau static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
213ad49f860SLiviu Dudau {
214ad49f860SLiviu Dudau 	struct drm_device *drm = state->dev;
21502725d31SMihail Atanassov 	struct drm_crtc *crtc;
21602725d31SMihail Atanassov 	struct drm_crtc_state *old_crtc_state;
21702725d31SMihail Atanassov 	int i;
218ad49f860SLiviu Dudau 
21985f64218SLiviu Dudau 	pm_runtime_get_sync(drm->dev);
22085f64218SLiviu Dudau 
221ad49f860SLiviu Dudau 	drm_atomic_helper_commit_modeset_disables(drm, state);
22246f1d42fSLiviu Dudau 
223a8e3fb55SMaarten Lankhorst 	for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
22402725d31SMihail Atanassov 		malidp_atomic_commit_update_gamma(crtc, old_crtc_state);
2256954f245SMihail Atanassov 		malidp_atomic_commit_update_coloradj(crtc, old_crtc_state);
22628ce675bSMihail Atanassov 		malidp_atomic_commit_se_config(crtc, old_crtc_state);
2276954f245SMihail Atanassov 	}
22802725d31SMihail Atanassov 
229fe37ed6aSBrian Starkey 	drm_atomic_helper_commit_planes(drm, state, 0);
230ad49f860SLiviu Dudau 
23146f1d42fSLiviu Dudau 	drm_atomic_helper_commit_modeset_enables(drm, state);
23246f1d42fSLiviu Dudau 
233ad49f860SLiviu Dudau 	malidp_atomic_commit_hw_done(state);
234ad49f860SLiviu Dudau 
235ad49f860SLiviu Dudau 	drm_atomic_helper_wait_for_vblanks(drm, state);
236ad49f860SLiviu Dudau 
23785f64218SLiviu Dudau 	pm_runtime_put(drm->dev);
23885f64218SLiviu Dudau 
239ad49f860SLiviu Dudau 	drm_atomic_helper_cleanup_planes(drm, state);
240ad49f860SLiviu Dudau }
241ad49f860SLiviu Dudau 
242a4b10cceSLaurent Pinchart static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = {
243ad49f860SLiviu Dudau 	.atomic_commit_tail = malidp_atomic_commit_tail,
244ad49f860SLiviu Dudau };
245ad49f860SLiviu Dudau 
246ad49f860SLiviu Dudau static const struct drm_mode_config_funcs malidp_mode_config_funcs = {
247783f7d97SNoralf Trønnes 	.fb_create = drm_gem_fb_create,
248bdecd835SNoralf Trønnes 	.output_poll_changed = drm_fb_helper_output_poll_changed,
249ad49f860SLiviu Dudau 	.atomic_check = drm_atomic_helper_check,
250ad49f860SLiviu Dudau 	.atomic_commit = drm_atomic_helper_commit,
251ad49f860SLiviu Dudau };
252ad49f860SLiviu Dudau 
253ad49f860SLiviu Dudau static int malidp_init(struct drm_device *drm)
254ad49f860SLiviu Dudau {
255ad49f860SLiviu Dudau 	int ret;
256ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
257ad49f860SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
258ad49f860SLiviu Dudau 
259ad49f860SLiviu Dudau 	drm_mode_config_init(drm);
260ad49f860SLiviu Dudau 
261ad49f860SLiviu Dudau 	drm->mode_config.min_width = hwdev->min_line_size;
262ad49f860SLiviu Dudau 	drm->mode_config.min_height = hwdev->min_line_size;
263ad49f860SLiviu Dudau 	drm->mode_config.max_width = hwdev->max_line_size;
264ad49f860SLiviu Dudau 	drm->mode_config.max_height = hwdev->max_line_size;
265ad49f860SLiviu Dudau 	drm->mode_config.funcs = &malidp_mode_config_funcs;
266ad49f860SLiviu Dudau 	drm->mode_config.helper_private = &malidp_mode_config_helpers;
267ad49f860SLiviu Dudau 
268ad49f860SLiviu Dudau 	ret = malidp_crtc_init(drm);
269ad49f860SLiviu Dudau 	if (ret) {
270ad49f860SLiviu Dudau 		drm_mode_config_cleanup(drm);
271ad49f860SLiviu Dudau 		return ret;
272ad49f860SLiviu Dudau 	}
273ad49f860SLiviu Dudau 
274ad49f860SLiviu Dudau 	return 0;
275ad49f860SLiviu Dudau }
276ad49f860SLiviu Dudau 
277de9c4810SBrian Starkey static void malidp_fini(struct drm_device *drm)
278de9c4810SBrian Starkey {
279de9c4810SBrian Starkey 	malidp_de_planes_destroy(drm);
280de9c4810SBrian Starkey 	drm_mode_config_cleanup(drm);
281de9c4810SBrian Starkey }
282de9c4810SBrian Starkey 
283ad49f860SLiviu Dudau static int malidp_irq_init(struct platform_device *pdev)
284ad49f860SLiviu Dudau {
285ad49f860SLiviu Dudau 	int irq_de, irq_se, ret = 0;
286ad49f860SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(&pdev->dev);
287ad49f860SLiviu Dudau 
288ad49f860SLiviu Dudau 	/* fetch the interrupts from DT */
289ad49f860SLiviu Dudau 	irq_de = platform_get_irq_byname(pdev, "DE");
290ad49f860SLiviu Dudau 	if (irq_de < 0) {
291ad49f860SLiviu Dudau 		DRM_ERROR("no 'DE' IRQ specified!\n");
292ad49f860SLiviu Dudau 		return irq_de;
293ad49f860SLiviu Dudau 	}
294ad49f860SLiviu Dudau 	irq_se = platform_get_irq_byname(pdev, "SE");
295ad49f860SLiviu Dudau 	if (irq_se < 0) {
296ad49f860SLiviu Dudau 		DRM_ERROR("no 'SE' IRQ specified!\n");
297ad49f860SLiviu Dudau 		return irq_se;
298ad49f860SLiviu Dudau 	}
299ad49f860SLiviu Dudau 
300ad49f860SLiviu Dudau 	ret = malidp_de_irq_init(drm, irq_de);
301ad49f860SLiviu Dudau 	if (ret)
302ad49f860SLiviu Dudau 		return ret;
303ad49f860SLiviu Dudau 
304ad49f860SLiviu Dudau 	ret = malidp_se_irq_init(drm, irq_se);
305ad49f860SLiviu Dudau 	if (ret) {
306ad49f860SLiviu Dudau 		malidp_de_irq_fini(drm);
307ad49f860SLiviu Dudau 		return ret;
308ad49f860SLiviu Dudau 	}
309ad49f860SLiviu Dudau 
310ad49f860SLiviu Dudau 	return 0;
311ad49f860SLiviu Dudau }
312ad49f860SLiviu Dudau 
313d55f7e5dSDaniel Vetter DEFINE_DRM_GEM_CMA_FOPS(fops);
314ad49f860SLiviu Dudau 
315ad49f860SLiviu Dudau static struct drm_driver malidp_driver = {
316ad49f860SLiviu Dudau 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
317ad49f860SLiviu Dudau 			   DRIVER_PRIME,
318bdecd835SNoralf Trønnes 	.lastclose = drm_fb_helper_lastclose,
319ad49f860SLiviu Dudau 	.gem_free_object_unlocked = drm_gem_cma_free_object,
320ad49f860SLiviu Dudau 	.gem_vm_ops = &drm_gem_cma_vm_ops,
321ad49f860SLiviu Dudau 	.dumb_create = drm_gem_cma_dumb_create,
322ad49f860SLiviu Dudau 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
323ad49f860SLiviu Dudau 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
324ad49f860SLiviu Dudau 	.gem_prime_export = drm_gem_prime_export,
325ad49f860SLiviu Dudau 	.gem_prime_import = drm_gem_prime_import,
326ad49f860SLiviu Dudau 	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
327ad49f860SLiviu Dudau 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
328ad49f860SLiviu Dudau 	.gem_prime_vmap = drm_gem_cma_prime_vmap,
329ad49f860SLiviu Dudau 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
330ad49f860SLiviu Dudau 	.gem_prime_mmap = drm_gem_cma_prime_mmap,
331ad49f860SLiviu Dudau 	.fops = &fops,
332ad49f860SLiviu Dudau 	.name = "mali-dp",
333ad49f860SLiviu Dudau 	.desc = "ARM Mali Display Processor driver",
334ad49f860SLiviu Dudau 	.date = "20160106",
335ad49f860SLiviu Dudau 	.major = 1,
336ad49f860SLiviu Dudau 	.minor = 0,
337ad49f860SLiviu Dudau };
338ad49f860SLiviu Dudau 
339ad49f860SLiviu Dudau static const struct of_device_id  malidp_drm_of_match[] = {
340ad49f860SLiviu Dudau 	{
341ad49f860SLiviu Dudau 		.compatible = "arm,mali-dp500",
342ad49f860SLiviu Dudau 		.data = &malidp_device[MALIDP_500]
343ad49f860SLiviu Dudau 	},
344ad49f860SLiviu Dudau 	{
345ad49f860SLiviu Dudau 		.compatible = "arm,mali-dp550",
346ad49f860SLiviu Dudau 		.data = &malidp_device[MALIDP_550]
347ad49f860SLiviu Dudau 	},
348ad49f860SLiviu Dudau 	{
349ad49f860SLiviu Dudau 		.compatible = "arm,mali-dp650",
350ad49f860SLiviu Dudau 		.data = &malidp_device[MALIDP_650]
351ad49f860SLiviu Dudau 	},
352ad49f860SLiviu Dudau 	{},
353ad49f860SLiviu Dudau };
354ad49f860SLiviu Dudau MODULE_DEVICE_TABLE(of, malidp_drm_of_match);
355ad49f860SLiviu Dudau 
356592d8c8cSMihail Atanassov static bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev,
357592d8c8cSMihail Atanassov 				       const struct of_device_id *dev_id)
358592d8c8cSMihail Atanassov {
359592d8c8cSMihail Atanassov 	u32 core_id;
360592d8c8cSMihail Atanassov 	const char *compatstr_dp500 = "arm,mali-dp500";
361592d8c8cSMihail Atanassov 	bool is_dp500;
362592d8c8cSMihail Atanassov 	bool dt_is_dp500;
363592d8c8cSMihail Atanassov 
364592d8c8cSMihail Atanassov 	/*
365592d8c8cSMihail Atanassov 	 * The DP500 CORE_ID register is in a different location, so check it
366592d8c8cSMihail Atanassov 	 * first. If the product id field matches, then this is DP500, otherwise
367592d8c8cSMihail Atanassov 	 * check the DP550/650 CORE_ID register.
368592d8c8cSMihail Atanassov 	 */
369592d8c8cSMihail Atanassov 	core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID);
370592d8c8cSMihail Atanassov 	/* Offset 0x18 will never read 0x500 on products other than DP500. */
371592d8c8cSMihail Atanassov 	is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500);
372592d8c8cSMihail Atanassov 	dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500,
373592d8c8cSMihail Atanassov 			      sizeof(dev_id->compatible)) != NULL;
374592d8c8cSMihail Atanassov 	if (is_dp500 != dt_is_dp500) {
375592d8c8cSMihail Atanassov 		DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n",
376592d8c8cSMihail Atanassov 			  dev_id->compatible, is_dp500 ? "is" : "is not");
377592d8c8cSMihail Atanassov 		return false;
378592d8c8cSMihail Atanassov 	} else if (!dt_is_dp500) {
379592d8c8cSMihail Atanassov 		u16 product_id;
380592d8c8cSMihail Atanassov 		char buf[32];
381592d8c8cSMihail Atanassov 
382592d8c8cSMihail Atanassov 		core_id = malidp_hw_read(hwdev,
383592d8c8cSMihail Atanassov 					 MALIDP550_DC_BASE + MALIDP_DE_CORE_ID);
384592d8c8cSMihail Atanassov 		product_id = MALIDP_PRODUCT_ID(core_id);
385592d8c8cSMihail Atanassov 		snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id);
386592d8c8cSMihail Atanassov 		if (!strnstr(dev_id->compatible, buf,
387592d8c8cSMihail Atanassov 			     sizeof(dev_id->compatible))) {
388592d8c8cSMihail Atanassov 			DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n",
389592d8c8cSMihail Atanassov 				  dev_id->compatible, product_id);
390592d8c8cSMihail Atanassov 			return false;
391592d8c8cSMihail Atanassov 		}
392592d8c8cSMihail Atanassov 	}
393592d8c8cSMihail Atanassov 	return true;
394592d8c8cSMihail Atanassov }
395592d8c8cSMihail Atanassov 
3964d6000edSMihail Atanassov static bool malidp_has_sufficient_address_space(const struct resource *res,
3974d6000edSMihail Atanassov 						const struct of_device_id *dev_id)
3984d6000edSMihail Atanassov {
3994d6000edSMihail Atanassov 	resource_size_t res_size = resource_size(res);
4004d6000edSMihail Atanassov 	const char *compatstr_dp500 = "arm,mali-dp500";
4014d6000edSMihail Atanassov 
4024d6000edSMihail Atanassov 	if (!strnstr(dev_id->compatible, compatstr_dp500,
4034d6000edSMihail Atanassov 		     sizeof(dev_id->compatible)))
4044d6000edSMihail Atanassov 		return res_size >= MALIDP550_ADDR_SPACE_SIZE;
4054d6000edSMihail Atanassov 	else if (res_size < MALIDP500_ADDR_SPACE_SIZE)
4064d6000edSMihail Atanassov 		return false;
4074d6000edSMihail Atanassov 	return true;
4084d6000edSMihail Atanassov }
4094d6000edSMihail Atanassov 
41050c7512fSLiviu Dudau static ssize_t core_id_show(struct device *dev, struct device_attribute *attr,
41150c7512fSLiviu Dudau 			    char *buf)
41250c7512fSLiviu Dudau {
41350c7512fSLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
41450c7512fSLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
41550c7512fSLiviu Dudau 
41650c7512fSLiviu Dudau 	return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id);
41750c7512fSLiviu Dudau }
41850c7512fSLiviu Dudau 
41950c7512fSLiviu Dudau DEVICE_ATTR_RO(core_id);
42050c7512fSLiviu Dudau 
42150c7512fSLiviu Dudau static int malidp_init_sysfs(struct device *dev)
42250c7512fSLiviu Dudau {
42350c7512fSLiviu Dudau 	int ret = device_create_file(dev, &dev_attr_core_id);
42450c7512fSLiviu Dudau 
42550c7512fSLiviu Dudau 	if (ret)
42650c7512fSLiviu Dudau 		DRM_ERROR("failed to create device file for core_id\n");
42750c7512fSLiviu Dudau 
42850c7512fSLiviu Dudau 	return ret;
42950c7512fSLiviu Dudau }
43050c7512fSLiviu Dudau 
43150c7512fSLiviu Dudau static void malidp_fini_sysfs(struct device *dev)
43250c7512fSLiviu Dudau {
43350c7512fSLiviu Dudau 	device_remove_file(dev, &dev_attr_core_id);
43450c7512fSLiviu Dudau }
43550c7512fSLiviu Dudau 
436ad49f860SLiviu Dudau #define MAX_OUTPUT_CHANNELS	3
437ad49f860SLiviu Dudau 
43885f64218SLiviu Dudau static int malidp_runtime_pm_suspend(struct device *dev)
43985f64218SLiviu Dudau {
44085f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
44185f64218SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
44285f64218SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
44385f64218SLiviu Dudau 
44485f64218SLiviu Dudau 	/* we can only suspend if the hardware is in config mode */
445a6993b21SLiviu Dudau 	WARN_ON(!hwdev->hw->in_config_mode(hwdev));
44685f64218SLiviu Dudau 
44785f64218SLiviu Dudau 	hwdev->pm_suspended = true;
44885f64218SLiviu Dudau 	clk_disable_unprepare(hwdev->mclk);
44985f64218SLiviu Dudau 	clk_disable_unprepare(hwdev->aclk);
45085f64218SLiviu Dudau 	clk_disable_unprepare(hwdev->pclk);
45185f64218SLiviu Dudau 
45285f64218SLiviu Dudau 	return 0;
45385f64218SLiviu Dudau }
45485f64218SLiviu Dudau 
45585f64218SLiviu Dudau static int malidp_runtime_pm_resume(struct device *dev)
45685f64218SLiviu Dudau {
45785f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
45885f64218SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
45985f64218SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
46085f64218SLiviu Dudau 
46185f64218SLiviu Dudau 	clk_prepare_enable(hwdev->pclk);
46285f64218SLiviu Dudau 	clk_prepare_enable(hwdev->aclk);
46385f64218SLiviu Dudau 	clk_prepare_enable(hwdev->mclk);
46485f64218SLiviu Dudau 	hwdev->pm_suspended = false;
46585f64218SLiviu Dudau 
46685f64218SLiviu Dudau 	return 0;
46785f64218SLiviu Dudau }
46885f64218SLiviu Dudau 
469ad49f860SLiviu Dudau static int malidp_bind(struct device *dev)
470ad49f860SLiviu Dudau {
471ad49f860SLiviu Dudau 	struct resource *res;
472ad49f860SLiviu Dudau 	struct drm_device *drm;
473ad49f860SLiviu Dudau 	struct malidp_drm *malidp;
474ad49f860SLiviu Dudau 	struct malidp_hw_device *hwdev;
475ad49f860SLiviu Dudau 	struct platform_device *pdev = to_platform_device(dev);
476592d8c8cSMihail Atanassov 	struct of_device_id const *dev_id;
477ad49f860SLiviu Dudau 	/* number of lines for the R, G and B output */
478ad49f860SLiviu Dudau 	u8 output_width[MAX_OUTPUT_CHANNELS];
479ad49f860SLiviu Dudau 	int ret = 0, i;
480ad49f860SLiviu Dudau 	u32 version, out_depth = 0;
481ad49f860SLiviu Dudau 
482ad49f860SLiviu Dudau 	malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL);
483ad49f860SLiviu Dudau 	if (!malidp)
484ad49f860SLiviu Dudau 		return -ENOMEM;
485ad49f860SLiviu Dudau 
486ad49f860SLiviu Dudau 	hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL);
487ad49f860SLiviu Dudau 	if (!hwdev)
488ad49f860SLiviu Dudau 		return -ENOMEM;
489ad49f860SLiviu Dudau 
490a6993b21SLiviu Dudau 	hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev);
491ad49f860SLiviu Dudau 	malidp->dev = hwdev;
492ad49f860SLiviu Dudau 
493ad49f860SLiviu Dudau 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
494ad49f860SLiviu Dudau 	hwdev->regs = devm_ioremap_resource(dev, res);
4951a9d71f8SWei Yongjun 	if (IS_ERR(hwdev->regs))
496ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->regs);
497ad49f860SLiviu Dudau 
498ad49f860SLiviu Dudau 	hwdev->pclk = devm_clk_get(dev, "pclk");
499ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->pclk))
500ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->pclk);
501ad49f860SLiviu Dudau 
502ad49f860SLiviu Dudau 	hwdev->aclk = devm_clk_get(dev, "aclk");
503ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->aclk))
504ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->aclk);
505ad49f860SLiviu Dudau 
506ad49f860SLiviu Dudau 	hwdev->mclk = devm_clk_get(dev, "mclk");
507ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->mclk))
508ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->mclk);
509ad49f860SLiviu Dudau 
510ad49f860SLiviu Dudau 	hwdev->pxlclk = devm_clk_get(dev, "pxlclk");
511ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->pxlclk))
512ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->pxlclk);
513ad49f860SLiviu Dudau 
514ad49f860SLiviu Dudau 	/* Get the optional framebuffer memory resource */
515ad49f860SLiviu Dudau 	ret = of_reserved_mem_device_init(dev);
516ad49f860SLiviu Dudau 	if (ret && ret != -ENODEV)
517ad49f860SLiviu Dudau 		return ret;
518ad49f860SLiviu Dudau 
519ad49f860SLiviu Dudau 	drm = drm_dev_alloc(&malidp_driver, dev);
5200f288605STom Gundersen 	if (IS_ERR(drm)) {
5210f288605STom Gundersen 		ret = PTR_ERR(drm);
522ad49f860SLiviu Dudau 		goto alloc_fail;
523ad49f860SLiviu Dudau 	}
524ad49f860SLiviu Dudau 
52585f64218SLiviu Dudau 	drm->dev_private = malidp;
52685f64218SLiviu Dudau 	dev_set_drvdata(dev, drm);
52785f64218SLiviu Dudau 
52885f64218SLiviu Dudau 	/* Enable power management */
52985f64218SLiviu Dudau 	pm_runtime_enable(dev);
53085f64218SLiviu Dudau 
53185f64218SLiviu Dudau 	/* Resume device to enable the clocks */
53285f64218SLiviu Dudau 	if (pm_runtime_enabled(dev))
53385f64218SLiviu Dudau 		pm_runtime_get_sync(dev);
53485f64218SLiviu Dudau 	else
53585f64218SLiviu Dudau 		malidp_runtime_pm_resume(dev);
536ad49f860SLiviu Dudau 
537592d8c8cSMihail Atanassov 	dev_id = of_match_device(malidp_drm_of_match, dev);
538592d8c8cSMihail Atanassov 	if (!dev_id) {
539592d8c8cSMihail Atanassov 		ret = -EINVAL;
540592d8c8cSMihail Atanassov 		goto query_hw_fail;
541592d8c8cSMihail Atanassov 	}
542592d8c8cSMihail Atanassov 
5434d6000edSMihail Atanassov 	if (!malidp_has_sufficient_address_space(res, dev_id)) {
5444d6000edSMihail Atanassov 		DRM_ERROR("Insufficient address space in device-tree.\n");
5454d6000edSMihail Atanassov 		ret = -EINVAL;
5464d6000edSMihail Atanassov 		goto query_hw_fail;
5474d6000edSMihail Atanassov 	}
5484d6000edSMihail Atanassov 
549592d8c8cSMihail Atanassov 	if (!malidp_is_compatible_hw_id(hwdev, dev_id)) {
550592d8c8cSMihail Atanassov 		ret = -EINVAL;
551592d8c8cSMihail Atanassov 		goto query_hw_fail;
552592d8c8cSMihail Atanassov 	}
553592d8c8cSMihail Atanassov 
554a6993b21SLiviu Dudau 	ret = hwdev->hw->query_hw(hwdev);
555ad49f860SLiviu Dudau 	if (ret) {
556ad49f860SLiviu Dudau 		DRM_ERROR("Invalid HW configuration\n");
557ad49f860SLiviu Dudau 		goto query_hw_fail;
558ad49f860SLiviu Dudau 	}
559ad49f860SLiviu Dudau 
560a6993b21SLiviu Dudau 	version = malidp_hw_read(hwdev, hwdev->hw->map.dc_base + MALIDP_DE_CORE_ID);
561ad49f860SLiviu Dudau 	DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16,
562ad49f860SLiviu Dudau 		 (version >> 12) & 0xf, (version >> 8) & 0xf);
563ad49f860SLiviu Dudau 
56450c7512fSLiviu Dudau 	malidp->core_id = version;
56550c7512fSLiviu Dudau 
566ad49f860SLiviu Dudau 	/* set the number of lines used for output of RGB data */
567ad49f860SLiviu Dudau 	ret = of_property_read_u8_array(dev->of_node,
568ad49f860SLiviu Dudau 					"arm,malidp-output-port-lines",
569ad49f860SLiviu Dudau 					output_width, MAX_OUTPUT_CHANNELS);
570ad49f860SLiviu Dudau 	if (ret)
571ad49f860SLiviu Dudau 		goto query_hw_fail;
572ad49f860SLiviu Dudau 
573ad49f860SLiviu Dudau 	for (i = 0; i < MAX_OUTPUT_CHANNELS; i++)
574ad49f860SLiviu Dudau 		out_depth = (out_depth << 8) | (output_width[i] & 0xf);
575a6993b21SLiviu Dudau 	malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base);
576ad49f860SLiviu Dudau 
577ad49f860SLiviu Dudau 	atomic_set(&malidp->config_valid, 0);
578ad49f860SLiviu Dudau 	init_waitqueue_head(&malidp->wq);
579ad49f860SLiviu Dudau 
580ad49f860SLiviu Dudau 	ret = malidp_init(drm);
581ad49f860SLiviu Dudau 	if (ret < 0)
58285f64218SLiviu Dudau 		goto query_hw_fail;
583ad49f860SLiviu Dudau 
58450c7512fSLiviu Dudau 	ret = malidp_init_sysfs(dev);
58550c7512fSLiviu Dudau 	if (ret)
58650c7512fSLiviu Dudau 		goto init_fail;
58750c7512fSLiviu Dudau 
588ad49f860SLiviu Dudau 	/* Set the CRTC's port so that the encoder component can find it */
58986418f90SRob Herring 	malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0);
590ad49f860SLiviu Dudau 
591ad49f860SLiviu Dudau 	ret = component_bind_all(dev, drm);
592ad49f860SLiviu Dudau 	if (ret) {
593ad49f860SLiviu Dudau 		DRM_ERROR("Failed to bind all components\n");
594ad49f860SLiviu Dudau 		goto bind_fail;
595ad49f860SLiviu Dudau 	}
596ad49f860SLiviu Dudau 
597ad49f860SLiviu Dudau 	ret = malidp_irq_init(pdev);
598ad49f860SLiviu Dudau 	if (ret < 0)
599ad49f860SLiviu Dudau 		goto irq_init_fail;
600ad49f860SLiviu Dudau 
601a6a7b9a2SLiviu Dudau 	drm->irq_enabled = true;
602a6a7b9a2SLiviu Dudau 
603ad49f860SLiviu Dudau 	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
604ad49f860SLiviu Dudau 	if (ret < 0) {
605ad49f860SLiviu Dudau 		DRM_ERROR("failed to initialise vblank\n");
606ad49f860SLiviu Dudau 		goto vblank_fail;
607ad49f860SLiviu Dudau 	}
60885f64218SLiviu Dudau 	pm_runtime_put(dev);
609ad49f860SLiviu Dudau 
610ad49f860SLiviu Dudau 	drm_mode_config_reset(drm);
611ad49f860SLiviu Dudau 
612bdecd835SNoralf Trønnes 	ret = drm_fb_cma_fbdev_init(drm, 32, 0);
613bdecd835SNoralf Trønnes 	if (ret)
614ad49f860SLiviu Dudau 		goto fbdev_fail;
615ad49f860SLiviu Dudau 
616ad49f860SLiviu Dudau 	drm_kms_helper_poll_init(drm);
61790731c24SBrian Starkey 
61890731c24SBrian Starkey 	ret = drm_dev_register(drm, 0);
61990731c24SBrian Starkey 	if (ret)
62090731c24SBrian Starkey 		goto register_fail;
62190731c24SBrian Starkey 
622ad49f860SLiviu Dudau 	return 0;
623ad49f860SLiviu Dudau 
62490731c24SBrian Starkey register_fail:
625bdecd835SNoralf Trønnes 	drm_fb_cma_fbdev_fini(drm);
62685f64218SLiviu Dudau 	drm_kms_helper_poll_fini(drm);
627ad49f860SLiviu Dudau fbdev_fail:
62885f64218SLiviu Dudau 	pm_runtime_get_sync(dev);
629ad49f860SLiviu Dudau vblank_fail:
630ad49f860SLiviu Dudau 	malidp_se_irq_fini(drm);
631ad49f860SLiviu Dudau 	malidp_de_irq_fini(drm);
632a6a7b9a2SLiviu Dudau 	drm->irq_enabled = false;
633ad49f860SLiviu Dudau irq_init_fail:
634ad49f860SLiviu Dudau 	component_unbind_all(dev, drm);
635ad49f860SLiviu Dudau bind_fail:
6363c31760eSBrian Starkey 	of_node_put(malidp->crtc.port);
6373c31760eSBrian Starkey 	malidp->crtc.port = NULL;
63850c7512fSLiviu Dudau init_fail:
63950c7512fSLiviu Dudau 	malidp_fini_sysfs(dev);
640de9c4810SBrian Starkey 	malidp_fini(drm);
64185f64218SLiviu Dudau query_hw_fail:
64285f64218SLiviu Dudau 	pm_runtime_put(dev);
64385f64218SLiviu Dudau 	if (pm_runtime_enabled(dev))
64485f64218SLiviu Dudau 		pm_runtime_disable(dev);
64585f64218SLiviu Dudau 	else
64685f64218SLiviu Dudau 		malidp_runtime_pm_suspend(dev);
647ad49f860SLiviu Dudau 	drm->dev_private = NULL;
648ad49f860SLiviu Dudau 	dev_set_drvdata(dev, NULL);
6490970d7a2SSrishti Sharma 	drm_dev_put(drm);
650ad49f860SLiviu Dudau alloc_fail:
651ad49f860SLiviu Dudau 	of_reserved_mem_device_release(dev);
652ad49f860SLiviu Dudau 
653ad49f860SLiviu Dudau 	return ret;
654ad49f860SLiviu Dudau }
655ad49f860SLiviu Dudau 
656ad49f860SLiviu Dudau static void malidp_unbind(struct device *dev)
657ad49f860SLiviu Dudau {
658ad49f860SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
659ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
660ad49f860SLiviu Dudau 
66190731c24SBrian Starkey 	drm_dev_unregister(drm);
662bdecd835SNoralf Trønnes 	drm_fb_cma_fbdev_fini(drm);
663ad49f860SLiviu Dudau 	drm_kms_helper_poll_fini(drm);
66485f64218SLiviu Dudau 	pm_runtime_get_sync(dev);
665ad49f860SLiviu Dudau 	malidp_se_irq_fini(drm);
666ad49f860SLiviu Dudau 	malidp_de_irq_fini(drm);
667ad49f860SLiviu Dudau 	component_unbind_all(dev, drm);
6683c31760eSBrian Starkey 	of_node_put(malidp->crtc.port);
6693c31760eSBrian Starkey 	malidp->crtc.port = NULL;
67050c7512fSLiviu Dudau 	malidp_fini_sysfs(dev);
671de9c4810SBrian Starkey 	malidp_fini(drm);
67285f64218SLiviu Dudau 	pm_runtime_put(dev);
67385f64218SLiviu Dudau 	if (pm_runtime_enabled(dev))
67485f64218SLiviu Dudau 		pm_runtime_disable(dev);
67585f64218SLiviu Dudau 	else
67685f64218SLiviu Dudau 		malidp_runtime_pm_suspend(dev);
677ad49f860SLiviu Dudau 	drm->dev_private = NULL;
678ad49f860SLiviu Dudau 	dev_set_drvdata(dev, NULL);
6790970d7a2SSrishti Sharma 	drm_dev_put(drm);
680ad49f860SLiviu Dudau 	of_reserved_mem_device_release(dev);
681ad49f860SLiviu Dudau }
682ad49f860SLiviu Dudau 
683ad49f860SLiviu Dudau static const struct component_master_ops malidp_master_ops = {
684ad49f860SLiviu Dudau 	.bind = malidp_bind,
685ad49f860SLiviu Dudau 	.unbind = malidp_unbind,
686ad49f860SLiviu Dudau };
687ad49f860SLiviu Dudau 
688ad49f860SLiviu Dudau static int malidp_compare_dev(struct device *dev, void *data)
689ad49f860SLiviu Dudau {
690ad49f860SLiviu Dudau 	struct device_node *np = data;
691ad49f860SLiviu Dudau 
692ad49f860SLiviu Dudau 	return dev->of_node == np;
693ad49f860SLiviu Dudau }
694ad49f860SLiviu Dudau 
695ad49f860SLiviu Dudau static int malidp_platform_probe(struct platform_device *pdev)
696ad49f860SLiviu Dudau {
69786418f90SRob Herring 	struct device_node *port;
698ad49f860SLiviu Dudau 	struct component_match *match = NULL;
699ad49f860SLiviu Dudau 
700ad49f860SLiviu Dudau 	if (!pdev->dev.of_node)
701ad49f860SLiviu Dudau 		return -ENODEV;
702ad49f860SLiviu Dudau 
703ad49f860SLiviu Dudau 	/* there is only one output port inside each device, find it */
70486418f90SRob Herring 	port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
70586418f90SRob Herring 	if (!port)
706ad49f860SLiviu Dudau 		return -ENODEV;
707ad49f860SLiviu Dudau 
70897ac0e47SRussell King 	drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev,
70997ac0e47SRussell King 				   port);
71097ac0e47SRussell King 	of_node_put(port);
711ad49f860SLiviu Dudau 	return component_master_add_with_match(&pdev->dev, &malidp_master_ops,
712ad49f860SLiviu Dudau 					       match);
713ad49f860SLiviu Dudau }
714ad49f860SLiviu Dudau 
715ad49f860SLiviu Dudau static int malidp_platform_remove(struct platform_device *pdev)
716ad49f860SLiviu Dudau {
717ad49f860SLiviu Dudau 	component_master_del(&pdev->dev, &malidp_master_ops);
718ad49f860SLiviu Dudau 	return 0;
719ad49f860SLiviu Dudau }
720ad49f860SLiviu Dudau 
72185f64218SLiviu Dudau static int __maybe_unused malidp_pm_suspend(struct device *dev)
72285f64218SLiviu Dudau {
72385f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
72485f64218SLiviu Dudau 
725194b8799SNoralf Trønnes 	return drm_mode_config_helper_suspend(drm);
72685f64218SLiviu Dudau }
72785f64218SLiviu Dudau 
72885f64218SLiviu Dudau static int __maybe_unused malidp_pm_resume(struct device *dev)
72985f64218SLiviu Dudau {
73085f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
73185f64218SLiviu Dudau 
732194b8799SNoralf Trønnes 	drm_mode_config_helper_resume(drm);
73385f64218SLiviu Dudau 
73485f64218SLiviu Dudau 	return 0;
73585f64218SLiviu Dudau }
73685f64218SLiviu Dudau 
73785f64218SLiviu Dudau static const struct dev_pm_ops malidp_pm_ops = {
73885f64218SLiviu Dudau 	SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \
73985f64218SLiviu Dudau 	SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL)
74085f64218SLiviu Dudau };
74185f64218SLiviu Dudau 
742ad49f860SLiviu Dudau static struct platform_driver malidp_platform_driver = {
743ad49f860SLiviu Dudau 	.probe		= malidp_platform_probe,
744ad49f860SLiviu Dudau 	.remove		= malidp_platform_remove,
745ad49f860SLiviu Dudau 	.driver	= {
746ad49f860SLiviu Dudau 		.name = "mali-dp",
74785f64218SLiviu Dudau 		.pm = &malidp_pm_ops,
748ad49f860SLiviu Dudau 		.of_match_table	= malidp_drm_of_match,
749ad49f860SLiviu Dudau 	},
750ad49f860SLiviu Dudau };
751ad49f860SLiviu Dudau 
752ad49f860SLiviu Dudau module_platform_driver(malidp_platform_driver);
753ad49f860SLiviu Dudau 
754ad49f860SLiviu Dudau MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>");
755ad49f860SLiviu Dudau MODULE_DESCRIPTION("ARM Mali DP DRM driver");
756ad49f860SLiviu Dudau MODULE_LICENSE("GPL v2");
757