diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_crtc.c')
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 181 | 
1 files changed, 143 insertions, 38 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index ebc01503d50..95c9435d026 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -33,12 +33,13 @@ enum exynos_crtc_mode {   *   * @drm_crtc: crtc object.   * @drm_plane: pointer of private plane object for this crtc + * @manager: the manager associated with this crtc   * @pipe: a crtc index created at load() with a new crtc object creation   *	and the crtc object would be set to private->crtc array   *	to get a crtc object corresponding to this pipe from private->crtc - *	array when irq interrupt occured. the reason of using this pipe is that + *	array when irq interrupt occurred. the reason of using this pipe is that   *	drm framework doesn't support multiple irq yet. - *	we can refer to the crtc to current hardware interrupt occured through + *	we can refer to the crtc to current hardware interrupt occurred through   *	this pipe value.   * @dpms: store the crtc dpms value   * @mode: store the crtc mode value @@ -46,6 +47,7 @@ enum exynos_crtc_mode {  struct exynos_drm_crtc {  	struct drm_crtc			drm_crtc;  	struct drm_plane		*plane; +	struct exynos_drm_manager	*manager;  	unsigned int			pipe;  	unsigned int			dpms;  	enum exynos_crtc_mode		mode; @@ -56,6 +58,7 @@ struct exynos_drm_crtc {  static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)  {  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); +	struct exynos_drm_manager *manager = exynos_crtc->manager;  	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode); @@ -71,7 +74,9 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)  		drm_vblank_off(crtc->dev, exynos_crtc->pipe);  	} -	exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms); +	if (manager->ops->dpms) +		manager->ops->dpms(manager, mode); +  	exynos_crtc->dpms = mode;  } @@ -83,9 +88,15 @@ static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)  static void exynos_drm_crtc_commit(struct drm_crtc *crtc)  {  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); +	struct exynos_drm_manager *manager = exynos_crtc->manager;  	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +  	exynos_plane_commit(exynos_crtc->plane); + +	if (manager->ops->commit) +		manager->ops->commit(manager); +  	exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);  } @@ -94,7 +105,12 @@ exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,  			    const struct drm_display_mode *mode,  			    struct drm_display_mode *adjusted_mode)  { -	/* drm framework doesn't check NULL */ +	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); +	struct exynos_drm_manager *manager = exynos_crtc->manager; + +	if (manager->ops->mode_fixup) +		return manager->ops->mode_fixup(manager, mode, adjusted_mode); +  	return true;  } @@ -104,10 +120,10 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,  			  struct drm_framebuffer *old_fb)  {  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); +	struct exynos_drm_manager *manager = exynos_crtc->manager;  	struct drm_plane *plane = exynos_crtc->plane;  	unsigned int crtc_w;  	unsigned int crtc_h; -	int pipe = exynos_crtc->pipe;  	int ret;  	/* @@ -116,18 +132,20 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,  	 */  	memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); -	crtc_w = crtc->fb->width - x; -	crtc_h = crtc->fb->height - y; +	crtc_w = crtc->primary->fb->width - x; +	crtc_h = crtc->primary->fb->height - y; -	ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, +	if (manager->ops->mode_set) +		manager->ops->mode_set(manager, &crtc->mode); + +	ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,  				    x, y, crtc_w, crtc_h);  	if (ret)  		return ret;  	plane->crtc = crtc; -	plane->fb = crtc->fb; - -	exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); +	plane->fb = crtc->primary->fb; +	drm_framebuffer_reference(plane->fb);  	return 0;  } @@ -147,10 +165,10 @@ static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y,  		return -EPERM;  	} -	crtc_w = crtc->fb->width - x; -	crtc_h = crtc->fb->height - y; +	crtc_w = crtc->primary->fb->width - x; +	crtc_h = crtc->primary->fb->height - y; -	ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, +	ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,  				    x, y, crtc_w, crtc_h);  	if (ret)  		return ret; @@ -168,10 +186,19 @@ static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,  static void exynos_drm_crtc_disable(struct drm_crtc *crtc)  { -	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); +	struct drm_plane *plane; +	int ret; -	exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);  	exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + +	drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) { +		if (plane->crtc != crtc) +			continue; + +		ret = plane->funcs->disable_plane(plane); +		if (ret) +			DRM_ERROR("Failed to disable plane %d\n", ret); +	}  }  static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { @@ -192,7 +219,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,  	struct drm_device *dev = crtc->dev;  	struct exynos_drm_private *dev_priv = dev->dev_private;  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); -	struct drm_framebuffer *old_fb = crtc->fb; +	struct drm_framebuffer *old_fb = crtc->primary->fb;  	int ret = -EINVAL;  	/* when the page flip is requested, crtc's dpms should be on */ @@ -223,11 +250,11 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,  		atomic_set(&exynos_crtc->pending_flip, 1);  		spin_unlock_irq(&dev->event_lock); -		crtc->fb = fb; +		crtc->primary->fb = fb;  		ret = exynos_drm_crtc_mode_set_commit(crtc, crtc->x, crtc->y,  						    NULL);  		if (ret) { -			crtc->fb = old_fb; +			crtc->primary->fb = old_fb;  			spin_lock_irq(&dev->event_lock);  			drm_vblank_put(dev, exynos_crtc->pipe); @@ -318,31 +345,35 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)  	drm_object_attach_property(&crtc->base, prop, 0);  } -int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) +int exynos_drm_crtc_create(struct exynos_drm_manager *manager)  {  	struct exynos_drm_crtc *exynos_crtc; -	struct exynos_drm_private *private = dev->dev_private; +	struct exynos_drm_private *private = manager->drm_dev->dev_private;  	struct drm_crtc *crtc;  	exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);  	if (!exynos_crtc)  		return -ENOMEM; -	exynos_crtc->pipe = nr; -	exynos_crtc->dpms = DRM_MODE_DPMS_OFF;  	init_waitqueue_head(&exynos_crtc->pending_flip_queue);  	atomic_set(&exynos_crtc->pending_flip, 0); -	exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true); + +	exynos_crtc->dpms = DRM_MODE_DPMS_OFF; +	exynos_crtc->manager = manager; +	exynos_crtc->pipe = manager->pipe; +	exynos_crtc->plane = exynos_plane_init(manager->drm_dev, +				1 << manager->pipe, true);  	if (!exynos_crtc->plane) {  		kfree(exynos_crtc);  		return -ENOMEM;  	} +	manager->crtc = &exynos_crtc->drm_crtc;  	crtc = &exynos_crtc->drm_crtc; -	private->crtc[nr] = crtc; +	private->crtc[manager->pipe] = crtc; -	drm_crtc_init(dev, crtc, &exynos_crtc_funcs); +	drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs);  	drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);  	exynos_drm_crtc_attach_mode_property(crtc); @@ -350,39 +381,41 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)  	return 0;  } -int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) +int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)  {  	struct exynos_drm_private *private = dev->dev_private;  	struct exynos_drm_crtc *exynos_crtc = -		to_exynos_crtc(private->crtc[crtc]); +		to_exynos_crtc(private->crtc[pipe]); +	struct exynos_drm_manager *manager = exynos_crtc->manager;  	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)  		return -EPERM; -	exynos_drm_fn_encoder(private->crtc[crtc], &crtc, -			exynos_drm_enable_vblank); +	if (manager->ops->enable_vblank) +		manager->ops->enable_vblank(manager);  	return 0;  } -void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) +void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)  {  	struct exynos_drm_private *private = dev->dev_private;  	struct exynos_drm_crtc *exynos_crtc = -		to_exynos_crtc(private->crtc[crtc]); +		to_exynos_crtc(private->crtc[pipe]); +	struct exynos_drm_manager *manager = exynos_crtc->manager;  	if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)  		return; -	exynos_drm_fn_encoder(private->crtc[crtc], &crtc, -			exynos_drm_disable_vblank); +	if (manager->ops->disable_vblank) +		manager->ops->disable_vblank(manager);  } -void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) +void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)  {  	struct exynos_drm_private *dev_priv = dev->dev_private;  	struct drm_pending_vblank_event *e, *t; -	struct drm_crtc *drm_crtc = dev_priv->crtc[crtc]; +	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);  	unsigned long flags; @@ -391,15 +424,87 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)  	list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,  			base.link) {  		/* if event's pipe isn't same as crtc then ignore it. */ -		if (crtc != e->pipe) +		if (pipe != e->pipe)  			continue;  		list_del(&e->base.link);  		drm_send_vblank_event(dev, -1, e); -		drm_vblank_put(dev, crtc); +		drm_vblank_put(dev, pipe);  		atomic_set(&exynos_crtc->pending_flip, 0);  		wake_up(&exynos_crtc->pending_flip_queue);  	}  	spin_unlock_irqrestore(&dev->event_lock, flags);  } + +void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc, +			struct exynos_drm_overlay *overlay) +{ +	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + +	if (manager->ops->win_mode_set) +		manager->ops->win_mode_set(manager, overlay); +} + +void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos) +{ +	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + +	if (manager->ops->win_commit) +		manager->ops->win_commit(manager, zpos); +} + +void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos) +{ +	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + +	if (manager->ops->win_enable) +		manager->ops->win_enable(manager, zpos); +} + +void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos) +{ +	struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + +	if (manager->ops->win_disable) +		manager->ops->win_disable(manager, zpos); +} + +void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) +{ +	struct exynos_drm_manager *manager; +	struct drm_device *dev = fb->dev; +	struct drm_crtc *crtc; + +	/* +	 * make sure that overlay data are updated to real hardware +	 * for all encoders. +	 */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		manager = to_exynos_crtc(crtc)->manager; + +		/* +		 * wait for vblank interrupt +		 * - this makes sure that overlay data are updated to +		 *	real hardware. +		 */ +		if (manager->ops->wait_for_vblank) +			manager->ops->wait_for_vblank(manager); +	} +} + +int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, +					unsigned int out_type) +{ +	struct drm_crtc *crtc; + +	list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { +		struct exynos_drm_crtc *exynos_crtc; + +		exynos_crtc = to_exynos_crtc(crtc); +		if (exynos_crtc->manager->type == out_type) +			return exynos_crtc->manager->pipe; +	} + +	return -EPERM; +}  | 
