diff options
| -rw-r--r-- | linux-core/drm_crtc.c | 1 | ||||
| -rw-r--r-- | linux-core/drm_crtc.h | 6 | ||||
| -rw-r--r-- | linux-core/intel_fb.c | 234 | 
3 files changed, 149 insertions, 92 deletions
| diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 4c6afd16..92a7d513 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -561,6 +561,7 @@ void drm_mode_config_init(struct drm_device *dev)  {  	mutex_init(&dev->mode_config.mutex);  	INIT_LIST_HEAD(&dev->mode_config.fb_list); +	INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list);  	INIT_LIST_HEAD(&dev->mode_config.crtc_list);  	INIT_LIST_HEAD(&dev->mode_config.connector_list);  	INIT_LIST_HEAD(&dev->mode_config.encoder_list); diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index c1d89ee8..043b4a57 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -498,8 +498,7 @@ struct drm_connector {   *   * This is used to set modes.   */ -struct drm_mode_set -{ +struct drm_mode_set {  	struct list_head head;  	struct drm_framebuffer *fb; @@ -557,6 +556,9 @@ struct drm_mode_config {  	struct list_head property_list; +	/* in-kernel framebuffers - hung of filp_head in drm_framebuffer */ +	struct list_head fb_kernel_list; +  	/* currently in use generation id */  	int current_generation; diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c index 9b2e9314..2355bf3d 100644 --- a/linux-core/intel_fb.c +++ b/linux-core/intel_fb.c @@ -585,6 +585,8 @@ int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height  		return -EINVAL;  	} +	list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); +  	intel_fb = to_intel_framebuffer(fb);  	*intel_fb_p = intel_fb; @@ -721,129 +723,181 @@ int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height  	return 0;  } -int intelfb_probe(struct drm_device *dev) +#define INTELFB_CONN_LIMIT 4 + +static int intelfb_create_crtcmodesets(struct intel_framebuffer *intel_fb, int num_sets, int crtc_base)  { -	struct fb_info *info; +	int i,j; +	struct drm_device *dev = intel_fb->base.dev;  	struct intelfb_par *par; -	struct device *device = &dev->pdev->dev;  -	struct intel_framebuffer *intel_fb;  	struct drm_mode_set *modeset;  	struct drm_crtc *crtc; -	struct drm_connector *connector; -	int ret; -	int crtc_count = 0, i; -	unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; -	unsigned int surface_width = 0, surface_height = 0; - -	DRM_DEBUG("\n"); - -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -		if (crtc->desired_mode) { -			if (crtc->desired_mode->hdisplay < fb_width) -				fb_width = crtc->desired_mode->hdisplay; +	struct fb_info *info; -			if (crtc->desired_mode->vdisplay < fb_height) -				fb_height = crtc->desired_mode->vdisplay; +	info = intel_fb->base.fbdev; +	par = info->par; -			if (crtc->desired_mode->hdisplay > surface_width) -				surface_width = crtc->desired_mode->hdisplay; +	for (i = 0; i < num_sets; i++) { +		modeset = kzalloc(sizeof(struct drm_mode_set) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); +		if (!modeset) +			goto fail; -			if (crtc->desired_mode->vdisplay > surface_height) -				surface_height = crtc->desired_mode->vdisplay; +		/* attach a CRTC to the modeset */ +		j = 0; +		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +			if (j == i + crtc_base) { +				modeset->crtc = crtc; +				break; +			} +			j++;  		} -		crtc_count++; +		modeset->fb = &intel_fb->base; + +		modeset->connectors = (struct drm_connector **)(modeset + 1); +		list_add_tail(&modeset->head, &par->mode_set_list);  	} -	if (fb_width == -1 || fb_height == -1) { -		return -EINVAL; +fail: +	list_for_each_entry(modeset, &par->mode_set_list, head) { +		list_del(&modeset->head); +		kfree(modeset);  	} -	DRM_DEBUG("here %d %d %d %d\n", fb_width, fb_height, surface_width, surface_height); -	ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb); -	if (ret) -		return -EINVAL; +	return -ENOMEM; +} -	DRM_DEBUG("here %p\n", intel_fb); -	info = intel_fb->base.fbdev; -	par = info->par; +static int intelfb_single_fb_probe(struct drm_device *dev) +{ +	struct drm_crtc *crtc; +	struct drm_connector *connector; +	unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; +	unsigned int surface_width = 0, surface_height = 0; +	int new_fb = 0; +	int crtc_count = 0; +	int ret, i, conn_count = 0; +	struct intel_framebuffer *intel_fb; +	struct fb_info *info; +	struct intelfb_par *par; +	struct drm_mode_set *modeset; -	DRM_DEBUG("crtc count is %d\n", crtc_count); -	/* make up a couple of config sets */ -	for (i = 0; i < crtc_count; i++) { -		int conn_count = 0; +	/* first up get a count of crtcs now in use and new min/maxes width/heights */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (drm_helper_crtc_in_use(crtc)) { +			if (crtc->desired_mode) { +				if (crtc->desired_mode->hdisplay < fb_width) +					fb_width = crtc->desired_mode->hdisplay; +				 +				if (crtc->desired_mode->vdisplay < fb_height) +					fb_height = crtc->desired_mode->vdisplay; +				 +				if (crtc->desired_mode->hdisplay > surface_width) +					surface_width = crtc->desired_mode->hdisplay; +				 +				if (crtc->desired_mode->vdisplay > surface_height) +				surface_height = crtc->desired_mode->vdisplay; +			} +		crtc_count++; +		} +	} -		modeset = kzalloc(sizeof(struct drm_mode_set), GFP_KERNEL); -		modeset->fb = &intel_fb->base; +	if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { +		/* hmm everyone went away - assume VGA cable just fell out +		   and will come back later. */ +		return 0; +	} -		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -			struct intel_crtc *intel_crtc = to_intel_crtc(crtc); -			if (intel_crtc->pipe == i) -				modeset->crtc = crtc; +	/* do we have an fb already? */ +	if (list_empty(&dev->mode_config.fb_kernel_list)) { +		/* create an fb if we don't have one */ +		ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb); +		if (ret) +			return -EINVAL; + +		/* create a set per crtc connected to this fb */ +		ret = intelfb_create_crtcmodesets(intel_fb, dev->mode_config.num_crtc, 0); +		if (ret) +			return ret; +		new_fb = 1; +	} else { +		struct drm_framebuffer *fb; +		fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); +		intel_fb = to_intel_framebuffer(fb); + +		/* check if we are going to have a size issue */ +		/* do a surface compare */ +		if ((fb->width < surface_width) || (fb->height < surface_height)) { +			DRM_ERROR("NEED BIGGER SURFACE DUDE\n"); +			return -EINVAL;  		} +	} +	info = intel_fb->base.fbdev; +	par = info->par; -		DRM_DEBUG("1\n"); -		/* find out how many connectors are on this */ +	/* okay we need to setup new connector sets in the crtcs */ +	list_for_each_entry(modeset, &par->mode_set_list, head) { +		 +		conn_count = 0;  		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {  			if (connector->encoder) -				if (connector->encoder->crtc == modeset->crtc) +				if(connector->encoder->crtc == modeset->crtc) { +					modeset->connectors[conn_count] = connector;  					conn_count++; +					if (conn_count > INTELFB_CONN_LIMIT) +						BUG(); +				}  		} -		list_add_tail(&modeset->head, &par->mode_set_list); - -		if (!conn_count) -			continue; -		 -		DRM_DEBUG("cc %d\n", conn_count); -		modeset->connectors = kcalloc(conn_count, sizeof(struct drm_connector *), GFP_KERNEL); -		if (!modeset->connectors) { -			ret = -ENOMEM; -			goto fail; -		} +		for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) +			modeset->connectors[i] = NULL;  		modeset->num_connectors = conn_count; -		 -		conn_count = 0; -		list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -			if (connector->encoder) -				if (connector->encoder->crtc == modeset->crtc) -					modeset->connectors[conn_count++] = connector; -		} -		modeset->mode = modeset->crtc->desired_mode; +		if (modeset->mode != modeset->crtc->desired_mode) +			modeset->mode = modeset->crtc->desired_mode;  	} -        -	info->var.pixclock = -1; -#if 0 -	info->var.right_margin = mode->hsync_start - mode->hdisplay; -	info->var.hsync_len = mode->hsync_end - mode->hsync_start; -	info->var.left_margin = mode->htotal - mode->hsync_end; -	info->var.lower_margin = mode->vsync_start - mode->vdisplay; -	info->var.vsync_len = mode->vsync_end - mode->vsync_start; -	info->var.upper_margin = mode->vtotal - mode->vsync_end; -	info->var.pixclock = KHZ2PICOS(mode->clock); -	if (mode->flags & V_PHSYNC) -		info->var.sync |= FB_SYNC_HOR_HIGH_ACT; +	if (new_fb) { +		info->var.pixclock = -1; +		if (register_framebuffer(info) < 0) +			return -EINVAL; +	} +		 +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, +	       info->fix.id); +	return 0; +} + +int intelfb_probe(struct drm_device *dev) +{ +	int ret; -	if (mode->flags & V_PVSYNC) -		info->var.sync |= FB_SYNC_VERT_HIGH_ACT; +	DRM_DEBUG("\n"); -	if (mode->flags & V_INTERLACE) -		info->var.vmode = FB_VMODE_INTERLACED; -	else if (mode->flags & V_DBLSCAN) -		info->var.vmode = FB_VMODE_DOUBLE; -	else -		info->var.vmode = FB_VMODE_NONINTERLACED; +	/* something has changed in the lower levels of hell - deal with it  +	   here */ + +	/* two modes : a) 1 fb to rule all crtcs. +	               b) one fb per crtc. +	   two actions 1) new connected device + 	               2) device removed. +	   case a/1 : if the fb surface isn't big enough - resize the surface fb. +	              if the fb size isn't big enough - resize fb into surface. +		      if everything big enough configure the new crtc/etc. +	   case a/2 : undo the configuration +	              possibly resize down the fb to fit the new configuration. +           case b/1 : see if it is on a new crtc - setup a new fb and add it. +	   case b/2 : teardown the new fb. +	*/ -#endif +	/* mode a first */ +	/* search for an fb */ +	if (i915_fbpercrtc == 1) { +		ret = -EINVAL; +		goto fail; +	} -	if (register_framebuffer(info) < 0) -		return -EINVAL; +	ret = intelfb_single_fb_probe(dev); -	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, -		info->fix.id); -	return 0;  fail:  	/*  TODO */  	return ret; | 
