diff mbox series

[v3] drm/bochs: add edid support.

Message ID 20181029134507.7775-1-kraxel@redhat.com (mailing list archive)
State New, archived
Headers show
Series [v3] drm/bochs: add edid support. | expand

Commit Message

Gerd Hoffmann Oct. 29, 2018, 1:45 p.m. UTC
Recent qemu (latest master branch, upcoming 3.1 release) got support
for EDID data.  This patch adds guest driver support.

EDID support in qemu is not (yet) enabled by default, so please use
'qemu -device VGA,edid=on' for testing.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 drivers/gpu/drm/bochs/bochs.h     |  1 +
 drivers/gpu/drm/bochs/bochs_hw.c  | 39 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/bochs/bochs_kms.c | 18 +++++++++++++++---
 3 files changed, 55 insertions(+), 3 deletions(-)

Comments

Jani Nikula Oct. 29, 2018, 5:44 p.m. UTC | #1
On Mon, 29 Oct 2018, Gerd Hoffmann <kraxel@redhat.com> wrote:
> Recent qemu (latest master branch, upcoming 3.1 release) got support
> for EDID data.  This patch adds guest driver support.
>
> EDID support in qemu is not (yet) enabled by default, so please use
> 'qemu -device VGA,edid=on' for testing.

Any chance of making this use drm_get_edid() (requires an i2c_adapter)
or at least drm_do_get_edid()?

BR,
Jani.

>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  drivers/gpu/drm/bochs/bochs.h     |  1 +
>  drivers/gpu/drm/bochs/bochs_hw.c  | 39 +++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/bochs/bochs_kms.c | 18 +++++++++++++++---
>  3 files changed, 55 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h
> index e7a69077e4..06b8166efa 100644
> --- a/drivers/gpu/drm/bochs/bochs.h
> +++ b/drivers/gpu/drm/bochs/bochs.h
> @@ -66,6 +66,7 @@ struct bochs_device {
>  	u16 yres_virtual;
>  	u32 stride;
>  	u32 bpp;
> +	struct edid *edid;
>  
>  	/* drm */
>  	struct drm_device  *dev;
> diff --git a/drivers/gpu/drm/bochs/bochs_hw.c b/drivers/gpu/drm/bochs/bochs_hw.c
> index cacff73a64..db4afe94c5 100644
> --- a/drivers/gpu/drm/bochs/bochs_hw.c
> +++ b/drivers/gpu/drm/bochs/bochs_hw.c
> @@ -69,6 +69,41 @@ static void bochs_hw_set_little_endian(struct bochs_device *bochs)
>  #define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b)
>  #endif
>  
> +static int bochs_load_edid(struct bochs_device *bochs)
> +{
> +	uint8_t *blob;
> +	size_t i, len;
> +	uint8_t num_exts;
> +
> +	if (!bochs->mmio)
> +		return -1;
> +
> +	if ((readb(bochs->mmio+0) != 0x00 ||
> +	     readb(bochs->mmio+1) != 0xff))
> +		return -1;
> +
> +	num_exts = readb(bochs->mmio + 126);
> +	len = EDID_LENGTH * (1 + num_exts);
> +	if (len > 0x400 /* vga register offset */)
> +		return -1;
> +
> +	kfree(bochs->edid);
> +	bochs->edid = kmalloc(len, GFP_KERNEL);
> +	blob = (void *)bochs->edid;
> +	for (i = 0; i < len; i++) {
> +		blob[i] = readb(bochs->mmio+i);
> +	}
> +
> +	if (!drm_edid_is_valid(bochs->edid)) {
> +		DRM_ERROR("EDID is not valid, ignoring.\n");
> +		kfree(bochs->edid);
> +		bochs->edid = NULL;
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
>  int bochs_hw_init(struct drm_device *dev)
>  {
>  	struct bochs_device *bochs = dev->dev_private;
> @@ -150,6 +185,9 @@ int bochs_hw_init(struct drm_device *dev)
>  	}
>  
>  noext:
> +	if (bochs_load_edid(bochs) == 0)
> +		DRM_INFO("Found EDID data blob.\n");
> +
>  	return 0;
>  }
>  
> @@ -164,6 +202,7 @@ void bochs_hw_fini(struct drm_device *dev)
>  	if (bochs->fb_map)
>  		iounmap(bochs->fb_map);
>  	pci_release_regions(dev->pdev);
> +	kfree(bochs->edid);
>  }
>  
>  void bochs_hw_setmode(struct bochs_device *bochs,
> diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c
> index 9bc5b438ae..b9931443a7 100644
> --- a/drivers/gpu/drm/bochs/bochs_kms.c
> +++ b/drivers/gpu/drm/bochs/bochs_kms.c
> @@ -213,10 +213,17 @@ static void bochs_encoder_init(struct drm_device *dev)
>  
>  static int bochs_connector_get_modes(struct drm_connector *connector)
>  {
> -	int count;
> +	struct bochs_device *bochs =
> +		container_of(connector, struct bochs_device, connector);
> +	int count = 0;
>  
> -	count = drm_add_modes_noedid(connector, 8192, 8192);
> -	drm_set_preferred_mode(connector, defx, defy);
> +	if (bochs->edid)
> +		count = drm_add_edid_modes(connector, bochs->edid);
> +
> +	if (!count) {
> +		count = drm_add_modes_noedid(connector, 8192, 8192);
> +		drm_set_preferred_mode(connector, defx, defy);
> +	}
>  	return count;
>  }
>  
> @@ -271,6 +278,11 @@ static void bochs_connector_init(struct drm_device *dev)
>  	drm_connector_helper_add(connector,
>  				 &bochs_connector_connector_helper_funcs);
>  	drm_connector_register(connector);
> +
> +	if (bochs->edid) {
> +		drm_connector_attach_edid_property(connector);
> +		drm_connector_update_edid_property(connector, bochs->edid);
> +	}
>  }
Gerd Hoffmann Oct. 29, 2018, 8:05 p.m. UTC | #2
On Mon, Oct 29, 2018 at 07:44:28PM +0200, Jani Nikula wrote:
> On Mon, 29 Oct 2018, Gerd Hoffmann <kraxel@redhat.com> wrote:
> > Recent qemu (latest master branch, upcoming 3.1 release) got support
> > for EDID data.  This patch adds guest driver support.
> >
> > EDID support in qemu is not (yet) enabled by default, so please use
> > 'qemu -device VGA,edid=on' for testing.
> 
> Any chance of making this use drm_get_edid() (requires an i2c_adapter)
> or at least drm_do_get_edid()?

I'll have a look at using drm_do_get_edid().  drm_get_edid() will not
fly as there is no i2c adapter in the first place.

cheers,
  Gerd
Daniel Vetter Oct. 30, 2018, 9:06 a.m. UTC | #3
On Mon, Oct 29, 2018 at 09:05:20PM +0100, Gerd Hoffmann wrote:
> On Mon, Oct 29, 2018 at 07:44:28PM +0200, Jani Nikula wrote:
> > On Mon, 29 Oct 2018, Gerd Hoffmann <kraxel@redhat.com> wrote:
> > > Recent qemu (latest master branch, upcoming 3.1 release) got support
> > > for EDID data.  This patch adds guest driver support.
> > >
> > > EDID support in qemu is not (yet) enabled by default, so please use
> > > 'qemu -device VGA,edid=on' for testing.
> > 
> > Any chance of making this use drm_get_edid() (requires an i2c_adapter)
> > or at least drm_do_get_edid()?
> 
> I'll have a look at using drm_do_get_edid().  drm_get_edid() will not
> fly as there is no i2c adapter in the first place.

Hm, not sure that makes sense. drm_do_get_edid is to handle the real-world
flakiness of sinks (it's where all the retry logic resides), if you don't
have a i2c_adapater (because the hw has some magic "give me an edid"
block). For virtual hw we hopefully don't randomly drop bits on the floor
between the guest and host. Imo totally fine as-is.

E.g. we also don't feed the VBT edid through drm_do_get_edid either.
-Daniel
Jani Nikula Oct. 30, 2018, 9:28 a.m. UTC | #4
On Tue, 30 Oct 2018, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Mon, Oct 29, 2018 at 09:05:20PM +0100, Gerd Hoffmann wrote:
>> On Mon, Oct 29, 2018 at 07:44:28PM +0200, Jani Nikula wrote:
>> > On Mon, 29 Oct 2018, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> > > Recent qemu (latest master branch, upcoming 3.1 release) got support
>> > > for EDID data.  This patch adds guest driver support.
>> > >
>> > > EDID support in qemu is not (yet) enabled by default, so please use
>> > > 'qemu -device VGA,edid=on' for testing.
>> > 
>> > Any chance of making this use drm_get_edid() (requires an i2c_adapter)
>> > or at least drm_do_get_edid()?
>> 
>> I'll have a look at using drm_do_get_edid().  drm_get_edid() will not
>> fly as there is no i2c adapter in the first place.
>
> Hm, not sure that makes sense. drm_do_get_edid is to handle the real-world
> flakiness of sinks (it's where all the retry logic resides), if you don't
> have a i2c_adapater (because the hw has some magic "give me an edid"
> block). For virtual hw we hopefully don't randomly drop bits on the floor
> between the guest and host. Imo totally fine as-is.
>
> E.g. we also don't feed the VBT edid through drm_do_get_edid either.

But nowadays we do handle the debugfs EDID override and the EDID
firmware loading in drm_do_get_edid(), so using that gives you those
features.

BR,
Jani.
Daniel Vetter Oct. 30, 2018, 9:44 a.m. UTC | #5
On Tue, Oct 30, 2018 at 11:28:24AM +0200, Jani Nikula wrote:
> On Tue, 30 Oct 2018, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Mon, Oct 29, 2018 at 09:05:20PM +0100, Gerd Hoffmann wrote:
> >> On Mon, Oct 29, 2018 at 07:44:28PM +0200, Jani Nikula wrote:
> >> > On Mon, 29 Oct 2018, Gerd Hoffmann <kraxel@redhat.com> wrote:
> >> > > Recent qemu (latest master branch, upcoming 3.1 release) got support
> >> > > for EDID data.  This patch adds guest driver support.
> >> > >
> >> > > EDID support in qemu is not (yet) enabled by default, so please use
> >> > > 'qemu -device VGA,edid=on' for testing.
> >> > 
> >> > Any chance of making this use drm_get_edid() (requires an i2c_adapter)
> >> > or at least drm_do_get_edid()?
> >> 
> >> I'll have a look at using drm_do_get_edid().  drm_get_edid() will not
> >> fly as there is no i2c adapter in the first place.
> >
> > Hm, not sure that makes sense. drm_do_get_edid is to handle the real-world
> > flakiness of sinks (it's where all the retry logic resides), if you don't
> > have a i2c_adapater (because the hw has some magic "give me an edid"
> > block). For virtual hw we hopefully don't randomly drop bits on the floor
> > between the guest and host. Imo totally fine as-is.
> >
> > E.g. we also don't feed the VBT edid through drm_do_get_edid either.
> 
> But nowadays we do handle the debugfs EDID override and the EDID
> firmware loading in drm_do_get_edid(), so using that gives you those
> features.

Oh, missed that. Would be good to clarify the kernel doc and mention this
in e.g. both drm_add_edid_modes() and &drm_connector_helper_funcs.get_modes?

And maybe make it more obvious that drm_do_get_edid is also for virtual
drivers, atm the kerneldoc talks a lot about DDC and everything hw.
-Daniel
diff mbox series

Patch

diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h
index e7a69077e4..06b8166efa 100644
--- a/drivers/gpu/drm/bochs/bochs.h
+++ b/drivers/gpu/drm/bochs/bochs.h
@@ -66,6 +66,7 @@  struct bochs_device {
 	u16 yres_virtual;
 	u32 stride;
 	u32 bpp;
+	struct edid *edid;
 
 	/* drm */
 	struct drm_device  *dev;
diff --git a/drivers/gpu/drm/bochs/bochs_hw.c b/drivers/gpu/drm/bochs/bochs_hw.c
index cacff73a64..db4afe94c5 100644
--- a/drivers/gpu/drm/bochs/bochs_hw.c
+++ b/drivers/gpu/drm/bochs/bochs_hw.c
@@ -69,6 +69,41 @@  static void bochs_hw_set_little_endian(struct bochs_device *bochs)
 #define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b)
 #endif
 
+static int bochs_load_edid(struct bochs_device *bochs)
+{
+	uint8_t *blob;
+	size_t i, len;
+	uint8_t num_exts;
+
+	if (!bochs->mmio)
+		return -1;
+
+	if ((readb(bochs->mmio+0) != 0x00 ||
+	     readb(bochs->mmio+1) != 0xff))
+		return -1;
+
+	num_exts = readb(bochs->mmio + 126);
+	len = EDID_LENGTH * (1 + num_exts);
+	if (len > 0x400 /* vga register offset */)
+		return -1;
+
+	kfree(bochs->edid);
+	bochs->edid = kmalloc(len, GFP_KERNEL);
+	blob = (void *)bochs->edid;
+	for (i = 0; i < len; i++) {
+		blob[i] = readb(bochs->mmio+i);
+	}
+
+	if (!drm_edid_is_valid(bochs->edid)) {
+		DRM_ERROR("EDID is not valid, ignoring.\n");
+		kfree(bochs->edid);
+		bochs->edid = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
 int bochs_hw_init(struct drm_device *dev)
 {
 	struct bochs_device *bochs = dev->dev_private;
@@ -150,6 +185,9 @@  int bochs_hw_init(struct drm_device *dev)
 	}
 
 noext:
+	if (bochs_load_edid(bochs) == 0)
+		DRM_INFO("Found EDID data blob.\n");
+
 	return 0;
 }
 
@@ -164,6 +202,7 @@  void bochs_hw_fini(struct drm_device *dev)
 	if (bochs->fb_map)
 		iounmap(bochs->fb_map);
 	pci_release_regions(dev->pdev);
+	kfree(bochs->edid);
 }
 
 void bochs_hw_setmode(struct bochs_device *bochs,
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c
index 9bc5b438ae..b9931443a7 100644
--- a/drivers/gpu/drm/bochs/bochs_kms.c
+++ b/drivers/gpu/drm/bochs/bochs_kms.c
@@ -213,10 +213,17 @@  static void bochs_encoder_init(struct drm_device *dev)
 
 static int bochs_connector_get_modes(struct drm_connector *connector)
 {
-	int count;
+	struct bochs_device *bochs =
+		container_of(connector, struct bochs_device, connector);
+	int count = 0;
 
-	count = drm_add_modes_noedid(connector, 8192, 8192);
-	drm_set_preferred_mode(connector, defx, defy);
+	if (bochs->edid)
+		count = drm_add_edid_modes(connector, bochs->edid);
+
+	if (!count) {
+		count = drm_add_modes_noedid(connector, 8192, 8192);
+		drm_set_preferred_mode(connector, defx, defy);
+	}
 	return count;
 }
 
@@ -271,6 +278,11 @@  static void bochs_connector_init(struct drm_device *dev)
 	drm_connector_helper_add(connector,
 				 &bochs_connector_connector_helper_funcs);
 	drm_connector_register(connector);
+
+	if (bochs->edid) {
+		drm_connector_attach_edid_property(connector);
+		drm_connector_update_edid_property(connector, bochs->edid);
+	}
 }