xref: /openbmc/linux/drivers/gpu/drm/arm/malidp_drv.c (revision 8cbc5caf)
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"
348cbc5cafSBrian Starkey #include "malidp_mw.h"
35ad49f860SLiviu Dudau #include "malidp_regs.h"
36ad49f860SLiviu Dudau #include "malidp_hw.h"
37ad49f860SLiviu Dudau 
38ad49f860SLiviu Dudau #define MALIDP_CONF_VALID_TIMEOUT	250
39ad49f860SLiviu Dudau 
4002725d31SMihail Atanassov static void malidp_write_gamma_table(struct malidp_hw_device *hwdev,
4102725d31SMihail Atanassov 				     u32 data[MALIDP_COEFFTAB_NUM_COEFFS])
4202725d31SMihail Atanassov {
4302725d31SMihail Atanassov 	int i;
4402725d31SMihail Atanassov 	/* Update all channels with a single gamma curve. */
4502725d31SMihail Atanassov 	const u32 gamma_write_mask = GENMASK(18, 16);
4602725d31SMihail Atanassov 	/*
4702725d31SMihail Atanassov 	 * Always write an entire table, so the address field in
4802725d31SMihail Atanassov 	 * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask
4902725d31SMihail Atanassov 	 * directly.
5002725d31SMihail Atanassov 	 */
5102725d31SMihail Atanassov 	malidp_hw_write(hwdev, gamma_write_mask,
52a6993b21SLiviu Dudau 			hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR);
5302725d31SMihail Atanassov 	for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i)
5402725d31SMihail Atanassov 		malidp_hw_write(hwdev, data[i],
55a6993b21SLiviu Dudau 				hwdev->hw->map.coeffs_base +
5602725d31SMihail Atanassov 				MALIDP_COEF_TABLE_DATA);
5702725d31SMihail Atanassov }
5802725d31SMihail Atanassov 
5902725d31SMihail Atanassov static void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc,
6002725d31SMihail Atanassov 					      struct drm_crtc_state *old_state)
6102725d31SMihail Atanassov {
6202725d31SMihail Atanassov 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
6302725d31SMihail Atanassov 	struct malidp_hw_device *hwdev = malidp->dev;
6402725d31SMihail Atanassov 
6502725d31SMihail Atanassov 	if (!crtc->state->color_mgmt_changed)
6602725d31SMihail Atanassov 		return;
6702725d31SMihail Atanassov 
6802725d31SMihail Atanassov 	if (!crtc->state->gamma_lut) {
6902725d31SMihail Atanassov 		malidp_hw_clearbits(hwdev,
7002725d31SMihail Atanassov 				    MALIDP_DISP_FUNC_GAMMA,
7102725d31SMihail Atanassov 				    MALIDP_DE_DISPLAY_FUNC);
7202725d31SMihail Atanassov 	} else {
7302725d31SMihail Atanassov 		struct malidp_crtc_state *mc =
7402725d31SMihail Atanassov 			to_malidp_crtc_state(crtc->state);
7502725d31SMihail Atanassov 
7602725d31SMihail Atanassov 		if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id !=
7702725d31SMihail Atanassov 					      old_state->gamma_lut->base.id))
7802725d31SMihail Atanassov 			malidp_write_gamma_table(hwdev, mc->gamma_coeffs);
7902725d31SMihail Atanassov 
8002725d31SMihail Atanassov 		malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA,
8102725d31SMihail Atanassov 				  MALIDP_DE_DISPLAY_FUNC);
8202725d31SMihail Atanassov 	}
8302725d31SMihail Atanassov }
8402725d31SMihail Atanassov 
856954f245SMihail Atanassov static
866954f245SMihail Atanassov void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc,
876954f245SMihail Atanassov 					  struct drm_crtc_state *old_state)
886954f245SMihail Atanassov {
896954f245SMihail Atanassov 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
906954f245SMihail Atanassov 	struct malidp_hw_device *hwdev = malidp->dev;
916954f245SMihail Atanassov 	int i;
926954f245SMihail Atanassov 
936954f245SMihail Atanassov 	if (!crtc->state->color_mgmt_changed)
946954f245SMihail Atanassov 		return;
956954f245SMihail Atanassov 
966954f245SMihail Atanassov 	if (!crtc->state->ctm) {
976954f245SMihail Atanassov 		malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ,
986954f245SMihail Atanassov 				    MALIDP_DE_DISPLAY_FUNC);
996954f245SMihail Atanassov 	} else {
1006954f245SMihail Atanassov 		struct malidp_crtc_state *mc =
1016954f245SMihail Atanassov 			to_malidp_crtc_state(crtc->state);
1026954f245SMihail Atanassov 
1036954f245SMihail Atanassov 		if (!old_state->ctm || (crtc->state->ctm->base.id !=
1046954f245SMihail Atanassov 					old_state->ctm->base.id))
1056954f245SMihail Atanassov 			for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i)
1066954f245SMihail Atanassov 				malidp_hw_write(hwdev,
1076954f245SMihail Atanassov 						mc->coloradj_coeffs[i],
108a6993b21SLiviu Dudau 						hwdev->hw->map.coeffs_base +
1096954f245SMihail Atanassov 						MALIDP_COLOR_ADJ_COEF + 4 * i);
1106954f245SMihail Atanassov 
1116954f245SMihail Atanassov 		malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ,
1126954f245SMihail Atanassov 				  MALIDP_DE_DISPLAY_FUNC);
1136954f245SMihail Atanassov 	}
1146954f245SMihail Atanassov }
1156954f245SMihail Atanassov 
11628ce675bSMihail Atanassov static void malidp_atomic_commit_se_config(struct drm_crtc *crtc,
11728ce675bSMihail Atanassov 					   struct drm_crtc_state *old_state)
11828ce675bSMihail Atanassov {
11928ce675bSMihail Atanassov 	struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state);
12028ce675bSMihail Atanassov 	struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state);
12128ce675bSMihail Atanassov 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
12228ce675bSMihail Atanassov 	struct malidp_hw_device *hwdev = malidp->dev;
12328ce675bSMihail Atanassov 	struct malidp_se_config *s = &cs->scaler_config;
12428ce675bSMihail Atanassov 	struct malidp_se_config *old_s = &old_cs->scaler_config;
125a6993b21SLiviu Dudau 	u32 se_control = hwdev->hw->map.se_base +
126a6993b21SLiviu Dudau 			 ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ?
12728ce675bSMihail Atanassov 			 0x10 : 0xC);
12828ce675bSMihail Atanassov 	u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL;
12928ce675bSMihail Atanassov 	u32 scr = se_control + MALIDP_SE_SCALING_CONTROL;
13028ce675bSMihail Atanassov 	u32 val;
13128ce675bSMihail Atanassov 
13228ce675bSMihail Atanassov 	/* Set SE_CONTROL */
13328ce675bSMihail Atanassov 	if (!s->scale_enable) {
13428ce675bSMihail Atanassov 		val = malidp_hw_read(hwdev, se_control);
13528ce675bSMihail Atanassov 		val &= ~MALIDP_SE_SCALING_EN;
13628ce675bSMihail Atanassov 		malidp_hw_write(hwdev, val, se_control);
13728ce675bSMihail Atanassov 		return;
13828ce675bSMihail Atanassov 	}
13928ce675bSMihail Atanassov 
140a6993b21SLiviu Dudau 	hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s);
14128ce675bSMihail Atanassov 	val = malidp_hw_read(hwdev, se_control);
14228ce675bSMihail Atanassov 	val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN;
14328ce675bSMihail Atanassov 
1440274e6a0SMihail Atanassov 	val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK);
1450274e6a0SMihail Atanassov 	val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0;
1460274e6a0SMihail Atanassov 
14728ce675bSMihail Atanassov 	val |= MALIDP_SE_RGBO_IF_EN;
14828ce675bSMihail Atanassov 	malidp_hw_write(hwdev, val, se_control);
14928ce675bSMihail Atanassov 
15028ce675bSMihail Atanassov 	/* Set IN_SIZE & OUT_SIZE. */
15128ce675bSMihail Atanassov 	val = MALIDP_SE_SET_V_SIZE(s->input_h) |
15228ce675bSMihail Atanassov 	      MALIDP_SE_SET_H_SIZE(s->input_w);
15328ce675bSMihail Atanassov 	malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE);
15428ce675bSMihail Atanassov 	val = MALIDP_SE_SET_V_SIZE(s->output_h) |
15528ce675bSMihail Atanassov 	      MALIDP_SE_SET_H_SIZE(s->output_w);
15628ce675bSMihail Atanassov 	malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE);
15728ce675bSMihail Atanassov 
15828ce675bSMihail Atanassov 	/* Set phase regs. */
15928ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH);
16028ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH);
16128ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH);
16228ce675bSMihail Atanassov 	malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH);
16328ce675bSMihail Atanassov }
16428ce675bSMihail Atanassov 
165ad49f860SLiviu Dudau /*
166ad49f860SLiviu Dudau  * set the "config valid" bit and wait until the hardware acts on it
167ad49f860SLiviu Dudau  */
168ad49f860SLiviu Dudau static int malidp_set_and_wait_config_valid(struct drm_device *drm)
169ad49f860SLiviu Dudau {
170ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
171ad49f860SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
172ad49f860SLiviu Dudau 	int ret;
173ad49f860SLiviu Dudau 
174a6993b21SLiviu Dudau 	hwdev->hw->set_config_valid(hwdev);
175ad49f860SLiviu Dudau 	/* don't wait for config_valid flag if we are in config mode */
1761cb3cbe7SLiviu Dudau 	if (hwdev->hw->in_config_mode(hwdev)) {
1771cb3cbe7SLiviu Dudau 		atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE);
178ad49f860SLiviu Dudau 		return 0;
1791cb3cbe7SLiviu Dudau 	}
180ad49f860SLiviu Dudau 
181ad49f860SLiviu Dudau 	ret = wait_event_interruptible_timeout(malidp->wq,
1821cb3cbe7SLiviu Dudau 			atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE,
183ad49f860SLiviu Dudau 			msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT));
184ad49f860SLiviu Dudau 
185ad49f860SLiviu Dudau 	return (ret > 0) ? 0 : -ETIMEDOUT;
186ad49f860SLiviu Dudau }
187ad49f860SLiviu Dudau 
188ad49f860SLiviu Dudau static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
189ad49f860SLiviu Dudau {
190ad49f860SLiviu Dudau 	struct drm_device *drm = state->dev;
191ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
192ad49f860SLiviu Dudau 
193d862b2d6SLiviu Dudau 	malidp->event = malidp->crtc.state->event;
194ad49f860SLiviu Dudau 	malidp->crtc.state->event = NULL;
195ad49f860SLiviu Dudau 
196d862b2d6SLiviu Dudau 	if (malidp->crtc.state->active) {
197d862b2d6SLiviu Dudau 		/*
198d862b2d6SLiviu Dudau 		 * if we have an event to deliver to userspace, make sure
199d862b2d6SLiviu Dudau 		 * the vblank is enabled as we are sending it from the IRQ
200d862b2d6SLiviu Dudau 		 * handler.
201d862b2d6SLiviu Dudau 		 */
202d862b2d6SLiviu Dudau 		if (malidp->event)
203d862b2d6SLiviu Dudau 			drm_crtc_vblank_get(&malidp->crtc);
204d862b2d6SLiviu Dudau 
205d862b2d6SLiviu Dudau 		/* only set config_valid if the CRTC is enabled */
206d862b2d6SLiviu Dudau 		if (malidp_set_and_wait_config_valid(drm) < 0)
207d862b2d6SLiviu Dudau 			DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n");
208d862b2d6SLiviu Dudau 	} else if (malidp->event) {
209d862b2d6SLiviu Dudau 		/* CRTC inactive means vblank IRQ is disabled, send event directly */
210ad49f860SLiviu Dudau 		spin_lock_irq(&drm->event_lock);
211d862b2d6SLiviu Dudau 		drm_crtc_send_vblank_event(&malidp->crtc, malidp->event);
212d862b2d6SLiviu Dudau 		malidp->event = NULL;
213ad49f860SLiviu Dudau 		spin_unlock_irq(&drm->event_lock);
214ad49f860SLiviu Dudau 	}
215ad49f860SLiviu Dudau 	drm_atomic_helper_commit_hw_done(state);
216ad49f860SLiviu Dudau }
217ad49f860SLiviu Dudau 
218ad49f860SLiviu Dudau static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
219ad49f860SLiviu Dudau {
220ad49f860SLiviu Dudau 	struct drm_device *drm = state->dev;
2211cb3cbe7SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
22202725d31SMihail Atanassov 	struct drm_crtc *crtc;
22302725d31SMihail Atanassov 	struct drm_crtc_state *old_crtc_state;
22402725d31SMihail Atanassov 	int i;
225ad49f860SLiviu Dudau 
22685f64218SLiviu Dudau 	pm_runtime_get_sync(drm->dev);
22785f64218SLiviu Dudau 
2281cb3cbe7SLiviu Dudau 	/*
2291cb3cbe7SLiviu Dudau 	 * set config_valid to a special value to let IRQ handlers
2301cb3cbe7SLiviu Dudau 	 * know that we are updating registers
2311cb3cbe7SLiviu Dudau 	 */
2321cb3cbe7SLiviu Dudau 	atomic_set(&malidp->config_valid, MALIDP_CONFIG_START);
2331cb3cbe7SLiviu Dudau 
234ad49f860SLiviu Dudau 	drm_atomic_helper_commit_modeset_disables(drm, state);
23546f1d42fSLiviu Dudau 
236a8e3fb55SMaarten Lankhorst 	for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
23702725d31SMihail Atanassov 		malidp_atomic_commit_update_gamma(crtc, old_crtc_state);
2386954f245SMihail Atanassov 		malidp_atomic_commit_update_coloradj(crtc, old_crtc_state);
23928ce675bSMihail Atanassov 		malidp_atomic_commit_se_config(crtc, old_crtc_state);
2406954f245SMihail Atanassov 	}
24102725d31SMihail Atanassov 
2428cbc5cafSBrian Starkey 	drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY);
2438cbc5cafSBrian Starkey 
2448cbc5cafSBrian Starkey 	malidp_mw_atomic_commit(drm, state);
245ad49f860SLiviu Dudau 
24646f1d42fSLiviu Dudau 	drm_atomic_helper_commit_modeset_enables(drm, state);
24746f1d42fSLiviu Dudau 
248ad49f860SLiviu Dudau 	malidp_atomic_commit_hw_done(state);
249ad49f860SLiviu Dudau 
25085f64218SLiviu Dudau 	pm_runtime_put(drm->dev);
25185f64218SLiviu Dudau 
252ad49f860SLiviu Dudau 	drm_atomic_helper_cleanup_planes(drm, state);
253ad49f860SLiviu Dudau }
254ad49f860SLiviu Dudau 
255a4b10cceSLaurent Pinchart static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = {
256ad49f860SLiviu Dudau 	.atomic_commit_tail = malidp_atomic_commit_tail,
257ad49f860SLiviu Dudau };
258ad49f860SLiviu Dudau 
259ad49f860SLiviu Dudau static const struct drm_mode_config_funcs malidp_mode_config_funcs = {
260783f7d97SNoralf Trønnes 	.fb_create = drm_gem_fb_create,
261bdecd835SNoralf Trønnes 	.output_poll_changed = drm_fb_helper_output_poll_changed,
262ad49f860SLiviu Dudau 	.atomic_check = drm_atomic_helper_check,
263ad49f860SLiviu Dudau 	.atomic_commit = drm_atomic_helper_commit,
264ad49f860SLiviu Dudau };
265ad49f860SLiviu Dudau 
266ad49f860SLiviu Dudau static int malidp_init(struct drm_device *drm)
267ad49f860SLiviu Dudau {
268ad49f860SLiviu Dudau 	int ret;
269ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
270ad49f860SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
271ad49f860SLiviu Dudau 
272ad49f860SLiviu Dudau 	drm_mode_config_init(drm);
273ad49f860SLiviu Dudau 
274ad49f860SLiviu Dudau 	drm->mode_config.min_width = hwdev->min_line_size;
275ad49f860SLiviu Dudau 	drm->mode_config.min_height = hwdev->min_line_size;
276ad49f860SLiviu Dudau 	drm->mode_config.max_width = hwdev->max_line_size;
277ad49f860SLiviu Dudau 	drm->mode_config.max_height = hwdev->max_line_size;
278ad49f860SLiviu Dudau 	drm->mode_config.funcs = &malidp_mode_config_funcs;
279ad49f860SLiviu Dudau 	drm->mode_config.helper_private = &malidp_mode_config_helpers;
280ad49f860SLiviu Dudau 
281ad49f860SLiviu Dudau 	ret = malidp_crtc_init(drm);
2828cbc5cafSBrian Starkey 	if (ret)
2838cbc5cafSBrian Starkey 		goto crtc_fail;
2848cbc5cafSBrian Starkey 
2858cbc5cafSBrian Starkey 	ret = malidp_mw_connector_init(drm);
2868cbc5cafSBrian Starkey 	if (ret)
2878cbc5cafSBrian Starkey 		goto crtc_fail;
288ad49f860SLiviu Dudau 
289ad49f860SLiviu Dudau 	return 0;
2908cbc5cafSBrian Starkey 
2918cbc5cafSBrian Starkey crtc_fail:
2928cbc5cafSBrian Starkey 	drm_mode_config_cleanup(drm);
2938cbc5cafSBrian Starkey 	return ret;
294ad49f860SLiviu Dudau }
295ad49f860SLiviu Dudau 
296de9c4810SBrian Starkey static void malidp_fini(struct drm_device *drm)
297de9c4810SBrian Starkey {
298de9c4810SBrian Starkey 	drm_mode_config_cleanup(drm);
299de9c4810SBrian Starkey }
300de9c4810SBrian Starkey 
301ad49f860SLiviu Dudau static int malidp_irq_init(struct platform_device *pdev)
302ad49f860SLiviu Dudau {
303ad49f860SLiviu Dudau 	int irq_de, irq_se, ret = 0;
304ad49f860SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(&pdev->dev);
305ad49f860SLiviu Dudau 
306ad49f860SLiviu Dudau 	/* fetch the interrupts from DT */
307ad49f860SLiviu Dudau 	irq_de = platform_get_irq_byname(pdev, "DE");
308ad49f860SLiviu Dudau 	if (irq_de < 0) {
309ad49f860SLiviu Dudau 		DRM_ERROR("no 'DE' IRQ specified!\n");
310ad49f860SLiviu Dudau 		return irq_de;
311ad49f860SLiviu Dudau 	}
312ad49f860SLiviu Dudau 	irq_se = platform_get_irq_byname(pdev, "SE");
313ad49f860SLiviu Dudau 	if (irq_se < 0) {
314ad49f860SLiviu Dudau 		DRM_ERROR("no 'SE' IRQ specified!\n");
315ad49f860SLiviu Dudau 		return irq_se;
316ad49f860SLiviu Dudau 	}
317ad49f860SLiviu Dudau 
318ad49f860SLiviu Dudau 	ret = malidp_de_irq_init(drm, irq_de);
319ad49f860SLiviu Dudau 	if (ret)
320ad49f860SLiviu Dudau 		return ret;
321ad49f860SLiviu Dudau 
322ad49f860SLiviu Dudau 	ret = malidp_se_irq_init(drm, irq_se);
323ad49f860SLiviu Dudau 	if (ret) {
324ad49f860SLiviu Dudau 		malidp_de_irq_fini(drm);
325ad49f860SLiviu Dudau 		return ret;
326ad49f860SLiviu Dudau 	}
327ad49f860SLiviu Dudau 
328ad49f860SLiviu Dudau 	return 0;
329ad49f860SLiviu Dudau }
330ad49f860SLiviu Dudau 
331d55f7e5dSDaniel Vetter DEFINE_DRM_GEM_CMA_FOPS(fops);
332ad49f860SLiviu Dudau 
3335ed4fdfaSLiviu Dudau static int malidp_dumb_create(struct drm_file *file_priv,
3345ed4fdfaSLiviu Dudau 			      struct drm_device *drm,
3355ed4fdfaSLiviu Dudau 			      struct drm_mode_create_dumb *args)
3365ed4fdfaSLiviu Dudau {
3375ed4fdfaSLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
3385ed4fdfaSLiviu Dudau 	/* allocate for the worst case scenario, i.e. rotated buffers */
3395ed4fdfaSLiviu Dudau 	u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 1);
3405ed4fdfaSLiviu Dudau 
3415ed4fdfaSLiviu Dudau 	args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment);
3425ed4fdfaSLiviu Dudau 
3435ed4fdfaSLiviu Dudau 	return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
3445ed4fdfaSLiviu Dudau }
3455ed4fdfaSLiviu Dudau 
346ad49f860SLiviu Dudau static struct drm_driver malidp_driver = {
347ad49f860SLiviu Dudau 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
348ad49f860SLiviu Dudau 			   DRIVER_PRIME,
349bdecd835SNoralf Trønnes 	.lastclose = drm_fb_helper_lastclose,
350ad49f860SLiviu Dudau 	.gem_free_object_unlocked = drm_gem_cma_free_object,
351ad49f860SLiviu Dudau 	.gem_vm_ops = &drm_gem_cma_vm_ops,
3525ed4fdfaSLiviu Dudau 	.dumb_create = malidp_dumb_create,
353ad49f860SLiviu Dudau 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
354ad49f860SLiviu Dudau 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
355ad49f860SLiviu Dudau 	.gem_prime_export = drm_gem_prime_export,
356ad49f860SLiviu Dudau 	.gem_prime_import = drm_gem_prime_import,
357ad49f860SLiviu Dudau 	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
358ad49f860SLiviu Dudau 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
359ad49f860SLiviu Dudau 	.gem_prime_vmap = drm_gem_cma_prime_vmap,
360ad49f860SLiviu Dudau 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
361ad49f860SLiviu Dudau 	.gem_prime_mmap = drm_gem_cma_prime_mmap,
362ad49f860SLiviu Dudau 	.fops = &fops,
363ad49f860SLiviu Dudau 	.name = "mali-dp",
364ad49f860SLiviu Dudau 	.desc = "ARM Mali Display Processor driver",
365ad49f860SLiviu Dudau 	.date = "20160106",
366ad49f860SLiviu Dudau 	.major = 1,
367ad49f860SLiviu Dudau 	.minor = 0,
368ad49f860SLiviu Dudau };
369ad49f860SLiviu Dudau 
370ad49f860SLiviu Dudau static const struct of_device_id  malidp_drm_of_match[] = {
371ad49f860SLiviu Dudau 	{
372ad49f860SLiviu Dudau 		.compatible = "arm,mali-dp500",
373ad49f860SLiviu Dudau 		.data = &malidp_device[MALIDP_500]
374ad49f860SLiviu Dudau 	},
375ad49f860SLiviu Dudau 	{
376ad49f860SLiviu Dudau 		.compatible = "arm,mali-dp550",
377ad49f860SLiviu Dudau 		.data = &malidp_device[MALIDP_550]
378ad49f860SLiviu Dudau 	},
379ad49f860SLiviu Dudau 	{
380ad49f860SLiviu Dudau 		.compatible = "arm,mali-dp650",
381ad49f860SLiviu Dudau 		.data = &malidp_device[MALIDP_650]
382ad49f860SLiviu Dudau 	},
383ad49f860SLiviu Dudau 	{},
384ad49f860SLiviu Dudau };
385ad49f860SLiviu Dudau MODULE_DEVICE_TABLE(of, malidp_drm_of_match);
386ad49f860SLiviu Dudau 
387592d8c8cSMihail Atanassov static bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev,
388592d8c8cSMihail Atanassov 				       const struct of_device_id *dev_id)
389592d8c8cSMihail Atanassov {
390592d8c8cSMihail Atanassov 	u32 core_id;
391592d8c8cSMihail Atanassov 	const char *compatstr_dp500 = "arm,mali-dp500";
392592d8c8cSMihail Atanassov 	bool is_dp500;
393592d8c8cSMihail Atanassov 	bool dt_is_dp500;
394592d8c8cSMihail Atanassov 
395592d8c8cSMihail Atanassov 	/*
396592d8c8cSMihail Atanassov 	 * The DP500 CORE_ID register is in a different location, so check it
397592d8c8cSMihail Atanassov 	 * first. If the product id field matches, then this is DP500, otherwise
398592d8c8cSMihail Atanassov 	 * check the DP550/650 CORE_ID register.
399592d8c8cSMihail Atanassov 	 */
400592d8c8cSMihail Atanassov 	core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID);
401592d8c8cSMihail Atanassov 	/* Offset 0x18 will never read 0x500 on products other than DP500. */
402592d8c8cSMihail Atanassov 	is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500);
403592d8c8cSMihail Atanassov 	dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500,
404592d8c8cSMihail Atanassov 			      sizeof(dev_id->compatible)) != NULL;
405592d8c8cSMihail Atanassov 	if (is_dp500 != dt_is_dp500) {
406592d8c8cSMihail Atanassov 		DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n",
407592d8c8cSMihail Atanassov 			  dev_id->compatible, is_dp500 ? "is" : "is not");
408592d8c8cSMihail Atanassov 		return false;
409592d8c8cSMihail Atanassov 	} else if (!dt_is_dp500) {
410592d8c8cSMihail Atanassov 		u16 product_id;
411592d8c8cSMihail Atanassov 		char buf[32];
412592d8c8cSMihail Atanassov 
413592d8c8cSMihail Atanassov 		core_id = malidp_hw_read(hwdev,
414592d8c8cSMihail Atanassov 					 MALIDP550_DC_BASE + MALIDP_DE_CORE_ID);
415592d8c8cSMihail Atanassov 		product_id = MALIDP_PRODUCT_ID(core_id);
416592d8c8cSMihail Atanassov 		snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id);
417592d8c8cSMihail Atanassov 		if (!strnstr(dev_id->compatible, buf,
418592d8c8cSMihail Atanassov 			     sizeof(dev_id->compatible))) {
419592d8c8cSMihail Atanassov 			DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n",
420592d8c8cSMihail Atanassov 				  dev_id->compatible, product_id);
421592d8c8cSMihail Atanassov 			return false;
422592d8c8cSMihail Atanassov 		}
423592d8c8cSMihail Atanassov 	}
424592d8c8cSMihail Atanassov 	return true;
425592d8c8cSMihail Atanassov }
426592d8c8cSMihail Atanassov 
4274d6000edSMihail Atanassov static bool malidp_has_sufficient_address_space(const struct resource *res,
4284d6000edSMihail Atanassov 						const struct of_device_id *dev_id)
4294d6000edSMihail Atanassov {
4304d6000edSMihail Atanassov 	resource_size_t res_size = resource_size(res);
4314d6000edSMihail Atanassov 	const char *compatstr_dp500 = "arm,mali-dp500";
4324d6000edSMihail Atanassov 
4334d6000edSMihail Atanassov 	if (!strnstr(dev_id->compatible, compatstr_dp500,
4344d6000edSMihail Atanassov 		     sizeof(dev_id->compatible)))
4354d6000edSMihail Atanassov 		return res_size >= MALIDP550_ADDR_SPACE_SIZE;
4364d6000edSMihail Atanassov 	else if (res_size < MALIDP500_ADDR_SPACE_SIZE)
4374d6000edSMihail Atanassov 		return false;
4384d6000edSMihail Atanassov 	return true;
4394d6000edSMihail Atanassov }
4404d6000edSMihail Atanassov 
44150c7512fSLiviu Dudau static ssize_t core_id_show(struct device *dev, struct device_attribute *attr,
44250c7512fSLiviu Dudau 			    char *buf)
44350c7512fSLiviu Dudau {
44450c7512fSLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
44550c7512fSLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
44650c7512fSLiviu Dudau 
44750c7512fSLiviu Dudau 	return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id);
44850c7512fSLiviu Dudau }
44950c7512fSLiviu Dudau 
45050c7512fSLiviu Dudau DEVICE_ATTR_RO(core_id);
45150c7512fSLiviu Dudau 
45250c7512fSLiviu Dudau static int malidp_init_sysfs(struct device *dev)
45350c7512fSLiviu Dudau {
45450c7512fSLiviu Dudau 	int ret = device_create_file(dev, &dev_attr_core_id);
45550c7512fSLiviu Dudau 
45650c7512fSLiviu Dudau 	if (ret)
45750c7512fSLiviu Dudau 		DRM_ERROR("failed to create device file for core_id\n");
45850c7512fSLiviu Dudau 
45950c7512fSLiviu Dudau 	return ret;
46050c7512fSLiviu Dudau }
46150c7512fSLiviu Dudau 
46250c7512fSLiviu Dudau static void malidp_fini_sysfs(struct device *dev)
46350c7512fSLiviu Dudau {
46450c7512fSLiviu Dudau 	device_remove_file(dev, &dev_attr_core_id);
46550c7512fSLiviu Dudau }
46650c7512fSLiviu Dudau 
467ad49f860SLiviu Dudau #define MAX_OUTPUT_CHANNELS	3
468ad49f860SLiviu Dudau 
46985f64218SLiviu Dudau static int malidp_runtime_pm_suspend(struct device *dev)
47085f64218SLiviu Dudau {
47185f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
47285f64218SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
47385f64218SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
47485f64218SLiviu Dudau 
47585f64218SLiviu Dudau 	/* we can only suspend if the hardware is in config mode */
476a6993b21SLiviu Dudau 	WARN_ON(!hwdev->hw->in_config_mode(hwdev));
47785f64218SLiviu Dudau 
47885f64218SLiviu Dudau 	hwdev->pm_suspended = true;
47985f64218SLiviu Dudau 	clk_disable_unprepare(hwdev->mclk);
48085f64218SLiviu Dudau 	clk_disable_unprepare(hwdev->aclk);
48185f64218SLiviu Dudau 	clk_disable_unprepare(hwdev->pclk);
48285f64218SLiviu Dudau 
48385f64218SLiviu Dudau 	return 0;
48485f64218SLiviu Dudau }
48585f64218SLiviu Dudau 
48685f64218SLiviu Dudau static int malidp_runtime_pm_resume(struct device *dev)
48785f64218SLiviu Dudau {
48885f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
48985f64218SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
49085f64218SLiviu Dudau 	struct malidp_hw_device *hwdev = malidp->dev;
49185f64218SLiviu Dudau 
49285f64218SLiviu Dudau 	clk_prepare_enable(hwdev->pclk);
49385f64218SLiviu Dudau 	clk_prepare_enable(hwdev->aclk);
49485f64218SLiviu Dudau 	clk_prepare_enable(hwdev->mclk);
49585f64218SLiviu Dudau 	hwdev->pm_suspended = false;
49685f64218SLiviu Dudau 
49785f64218SLiviu Dudau 	return 0;
49885f64218SLiviu Dudau }
49985f64218SLiviu Dudau 
500ad49f860SLiviu Dudau static int malidp_bind(struct device *dev)
501ad49f860SLiviu Dudau {
502ad49f860SLiviu Dudau 	struct resource *res;
503ad49f860SLiviu Dudau 	struct drm_device *drm;
504ad49f860SLiviu Dudau 	struct malidp_drm *malidp;
505ad49f860SLiviu Dudau 	struct malidp_hw_device *hwdev;
506ad49f860SLiviu Dudau 	struct platform_device *pdev = to_platform_device(dev);
507592d8c8cSMihail Atanassov 	struct of_device_id const *dev_id;
508ad49f860SLiviu Dudau 	/* number of lines for the R, G and B output */
509ad49f860SLiviu Dudau 	u8 output_width[MAX_OUTPUT_CHANNELS];
510ad49f860SLiviu Dudau 	int ret = 0, i;
511ad49f860SLiviu Dudau 	u32 version, out_depth = 0;
512ad49f860SLiviu Dudau 
513ad49f860SLiviu Dudau 	malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL);
514ad49f860SLiviu Dudau 	if (!malidp)
515ad49f860SLiviu Dudau 		return -ENOMEM;
516ad49f860SLiviu Dudau 
517ad49f860SLiviu Dudau 	hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL);
518ad49f860SLiviu Dudau 	if (!hwdev)
519ad49f860SLiviu Dudau 		return -ENOMEM;
520ad49f860SLiviu Dudau 
521a6993b21SLiviu Dudau 	hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev);
522ad49f860SLiviu Dudau 	malidp->dev = hwdev;
523ad49f860SLiviu Dudau 
524ad49f860SLiviu Dudau 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
525ad49f860SLiviu Dudau 	hwdev->regs = devm_ioremap_resource(dev, res);
5261a9d71f8SWei Yongjun 	if (IS_ERR(hwdev->regs))
527ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->regs);
528ad49f860SLiviu Dudau 
529ad49f860SLiviu Dudau 	hwdev->pclk = devm_clk_get(dev, "pclk");
530ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->pclk))
531ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->pclk);
532ad49f860SLiviu Dudau 
533ad49f860SLiviu Dudau 	hwdev->aclk = devm_clk_get(dev, "aclk");
534ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->aclk))
535ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->aclk);
536ad49f860SLiviu Dudau 
537ad49f860SLiviu Dudau 	hwdev->mclk = devm_clk_get(dev, "mclk");
538ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->mclk))
539ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->mclk);
540ad49f860SLiviu Dudau 
541ad49f860SLiviu Dudau 	hwdev->pxlclk = devm_clk_get(dev, "pxlclk");
542ad49f860SLiviu Dudau 	if (IS_ERR(hwdev->pxlclk))
543ad49f860SLiviu Dudau 		return PTR_ERR(hwdev->pxlclk);
544ad49f860SLiviu Dudau 
545ad49f860SLiviu Dudau 	/* Get the optional framebuffer memory resource */
546ad49f860SLiviu Dudau 	ret = of_reserved_mem_device_init(dev);
547ad49f860SLiviu Dudau 	if (ret && ret != -ENODEV)
548ad49f860SLiviu Dudau 		return ret;
549ad49f860SLiviu Dudau 
550ad49f860SLiviu Dudau 	drm = drm_dev_alloc(&malidp_driver, dev);
5510f288605STom Gundersen 	if (IS_ERR(drm)) {
5520f288605STom Gundersen 		ret = PTR_ERR(drm);
553ad49f860SLiviu Dudau 		goto alloc_fail;
554ad49f860SLiviu Dudau 	}
555ad49f860SLiviu Dudau 
55685f64218SLiviu Dudau 	drm->dev_private = malidp;
55785f64218SLiviu Dudau 	dev_set_drvdata(dev, drm);
55885f64218SLiviu Dudau 
55985f64218SLiviu Dudau 	/* Enable power management */
56085f64218SLiviu Dudau 	pm_runtime_enable(dev);
56185f64218SLiviu Dudau 
56285f64218SLiviu Dudau 	/* Resume device to enable the clocks */
56385f64218SLiviu Dudau 	if (pm_runtime_enabled(dev))
56485f64218SLiviu Dudau 		pm_runtime_get_sync(dev);
56585f64218SLiviu Dudau 	else
56685f64218SLiviu Dudau 		malidp_runtime_pm_resume(dev);
567ad49f860SLiviu Dudau 
568592d8c8cSMihail Atanassov 	dev_id = of_match_device(malidp_drm_of_match, dev);
569592d8c8cSMihail Atanassov 	if (!dev_id) {
570592d8c8cSMihail Atanassov 		ret = -EINVAL;
571592d8c8cSMihail Atanassov 		goto query_hw_fail;
572592d8c8cSMihail Atanassov 	}
573592d8c8cSMihail Atanassov 
5744d6000edSMihail Atanassov 	if (!malidp_has_sufficient_address_space(res, dev_id)) {
5754d6000edSMihail Atanassov 		DRM_ERROR("Insufficient address space in device-tree.\n");
5764d6000edSMihail Atanassov 		ret = -EINVAL;
5774d6000edSMihail Atanassov 		goto query_hw_fail;
5784d6000edSMihail Atanassov 	}
5794d6000edSMihail Atanassov 
580592d8c8cSMihail Atanassov 	if (!malidp_is_compatible_hw_id(hwdev, dev_id)) {
581592d8c8cSMihail Atanassov 		ret = -EINVAL;
582592d8c8cSMihail Atanassov 		goto query_hw_fail;
583592d8c8cSMihail Atanassov 	}
584592d8c8cSMihail Atanassov 
585a6993b21SLiviu Dudau 	ret = hwdev->hw->query_hw(hwdev);
586ad49f860SLiviu Dudau 	if (ret) {
587ad49f860SLiviu Dudau 		DRM_ERROR("Invalid HW configuration\n");
588ad49f860SLiviu Dudau 		goto query_hw_fail;
589ad49f860SLiviu Dudau 	}
590ad49f860SLiviu Dudau 
591a6993b21SLiviu Dudau 	version = malidp_hw_read(hwdev, hwdev->hw->map.dc_base + MALIDP_DE_CORE_ID);
592ad49f860SLiviu Dudau 	DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16,
593ad49f860SLiviu Dudau 		 (version >> 12) & 0xf, (version >> 8) & 0xf);
594ad49f860SLiviu Dudau 
59550c7512fSLiviu Dudau 	malidp->core_id = version;
59650c7512fSLiviu Dudau 
597ad49f860SLiviu Dudau 	/* set the number of lines used for output of RGB data */
598ad49f860SLiviu Dudau 	ret = of_property_read_u8_array(dev->of_node,
599ad49f860SLiviu Dudau 					"arm,malidp-output-port-lines",
600ad49f860SLiviu Dudau 					output_width, MAX_OUTPUT_CHANNELS);
601ad49f860SLiviu Dudau 	if (ret)
602ad49f860SLiviu Dudau 		goto query_hw_fail;
603ad49f860SLiviu Dudau 
604ad49f860SLiviu Dudau 	for (i = 0; i < MAX_OUTPUT_CHANNELS; i++)
605ad49f860SLiviu Dudau 		out_depth = (out_depth << 8) | (output_width[i] & 0xf);
606a6993b21SLiviu Dudau 	malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base);
607ad49f860SLiviu Dudau 
6081cb3cbe7SLiviu Dudau 	atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT);
609ad49f860SLiviu Dudau 	init_waitqueue_head(&malidp->wq);
610ad49f860SLiviu Dudau 
611ad49f860SLiviu Dudau 	ret = malidp_init(drm);
612ad49f860SLiviu Dudau 	if (ret < 0)
61385f64218SLiviu Dudau 		goto query_hw_fail;
614ad49f860SLiviu Dudau 
61550c7512fSLiviu Dudau 	ret = malidp_init_sysfs(dev);
61650c7512fSLiviu Dudau 	if (ret)
61750c7512fSLiviu Dudau 		goto init_fail;
61850c7512fSLiviu Dudau 
619ad49f860SLiviu Dudau 	/* Set the CRTC's port so that the encoder component can find it */
62086418f90SRob Herring 	malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0);
621ad49f860SLiviu Dudau 
622ad49f860SLiviu Dudau 	ret = component_bind_all(dev, drm);
623ad49f860SLiviu Dudau 	if (ret) {
624ad49f860SLiviu Dudau 		DRM_ERROR("Failed to bind all components\n");
625ad49f860SLiviu Dudau 		goto bind_fail;
626ad49f860SLiviu Dudau 	}
627ad49f860SLiviu Dudau 
628ad49f860SLiviu Dudau 	ret = malidp_irq_init(pdev);
629ad49f860SLiviu Dudau 	if (ret < 0)
630ad49f860SLiviu Dudau 		goto irq_init_fail;
631ad49f860SLiviu Dudau 
632a6a7b9a2SLiviu Dudau 	drm->irq_enabled = true;
633a6a7b9a2SLiviu Dudau 
634ad49f860SLiviu Dudau 	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
635ad49f860SLiviu Dudau 	if (ret < 0) {
636ad49f860SLiviu Dudau 		DRM_ERROR("failed to initialise vblank\n");
637ad49f860SLiviu Dudau 		goto vblank_fail;
638ad49f860SLiviu Dudau 	}
63985f64218SLiviu Dudau 	pm_runtime_put(dev);
640ad49f860SLiviu Dudau 
641ad49f860SLiviu Dudau 	drm_mode_config_reset(drm);
642ad49f860SLiviu Dudau 
643bdecd835SNoralf Trønnes 	ret = drm_fb_cma_fbdev_init(drm, 32, 0);
644bdecd835SNoralf Trønnes 	if (ret)
645ad49f860SLiviu Dudau 		goto fbdev_fail;
646ad49f860SLiviu Dudau 
647ad49f860SLiviu Dudau 	drm_kms_helper_poll_init(drm);
64890731c24SBrian Starkey 
64990731c24SBrian Starkey 	ret = drm_dev_register(drm, 0);
65090731c24SBrian Starkey 	if (ret)
65190731c24SBrian Starkey 		goto register_fail;
65290731c24SBrian Starkey 
653ad49f860SLiviu Dudau 	return 0;
654ad49f860SLiviu Dudau 
65590731c24SBrian Starkey register_fail:
656bdecd835SNoralf Trønnes 	drm_fb_cma_fbdev_fini(drm);
65785f64218SLiviu Dudau 	drm_kms_helper_poll_fini(drm);
658ad49f860SLiviu Dudau fbdev_fail:
65985f64218SLiviu Dudau 	pm_runtime_get_sync(dev);
660ad49f860SLiviu Dudau vblank_fail:
661ad49f860SLiviu Dudau 	malidp_se_irq_fini(drm);
662ad49f860SLiviu Dudau 	malidp_de_irq_fini(drm);
663a6a7b9a2SLiviu Dudau 	drm->irq_enabled = false;
664ad49f860SLiviu Dudau irq_init_fail:
665109c4d18SAyan Kumar Halder 	drm_atomic_helper_shutdown(drm);
666ad49f860SLiviu Dudau 	component_unbind_all(dev, drm);
667ad49f860SLiviu Dudau bind_fail:
6683c31760eSBrian Starkey 	of_node_put(malidp->crtc.port);
6693c31760eSBrian Starkey 	malidp->crtc.port = NULL;
67050c7512fSLiviu Dudau init_fail:
67150c7512fSLiviu Dudau 	malidp_fini_sysfs(dev);
672de9c4810SBrian Starkey 	malidp_fini(drm);
67385f64218SLiviu Dudau query_hw_fail:
67485f64218SLiviu Dudau 	pm_runtime_put(dev);
67585f64218SLiviu Dudau 	if (pm_runtime_enabled(dev))
67685f64218SLiviu Dudau 		pm_runtime_disable(dev);
67785f64218SLiviu Dudau 	else
67885f64218SLiviu Dudau 		malidp_runtime_pm_suspend(dev);
679ad49f860SLiviu Dudau 	drm->dev_private = NULL;
680ad49f860SLiviu Dudau 	dev_set_drvdata(dev, NULL);
6810970d7a2SSrishti Sharma 	drm_dev_put(drm);
682ad49f860SLiviu Dudau alloc_fail:
683ad49f860SLiviu Dudau 	of_reserved_mem_device_release(dev);
684ad49f860SLiviu Dudau 
685ad49f860SLiviu Dudau 	return ret;
686ad49f860SLiviu Dudau }
687ad49f860SLiviu Dudau 
688ad49f860SLiviu Dudau static void malidp_unbind(struct device *dev)
689ad49f860SLiviu Dudau {
690ad49f860SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
691ad49f860SLiviu Dudau 	struct malidp_drm *malidp = drm->dev_private;
692ad49f860SLiviu Dudau 
69390731c24SBrian Starkey 	drm_dev_unregister(drm);
694bdecd835SNoralf Trønnes 	drm_fb_cma_fbdev_fini(drm);
695ad49f860SLiviu Dudau 	drm_kms_helper_poll_fini(drm);
69685f64218SLiviu Dudau 	pm_runtime_get_sync(dev);
69757085dcaSLiviu Dudau 	drm_crtc_vblank_off(&malidp->crtc);
698ad49f860SLiviu Dudau 	malidp_se_irq_fini(drm);
699ad49f860SLiviu Dudau 	malidp_de_irq_fini(drm);
70057085dcaSLiviu Dudau 	drm->irq_enabled = false;
701109c4d18SAyan Kumar Halder 	drm_atomic_helper_shutdown(drm);
702ad49f860SLiviu Dudau 	component_unbind_all(dev, drm);
7033c31760eSBrian Starkey 	of_node_put(malidp->crtc.port);
7043c31760eSBrian Starkey 	malidp->crtc.port = NULL;
70550c7512fSLiviu Dudau 	malidp_fini_sysfs(dev);
706de9c4810SBrian Starkey 	malidp_fini(drm);
70785f64218SLiviu Dudau 	pm_runtime_put(dev);
70885f64218SLiviu Dudau 	if (pm_runtime_enabled(dev))
70985f64218SLiviu Dudau 		pm_runtime_disable(dev);
71085f64218SLiviu Dudau 	else
71185f64218SLiviu Dudau 		malidp_runtime_pm_suspend(dev);
712ad49f860SLiviu Dudau 	drm->dev_private = NULL;
713ad49f860SLiviu Dudau 	dev_set_drvdata(dev, NULL);
7140970d7a2SSrishti Sharma 	drm_dev_put(drm);
715ad49f860SLiviu Dudau 	of_reserved_mem_device_release(dev);
716ad49f860SLiviu Dudau }
717ad49f860SLiviu Dudau 
718ad49f860SLiviu Dudau static const struct component_master_ops malidp_master_ops = {
719ad49f860SLiviu Dudau 	.bind = malidp_bind,
720ad49f860SLiviu Dudau 	.unbind = malidp_unbind,
721ad49f860SLiviu Dudau };
722ad49f860SLiviu Dudau 
723ad49f860SLiviu Dudau static int malidp_compare_dev(struct device *dev, void *data)
724ad49f860SLiviu Dudau {
725ad49f860SLiviu Dudau 	struct device_node *np = data;
726ad49f860SLiviu Dudau 
727ad49f860SLiviu Dudau 	return dev->of_node == np;
728ad49f860SLiviu Dudau }
729ad49f860SLiviu Dudau 
730ad49f860SLiviu Dudau static int malidp_platform_probe(struct platform_device *pdev)
731ad49f860SLiviu Dudau {
73286418f90SRob Herring 	struct device_node *port;
733ad49f860SLiviu Dudau 	struct component_match *match = NULL;
734ad49f860SLiviu Dudau 
735ad49f860SLiviu Dudau 	if (!pdev->dev.of_node)
736ad49f860SLiviu Dudau 		return -ENODEV;
737ad49f860SLiviu Dudau 
738ad49f860SLiviu Dudau 	/* there is only one output port inside each device, find it */
73986418f90SRob Herring 	port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
74086418f90SRob Herring 	if (!port)
741ad49f860SLiviu Dudau 		return -ENODEV;
742ad49f860SLiviu Dudau 
74397ac0e47SRussell King 	drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev,
74497ac0e47SRussell King 				   port);
74597ac0e47SRussell King 	of_node_put(port);
746ad49f860SLiviu Dudau 	return component_master_add_with_match(&pdev->dev, &malidp_master_ops,
747ad49f860SLiviu Dudau 					       match);
748ad49f860SLiviu Dudau }
749ad49f860SLiviu Dudau 
750ad49f860SLiviu Dudau static int malidp_platform_remove(struct platform_device *pdev)
751ad49f860SLiviu Dudau {
752ad49f860SLiviu Dudau 	component_master_del(&pdev->dev, &malidp_master_ops);
753ad49f860SLiviu Dudau 	return 0;
754ad49f860SLiviu Dudau }
755ad49f860SLiviu Dudau 
75685f64218SLiviu Dudau static int __maybe_unused malidp_pm_suspend(struct device *dev)
75785f64218SLiviu Dudau {
75885f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
75985f64218SLiviu Dudau 
760194b8799SNoralf Trønnes 	return drm_mode_config_helper_suspend(drm);
76185f64218SLiviu Dudau }
76285f64218SLiviu Dudau 
76385f64218SLiviu Dudau static int __maybe_unused malidp_pm_resume(struct device *dev)
76485f64218SLiviu Dudau {
76585f64218SLiviu Dudau 	struct drm_device *drm = dev_get_drvdata(dev);
76685f64218SLiviu Dudau 
767194b8799SNoralf Trønnes 	drm_mode_config_helper_resume(drm);
76885f64218SLiviu Dudau 
76985f64218SLiviu Dudau 	return 0;
77085f64218SLiviu Dudau }
77185f64218SLiviu Dudau 
77285f64218SLiviu Dudau static const struct dev_pm_ops malidp_pm_ops = {
77385f64218SLiviu Dudau 	SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \
77485f64218SLiviu Dudau 	SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL)
77585f64218SLiviu Dudau };
77685f64218SLiviu Dudau 
777ad49f860SLiviu Dudau static struct platform_driver malidp_platform_driver = {
778ad49f860SLiviu Dudau 	.probe		= malidp_platform_probe,
779ad49f860SLiviu Dudau 	.remove		= malidp_platform_remove,
780ad49f860SLiviu Dudau 	.driver	= {
781ad49f860SLiviu Dudau 		.name = "mali-dp",
78285f64218SLiviu Dudau 		.pm = &malidp_pm_ops,
783ad49f860SLiviu Dudau 		.of_match_table	= malidp_drm_of_match,
784ad49f860SLiviu Dudau 	},
785ad49f860SLiviu Dudau };
786ad49f860SLiviu Dudau 
787ad49f860SLiviu Dudau module_platform_driver(malidp_platform_driver);
788ad49f860SLiviu Dudau 
789ad49f860SLiviu Dudau MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>");
790ad49f860SLiviu Dudau MODULE_DESCRIPTION("ARM Mali DP DRM driver");
791ad49f860SLiviu Dudau MODULE_LICENSE("GPL v2");
792