mbox series

[0/2] drm: Add driverof PowerPC OF displays

Message ID 20220518183006.14548-1-tzimmermann@suse.de
Headers show
Series drm: Add driverof PowerPC OF displays | expand

Message

Thomas Zimmermann May 18, 2022, 6:30 p.m. UTC
PowerPC's Open Firmware offers a simple display buffer for graphics
output. Add ofdrm, a DRM driver for the device. As with the existing
simpledrm driver, the graphics hardware is pre-initialized by the
firmware. The driver only provides blitting, no actual DRM modesetting
is possible.

Thomas Zimmermann (2):
  MAINTAINERS: Broaden scope of simpledrm entry
  drm/tiny: Add ofdrm for Open Firmware framebuffers

 MAINTAINERS                   |   5 +-
 drivers/gpu/drm/tiny/Kconfig  |  12 +
 drivers/gpu/drm/tiny/Makefile |   1 +
 drivers/gpu/drm/tiny/ofdrm.c  | 748 ++++++++++++++++++++++++++++++++++
 drivers/video/fbdev/Kconfig   |   1 +
 5 files changed, 766 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/tiny/ofdrm.c

Comments

Michal Suchanek May 19, 2022, 6:34 a.m. UTC | #1
On Wed, May 18, 2022 at 10:11:03PM +0100, Mark Cave-Ayland wrote:
> On 18/05/2022 19:30, Thomas Zimmermann wrote:
> 
> > Open Firmware provides basic display output via the 'display' node.
> > DT platform code already provides a device that represents the node's
> > framebuffer. Add a DRM driver for the device. The display mode and
> > color format is pre-initialized by the system's firmware. Runtime
> > modesetting via DRM is not possible. The display is useful during
> > early boot stages or as error fallback.
> > 
> > Similar functionality is already provided by fbdev's offb driver,
> > which is insufficient for modern userspace. The old driver includes
> > support for BootX device tree, which can be found on old 32-bit
> > PowerPC Macintosh systems. If these are still in use, the
> > functionality can be added to ofdrm or implemented in a new
> > driver. As with simepldrm, the fbdev driver cannot be selected is
> > ofdrm is already enabled.
> > 
> > Two noteable points about the driver:
> > 
> >   * Reading the framebuffer aperture from the device tree is not
> > reliable on all systems. Ofdrm takes the heuristics and a comment
> > from offb to pick the correct range.
> > 
> >   * No resource management may be tied to the underlying PCI device.
> > Otherwise the handover to the native driver will fail with a resource
> > conflict. PCI management is therefore done as part of the platform
> > device's cleanup.
> > 
> > The driver has been tested on qemu's ppc64le emulation. The device
> > hand-over has been tested with bochs.
> 
> Thanks for working on this! Have you tried it on qemu-system-sparc and
> qemu-system-sparc64 at all? At least under QEMU I'd expect it to work for
> these platforms too, unless there is a particular dependency on PCI. A

There is an implicit dependency on PCI, and it won't work because it
depends on PPC:

depends on DRM && MMU && PPC

this is what the offb has, too.

I am wondering what is the driver for OF based framebuffer on sparc and
arm but offb clearly isn't with its dependency on PPC.

Thanks

Michal

> couple of comments inline below:
> 
> > Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> > ---
> >   MAINTAINERS                   |   1 +
> >   drivers/gpu/drm/tiny/Kconfig  |  12 +
> >   drivers/gpu/drm/tiny/Makefile |   1 +
> >   drivers/gpu/drm/tiny/ofdrm.c  | 748 ++++++++++++++++++++++++++++++++++
> >   drivers/video/fbdev/Kconfig   |   1 +
> >   5 files changed, 763 insertions(+)
> >   create mode 100644 drivers/gpu/drm/tiny/ofdrm.c
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 43d833273ae9..090cbe1aa5e3 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6395,6 +6395,7 @@ L:	dri-devel@lists.freedesktop.org
> >   S:	Maintained
> >   T:	git git://anongit.freedesktop.org/drm/drm-misc
> >   F:	drivers/gpu/drm/drm_aperture.c
> > +F:	drivers/gpu/drm/tiny/ofdrm.c
> >   F:	drivers/gpu/drm/tiny/simpledrm.c
> >   F:	include/drm/drm_aperture.h
> > diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig
> > index 627d637a1e7e..0bc54af42e7f 100644
> > --- a/drivers/gpu/drm/tiny/Kconfig
> > +++ b/drivers/gpu/drm/tiny/Kconfig
> > @@ -51,6 +51,18 @@ config DRM_GM12U320
> >   	 This is a KMS driver for projectors which use the GM12U320 chipset
> >   	 for video transfer over USB2/3, such as the Acer C120 mini projector.
> > +config DRM_OFDRM
> > +	tristate "Open Firmware display driver"
> > +	depends on DRM && MMU && PPC
> > +	select DRM_GEM_SHMEM_HELPER
> > +	select DRM_KMS_HELPER
> > +	help
> > +	  DRM driver for Open Firmware framebuffers.
> > +
> > +	  This driver assumes that the display hardware has been initialized
> > +	  by the Open Firmware before the kernel boots. Scanout buffer, size,
> > +	  and display format must be provided via device tree.
> > +
> >   config DRM_PANEL_MIPI_DBI
> >   	tristate "DRM support for MIPI DBI compatible panels"
> >   	depends on DRM && SPI
> > diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile
> > index 1d9d6227e7ab..76dde89a044b 100644
> > --- a/drivers/gpu/drm/tiny/Makefile
> > +++ b/drivers/gpu/drm/tiny/Makefile
> > @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_ARCPGU)		+= arcpgu.o
> >   obj-$(CONFIG_DRM_BOCHS)			+= bochs.o
> >   obj-$(CONFIG_DRM_CIRRUS_QEMU)		+= cirrus.o
> >   obj-$(CONFIG_DRM_GM12U320)		+= gm12u320.o
> > +obj-$(CONFIG_DRM_OFDRM)			+= ofdrm.o
> >   obj-$(CONFIG_DRM_PANEL_MIPI_DBI)	+= panel-mipi-dbi.o
> >   obj-$(CONFIG_DRM_SIMPLEDRM)		+= simpledrm.o
> >   obj-$(CONFIG_TINYDRM_HX8357D)		+= hx8357d.o
> > diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c
> > new file mode 100644
> > index 000000000000..aca715b36179
> > --- /dev/null
> > +++ b/drivers/gpu/drm/tiny/ofdrm.c
> > @@ -0,0 +1,748 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include <drm/drm_aperture.h>
> > +#include <drm/drm_atomic_state_helper.h>
> > +#include <drm/drm_connector.h>
> > +#include <drm/drm_damage_helper.h>
> > +#include <drm/drm_device.h>
> > +#include <drm/drm_drv.h>
> > +#include <drm/drm_fb_helper.h>
> > +#include <drm/drm_format_helper.h>
> > +#include <drm/drm_gem_atomic_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_gem_shmem_helper.h>
> > +#include <drm/drm_managed.h>
> > +#include <drm/drm_modeset_helper_vtables.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/drm_simple_kms_helper.h>
> > +
> > +
> > +#define DRIVER_NAME	"ofdrm"
> > +#define DRIVER_DESC	"DRM driver for OF platform devices"
> > +#define DRIVER_DATE	"20220501"
> > +#define DRIVER_MAJOR	1
> > +#define DRIVER_MINOR	0
> > +
> > +/*
> > + * Assume a monitor resolution of 96 dpi to
> > + * get a somewhat reasonable screen size.
> > + */
> > +#define RES_MM(d)	\
> > +	(((d) * 254ul) / (96ul * 10ul))
> > +
> > +#define OFDRM_MODE(hd, vd)	\
> > +	DRM_SIMPLE_MODE(hd, vd, RES_MM(hd), RES_MM(vd))
> > +
> > +/*
> > + * Helpers for display nodes
> > + */
> > +
> > +static int display_get_validated_int(struct drm_device *dev, const char *name, uint32_t value)
> > +{
> > +	if (value > INT_MAX) {
> > +		drm_err(dev, "invalid framebuffer %s of %u\n", name, value);
> > +		return -EINVAL;
> > +	}
> > +	return (int)value;
> > +}
> > +
> > +static int display_get_validated_int0(struct drm_device *dev, const char *name, uint32_t value)
> > +{
> > +	if (!value) {
> > +		drm_err(dev, "invalid framebuffer %s of %u\n", name, value);
> > +		return -EINVAL;
> > +	}
> > +	return display_get_validated_int(dev, name, value);
> > +}
> > +
> > +static const struct drm_format_info *display_get_validated_format(struct drm_device *dev,
> > +								  u32 depth)
> > +{
> > +	const struct drm_format_info *info;
> > +	u32 format;
> > +
> > +	switch (depth) {
> > +	case 8:
> > +		format = drm_mode_legacy_fb_format(8, 8);
> > +		break;
> > +	case 15:
> > +	case 16:
> > +		format = drm_mode_legacy_fb_format(16, depth);
> > +		break;
> > +	case 32:
> > +		format = drm_mode_legacy_fb_format(32, 24);
> > +		break;
> > +	default:
> > +		drm_err(dev, "unsupported framebuffer depth %u\n", depth);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	info = drm_format_info(format);
> > +	if (!info) {
> > +		drm_err(dev, "cannot find framebuffer format for depth %u\n", depth);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	return info;
> > +}
> > +
> > +static int display_read_u32_of(struct drm_device *dev, struct device_node *of_node,
> > +			       const char *name, u32 *value)
> > +{
> > +	int ret = of_property_read_u32(of_node, name, value);
> > +
> > +	if (ret)
> > +		drm_err(dev, "cannot parse framebuffer %s: error %d\n", name, ret);
> > +	return ret;
> > +}
> > +
> > +static int display_get_width_of(struct drm_device *dev, struct device_node *of_node)
> > +{
> > +	u32 width;
> > +	int ret = display_read_u32_of(dev, of_node, "width", &width);
> > +
> > +	if (ret)
> > +		return ret;
> > +	return display_get_validated_int0(dev, "width", width);
> > +}
> > +
> > +static int display_get_height_of(struct drm_device *dev, struct device_node *of_node)
> > +{
> > +	u32 height;
> > +	int ret = display_read_u32_of(dev, of_node, "height", &height);
> > +
> > +	if (ret)
> > +		return ret;
> > +	return display_get_validated_int0(dev, "height", height);
> > +}
> > +
> > +static int display_get_linebytes_of(struct drm_device *dev, struct device_node *of_node)
> > +{
> > +	u32 linebytes;
> > +	int ret = display_read_u32_of(dev, of_node, "linebytes", &linebytes);
> > +
> > +	if (ret)
> > +		return ret;
> > +	return display_get_validated_int(dev, "linebytes", linebytes);
> > +}
> > +
> > +static const struct drm_format_info *display_get_format_of(struct drm_device *dev,
> > +							   struct device_node *of_node)
> > +{
> > +	u32 depth;
> > +	int ret = display_read_u32_of(dev, of_node, "depth", &depth);
> > +
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +	return display_get_validated_format(dev, depth);
> > +}
> > +
> > +static u64 display_get_address_of(struct drm_device *dev, struct device_node *of_node)
> > +{
> > +	u32 address;
> > +	int ret;
> > +
> > +	/*
> > +	 * Not all devices provide an address property, it's not
> > +	 * a bug if this fails. The driver will try to find the
> > +	 * framebuffer base address from the device's memory regions.
> > +	 */
> > +	ret = of_property_read_u32(of_node, "address", &address);
> > +	if (ret)
> > +		return OF_BAD_ADDR;
> > +
> > +	return address;
> > +}
> > +
> > +#if defined(CONFIG_PCI)
> > +static struct pci_dev *display_get_pci_dev_of(struct drm_device *dev, struct device_node *of_node)
> > +{
> > +	const __be32 *vendor_p, *device_p;
> > +	u32 vendor, device;
> > +	struct pci_dev *pcidev;
> > +
> > +	vendor_p = of_get_property(of_node, "vendor-id", NULL);
> > +	if (!vendor_p)
> > +		return ERR_PTR(-ENODEV);
> > +	vendor = be32_to_cpup(vendor_p);
> > +
> > +	device_p = of_get_property(of_node, "device-id", NULL);
> > +	if (!device_p)
> > +		return ERR_PTR(-ENODEV);
> > +	device = be32_to_cpup(device_p);
> > +
> > +	pcidev = pci_get_device(vendor, device, NULL);
> > +	if (!pcidev)
> > +		return ERR_PTR(-ENODEV);
> > +
> > +	return pcidev;
> > +}
> > +#else
> > +static struct pci_dev *display_get_pci_dev_of(struct drm_device *dev, struct device_node *of_node)
> > +{
> > +	return ERR_PTR(-ENODEV);
> > +}
> > +#endif
> > +
> > +/*
> > + * Open Firmware display device
> > + */
> > +
> > +struct ofdrm_device {
> > +	struct drm_device dev;
> > +	struct platform_device *pdev;
> > +
> > +	/* OF display settings */
> > +	struct drm_display_mode mode;
> > +	const struct drm_format_info *format;
> > +	unsigned int pitch;
> > +	resource_size_t fb_base;
> > +	resource_size_t fb_size;
> > +
> > +	/* memory management */
> > +	struct resource *mem;
> > +	void __iomem *screen_base;
> > +
> > +	/* modesetting */
> > +	uint32_t formats[8];
> > +	size_t nformats;
> > +	struct drm_connector connector;
> > +	struct drm_simple_display_pipe pipe;
> > +};
> > +
> > +static struct ofdrm_device *ofdrm_device_of_dev(struct drm_device *dev)
> > +{
> > +	return container_of(dev, struct ofdrm_device, dev);
> > +}
> > +
> > +/*
> > + *  OF display settings
> > + */
> > +
> > +static void ofdrm_pci_release(void *data)
> > +{
> > +	struct pci_dev *pcidev = data;
> > +
> > +	pci_disable_device(pcidev);
> > +}
> > +
> > +static struct drm_display_mode ofdrm_mode(unsigned int width, unsigned int height)
> > +{
> > +	struct drm_display_mode mode = { OFDRM_MODE(width, height) };
> > +
> > +	mode.clock = mode.hdisplay * mode.vdisplay * 60 / 1000 /* kHz */;
> > +	drm_mode_set_name(&mode);
> > +
> > +	return mode;
> > +}
> > +
> > +static struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev,
> > +					       struct resource *fb_res)
> > +{
> > +	struct platform_device *pdev = to_platform_device(odev->dev.dev);
> > +	struct resource *res, *max_res = NULL;
> > +	u32 i;
> > +
> > +	for (i = 0; pdev->num_resources; ++i) {
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> > +		if (!res)
> > +			break; /* all resources processed */
> > +		if (resource_size(res) < resource_size(fb_res))
> > +			continue; /* resource too small */
> > +		if (fb_res->start && resource_contains(res, fb_res))
> > +			return res; /* resource contains framebuffer */
> > +		if (!max_res || resource_size(res) > resource_size(max_res))
> > +			max_res = res; /* store largest resource as fallback */
> > +	}
> > +
> > +	return max_res;
> > +}
> > +
> > +static int ofdrm_device_init_fb(struct ofdrm_device *odev)
> > +{
> > +	struct drm_device *dev = &odev->dev;
> > +	struct platform_device *pdev = odev->pdev;
> > +	struct device_node *of_node = pdev->dev.of_node;
> > +	int width, height, linebytes;
> > +	u64 address;
> > +	const struct drm_format_info *format;
> > +	struct pci_dev *pcidev;
> > +	struct resource *res;
> > +	int ret;
> > +
> > +	width = display_get_width_of(dev, of_node);
> > +	if (width < 0)
> > +		return width;
> > +	height = display_get_height_of(dev, of_node);
> > +	if (height < 0)
> > +		return height;
> > +	linebytes = display_get_linebytes_of(dev, of_node);
> > +	if (linebytes < 0)
> > +		return linebytes;
> > +	format = display_get_format_of(dev, of_node);
> > +	if (IS_ERR(format))
> > +		return PTR_ERR(format);
> > +	address = display_get_address_of(dev, of_node);
> > +
> > +	/*
> > +	 * Never use pcim_ or other managed helpers on the returned PCI
> > +	 * device. Otherwise, probing the native driver will fail for
> > +	 * resource conflicts. PCI-device management has to be tied to
> > +	 * the lifetime of the platform device until the native driver
> > +	 * takes over.
> > +	 */
> > +	pcidev = display_get_pci_dev_of(dev, of_node);
> > +	if (!IS_ERR(pcidev)) {
> > +		ret = pci_enable_device(pcidev);
> > +		if (drm_WARN(dev, ret, "pci_enable_device(%s) failed: %d\n",
> > +			     dev_name(&pcidev->dev), ret))
> > +			return ret;
> > +		ret = devm_add_action_or_reset(&pdev->dev, ofdrm_pci_release, pcidev);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	odev->mode = ofdrm_mode(width, height);
> > +	odev->format = format;
> > +
> > +	if (linebytes)
> > +		odev->pitch = linebytes;
> > +	else
> > +		odev->pitch = width * format->cpp[0];
> > +
> > +	odev->fb_size = odev->pitch * height;
> > +
> > +	/*
> > +	 * Try to figure out the address of the framebuffer. Unfortunately, Open
> > +	 * Firmware doesn't provide a standard way to do so. All we can do is a
> > +	 * dodgy heuristic that happens to work in practice.
> 
> This isn't quite true: there is a Forth value named "frame-buffer-adr" which
> should be set to the physical screen base address for PCI and non-PCI cards.
> It is mentioned in both the IEEE-1275 specification (page 32) and according
> to OpenBIOS is also referenced by BootX:
> https://mail.coreboot.org/pipermail/openbios/2011-August/006617.html.
> 
> > +	 * On most machines, the "address" property contains what we need, though
> > +	 * not on Matrox cards found in IBM machines. What appears to give good
> > +	 * results is to go through the PCI ranges and pick one that encloses the
> > +	 * "address" property. If none match, we pick the largest.
> > +	 */
> 
> In my experience so far "frame-buffer-adr" is the definitive way to get the
> screen physical address, however you'll need to call the CIF interface in
> order to get the value. The cases where I've seen an "address" property in
> the display node is when the display has also been mapped to a virtual
> address, which may or may not be the same as the physical address. I suspect
> this optional virtual mapping may not be present by the time the ofdrm
> driver loads?
> 
> > +	if (address != OF_BAD_ADDR) {
> > +		struct resource fb_res = DEFINE_RES_MEM(address, odev->fb_size);
> > +
> > +		res = ofdrm_find_fb_resource(odev, &fb_res);
> > +		if (!res)
> > +			return -EINVAL;
> > +		if (resource_contains(res, &fb_res))
> > +			odev->fb_base = address;
> > +		else
> > +			odev->fb_base = res->start;
> > +	} else {
> > +		struct resource fb_res = DEFINE_RES_MEM(0u, odev->fb_size);
> > +
> > +		res = ofdrm_find_fb_resource(odev, &fb_res);
> > +		if (!res)
> > +			return -EINVAL;
> > +		odev->fb_base = res->start;
> > +	}
> > +
> > +	drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&odev->mode));
> > +	drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, linebytes=%d byte, address=0x%llx\n",
> > +		&format->format, width, height, linebytes, address);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * Memory management
> > + */
> > +
> > +static int ofdrm_device_init_mm(struct ofdrm_device *odev)
> > +{
> > +	struct drm_device *dev = &odev->dev;
> > +	struct platform_device *pdev = odev->pdev;
> > +	struct resource res = DEFINE_RES_MEM(odev->fb_base, round_up(odev->fb_size, PAGE_SIZE));
> > +	struct resource *mem;
> > +	void __iomem *screen_base;
> > +	int ret;
> > +
> > +	ret = devm_aperture_acquire_from_firmware(dev, res.start, resource_size(&res));
> > +	if (ret) {
> > +		drm_err(dev, "could not acquire memory range %pr: error %d\n", &res, ret);
> > +		return ret;
> > +	}
> > +
> > +	mem = devm_request_mem_region(&pdev->dev, res.start, resource_size(&res),
> > +				      odev->dev.driver->name);
> > +	if (!mem) {
> > +		drm_warn(dev, "could not acquire memory region %pr\n", &res);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	drm_dbg(dev, "mapping framebuffer: %pr\n", mem);
> > +
> > +	screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
> > +	if (drm_WARN_ON(dev, !screen_base))
> > +		return -ENOMEM;
> > +
> > +	odev->mem = mem;
> > +	odev->screen_base = screen_base;
> > +
> > +	drm_dbg(dev, "framebuffer at %pr, mapped at 0x%p\n", mem, screen_base);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * Modesetting
> > + */
> > +
> > +/*
> > + * Support all formats of OF display and maybe more; in order
> > + * of preference. The display's update function will do any
> > + * conversion necessary.
> > + *
> > + * TODO: Add blit helpers for remaining formats and uncomment
> > + *       constants.
> > + */
> > +static const uint32_t ofdrm_default_formats[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_RGB565,
> > +	//DRM_FORMAT_XRGB1555,
> > +	DRM_FORMAT_C8,
> > +};
> > +
> > +static const uint64_t ofdrm_format_modifiers[] = {
> > +	DRM_FORMAT_MOD_LINEAR,
> > +	DRM_FORMAT_MOD_INVALID
> > +};
> > +
> > +static int ofdrm_connector_helper_get_modes(struct drm_connector *connector)
> > +{
> > +	struct ofdrm_device *odev = ofdrm_device_of_dev(connector->dev);
> > +	struct drm_display_mode *mode;
> > +
> > +	mode = drm_mode_duplicate(connector->dev, &odev->mode);
> > +	if (!mode)
> > +		return 0;
> > +
> > +	if (mode->name[0] == '\0')
> > +		drm_mode_set_name(mode);
> > +
> > +	mode->type |= DRM_MODE_TYPE_PREFERRED;
> > +	drm_mode_probed_add(connector, mode);
> > +
> > +	if (mode->width_mm)
> > +		connector->display_info.width_mm = mode->width_mm;
> > +	if (mode->height_mm)
> > +		connector->display_info.height_mm = mode->height_mm;
> > +
> > +	return 1;
> > +}
> > +
> > +static const struct drm_connector_helper_funcs ofdrm_connector_helper_funcs = {
> > +	.get_modes = ofdrm_connector_helper_get_modes,
> > +};
> > +
> > +static const struct drm_connector_funcs ofdrm_connector_funcs = {
> > +	.reset = drm_atomic_helper_connector_reset,
> > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > +	.destroy = drm_connector_cleanup,
> > +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> > +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> > +};
> > +
> > +static int ofdrm_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
> > +						const struct drm_display_mode *mode)
> > +{
> > +	struct ofdrm_device *odev = ofdrm_device_of_dev(pipe->crtc.dev);
> > +
> > +	if (mode->hdisplay != odev->mode.hdisplay &&
> > +	    mode->vdisplay != odev->mode.vdisplay)
> > +		return MODE_ONE_SIZE;
> > +	else if (mode->hdisplay != odev->mode.hdisplay)
> > +		return MODE_ONE_WIDTH;
> > +	else if (mode->vdisplay != odev->mode.vdisplay)
> > +		return MODE_ONE_HEIGHT;
> > +
> > +	return MODE_OK;
> > +}
> > +
> > +static void ofdrm_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
> > +					     struct drm_crtc_state *crtc_state,
> > +					     struct drm_plane_state *plane_state)
> > +{
> > +	struct ofdrm_device *odev = ofdrm_device_of_dev(pipe->crtc.dev);
> > +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
> > +	struct drm_framebuffer *fb = plane_state->fb;
> > +	void *vmap = shadow_plane_state->data[0].vaddr; /* TODO: Use mapping abstraction */
> > +	struct drm_device *dev = &odev->dev;
> > +	void __iomem *dst = odev->screen_base;
> > +	struct drm_rect src_clip, dst_clip;
> > +	int idx;
> > +
> > +	if (!fb)
> > +		return;
> > +
> > +	drm_rect_fp_to_int(&src_clip, &plane_state->src);
> > +
> > +	dst_clip = plane_state->dst;
> > +	if (!drm_rect_intersect(&dst_clip, &src_clip))
> > +		return;
> > +
> > +	if (!drm_dev_enter(dev, &idx))
> > +		return;
> > +
> > +	dst += drm_fb_clip_offset(odev->pitch, odev->format, &dst_clip);
> > +	drm_fb_blit_toio(dst, odev->pitch, odev->format->format, vmap, fb, &src_clip);
> > +
> > +	drm_dev_exit(idx);
> > +}
> > +
> > +static void ofdrm_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe)
> > +{
> > +	struct ofdrm_device *odev = ofdrm_device_of_dev(pipe->crtc.dev);
> > +	struct drm_device *dev = &odev->dev;
> > +	int idx;
> > +
> > +	if (!drm_dev_enter(dev, &idx))
> > +		return;
> > +
> > +	/* Clear screen to black if disabled */
> > +	memset_io(odev->screen_base, 0, odev->pitch * odev->mode.vdisplay);
> > +
> > +	drm_dev_exit(idx);
> > +}
> > +
> > +static void ofdrm_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
> > +					     struct drm_plane_state *old_plane_state)
> > +{
> > +	struct ofdrm_device *odev = ofdrm_device_of_dev(pipe->crtc.dev);
> > +	struct drm_plane_state *plane_state = pipe->plane.state;
> > +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
> > +	void *vmap = shadow_plane_state->data[0].vaddr; /* TODO: Use mapping abstraction */
> > +	struct drm_framebuffer *fb = plane_state->fb;
> > +	struct drm_device *dev = &odev->dev;
> > +	struct drm_atomic_helper_damage_iter damage_iter;
> > +	struct drm_rect src_clip, dst_clip;
> > +	void __iomem *dst;
> > +	int idx;
> > +
> > +	if (!fb)
> > +		return;
> > +
> > +	if (!drm_dev_enter(dev, &idx))
> > +		return;
> > +
> > +	drm_atomic_helper_damage_iter_init(&damage_iter, old_plane_state, plane_state);
> > +	drm_atomic_for_each_plane_damage(&damage_iter, &src_clip) {
> > +
> > +		dst_clip = plane_state->dst;
> > +		if (!drm_rect_intersect(&dst_clip, &src_clip))
> > +			continue;
> > +
> > +		dst = odev->screen_base;
> > +		dst += drm_fb_clip_offset(odev->pitch, odev->format, &dst_clip);
> > +		drm_fb_blit_toio(dst, odev->pitch, odev->format->format, vmap, fb, &src_clip);
> > +	}
> > +
> > +	drm_dev_exit(idx);
> > +}
> > +
> > +static const struct drm_simple_display_pipe_funcs ofdrm_simple_display_pipe_funcs = {
> > +	.mode_valid = ofdrm_simple_display_pipe_mode_valid,
> > +	.enable = ofdrm_simple_display_pipe_enable,
> > +	.disable = ofdrm_simple_display_pipe_disable,
> > +	.update = ofdrm_simple_display_pipe_update,
> > +	DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
> > +};
> > +
> > +static const struct drm_mode_config_funcs ofdrm_mode_config_funcs = {
> > +	.fb_create = drm_gem_fb_create_with_dirty,
> > +	.atomic_check = drm_atomic_helper_check,
> > +	.atomic_commit = drm_atomic_helper_commit,
> > +};
> > +
> > +static const uint32_t *ofdrm_device_formats(struct ofdrm_device *odev, size_t *nformats_out)
> > +{
> > +	struct drm_device *dev = &odev->dev;
> > +	size_t i;
> > +
> > +	if (odev->nformats)
> > +		goto out; /* don't rebuild list on recurring calls */
> > +
> > +	/* native format goes first */
> > +	odev->formats[0] = odev->format->format;
> > +	odev->nformats = 1;
> > +
> > +	/* default formats go second */
> > +	for (i = 0; i < ARRAY_SIZE(ofdrm_default_formats); ++i) {
> > +		if (ofdrm_default_formats[i] == odev->format->format)
> > +			continue; /* native format already went first */
> > +		odev->formats[odev->nformats] = ofdrm_default_formats[i];
> > +		odev->nformats++;
> > +	}
> > +
> > +	/*
> > +	 * TODO: The ofdrm driver converts framebuffers to the native
> > +	 * format when copying them to device memory. If there are more
> > +	 * formats listed than supported by the driver, the native format
> > +	 * is not supported by the conversion helpers. Therefore *only*
> > +	 * support the native format and add a conversion helper ASAP.
> > +	 */
> > +	if (drm_WARN_ONCE(dev, i != odev->nformats,
> > +			  "format conversion helpers required for %p4cc",
> > +			  &odev->format->format)) {
> > +		odev->nformats = 1;
> > +	}
> > +
> > +out:
> > +	*nformats_out = odev->nformats;
> > +	return odev->formats;
> > +}
> > +
> > +static int ofdrm_device_init_modeset(struct ofdrm_device *odev)
> > +{
> > +	struct drm_device *dev = &odev->dev;
> > +	struct drm_display_mode *mode = &odev->mode;
> > +	struct drm_connector *connector = &odev->connector;
> > +	struct drm_simple_display_pipe *pipe = &odev->pipe;
> > +	unsigned long max_width, max_height;
> > +	const uint32_t *formats;
> > +	size_t nformats;
> > +	int ret;
> > +
> > +	ret = drmm_mode_config_init(dev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH);
> > +	max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT);
> > +
> > +	dev->mode_config.min_width = mode->hdisplay;
> > +	dev->mode_config.max_width = max_width;
> > +	dev->mode_config.min_height = mode->vdisplay;
> > +	dev->mode_config.max_height = max_height;
> > +	dev->mode_config.preferred_depth = odev->format->cpp[0] * 8;
> > +	dev->mode_config.funcs = &ofdrm_mode_config_funcs;
> > +
> > +	ret = drm_connector_init(dev, connector, &ofdrm_connector_funcs,
> > +				 DRM_MODE_CONNECTOR_Unknown);
> > +	if (ret)
> > +		return ret;
> > +	drm_connector_helper_add(connector, &ofdrm_connector_helper_funcs);
> > +	drm_connector_set_panel_orientation_with_quirk(connector,
> > +						       DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
> > +						       mode->hdisplay, mode->vdisplay);
> > +
> > +	formats = ofdrm_device_formats(odev, &nformats);
> > +
> > +	ret = drm_simple_display_pipe_init(dev, pipe, &ofdrm_simple_display_pipe_funcs,
> > +					   formats, nformats, ofdrm_format_modifiers,
> > +					   connector);
> > +	if (ret)
> > +		return ret;
> > +
> > +	drm_plane_enable_fb_damage_clips(&pipe->plane);
> > +
> > +	drm_mode_config_reset(dev);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * Init / Cleanup
> > + */
> > +
> > +static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv,
> > +						struct platform_device *pdev)
> > +{
> > +	struct ofdrm_device *odev;
> > +	int ret;
> > +
> > +	odev = devm_drm_dev_alloc(&pdev->dev, drv, struct ofdrm_device, dev);
> > +	if (IS_ERR(odev))
> > +		return ERR_CAST(odev);
> > +	odev->pdev = pdev;
> > +	platform_set_drvdata(pdev, &odev->dev);
> > +
> > +	ret = ofdrm_device_init_fb(odev);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +	ret = ofdrm_device_init_mm(odev);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +	ret = ofdrm_device_init_modeset(odev);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +
> > +	return odev;
> > +}
> > +
> > +/*
> > + * DRM driver
> > + */
> > +
> > +DEFINE_DRM_GEM_FOPS(ofdrm_fops);
> > +
> > +static struct drm_driver ofdrm_driver = {
> > +	DRM_GEM_SHMEM_DRIVER_OPS,
> > +	.name			= DRIVER_NAME,
> > +	.desc			= DRIVER_DESC,
> > +	.date			= DRIVER_DATE,
> > +	.major			= DRIVER_MAJOR,
> > +	.minor			= DRIVER_MINOR,
> > +	.driver_features	= DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
> > +	.fops			= &ofdrm_fops,
> > +};
> > +
> > +/*
> > + * Platform driver
> > + */
> > +
> > +static int ofdrm_probe(struct platform_device *pdev)
> > +{
> > +	struct ofdrm_device *odev;
> > +	struct drm_device *dev;
> > +	int ret;
> > +
> > +	odev = ofdrm_device_create(&ofdrm_driver, pdev);
> > +	if (IS_ERR(odev))
> > +		return PTR_ERR(odev);
> > +	dev = &odev->dev;
> > +
> > +	ret = drm_dev_register(dev, 0);
> > +	if (ret)
> > +		return ret;
> > +
> > +	drm_fbdev_generic_setup(dev, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ofdrm_remove(struct platform_device *pdev)
> > +{
> > +	struct drm_device *dev = platform_get_drvdata(pdev);
> > +
> > +	drm_dev_unplug(dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id ofdrm_of_match_display[] = {
> > +	{ .compatible = "display", },
> > +	{ },
> > +};
> > +MODULE_DEVICE_TABLE(of, ofdrm_of_match_display);
> > +
> > +static struct platform_driver ofdrm_platform_driver = {
> > +	.driver = {
> > +		.name = "of-display",
> > +		.of_match_table = ofdrm_of_match_display,
> > +	},
> > +	.probe = ofdrm_probe,
> > +	.remove = ofdrm_remove,
> > +};
> > +
> > +module_platform_driver(ofdrm_platform_driver);
> > +
> > +MODULE_DESCRIPTION(DRIVER_DESC);
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
> > index e8a94e202d63..a4982dc3aa0d 100644
> > --- a/drivers/video/fbdev/Kconfig
> > +++ b/drivers/video/fbdev/Kconfig
> > @@ -455,6 +455,7 @@ config FB_ATARI
> >   config FB_OF
> >   	bool "Open Firmware frame buffer device support"
> >   	depends on (FB = y) && PPC && (!PPC_PSERIES || PCI)
> > +	depends on !DRM_OFDRM
> >   	select FB_CFB_FILLRECT
> >   	select FB_CFB_COPYAREA
> >   	select FB_CFB_IMAGEBLIT
> 
> 
> ATB,
> 
> Mark.
Thomas Zimmermann May 19, 2022, 7:27 a.m. UTC | #2
Hi

Am 19.05.22 um 09:11 schrieb Geert Uytterhoeven:
> Hi Michal,
> 
> On Wed, May 18, 2022 at 8:54 PM Michal Suchánek <msuchanek@suse.de> wrote:
>> On Wed, May 18, 2022 at 08:30:06PM +0200, Thomas Zimmermann wrote:
>>> --- a/drivers/gpu/drm/tiny/Kconfig
>>> +++ b/drivers/gpu/drm/tiny/Kconfig
>>> @@ -51,6 +51,18 @@ config DRM_GM12U320
>>>         This is a KMS driver for projectors which use the GM12U320 chipset
>>>         for video transfer over USB2/3, such as the Acer C120 mini projector.
>>>
>>> +config DRM_OFDRM
>>> +     tristate "Open Firmware display driver"
>>> +     depends on DRM && MMU && PPC
>>
>> Does this build with !PCI?
>>
>> The driver uses some PCI functions, so it might possibly break with
>> randconfig. I don't think there are practical !PCI PPC configurations.
> 
> IIRC, the first PowerMacs didn't have PCI.

I'll try to build without PCI to see what happens.

Those old Macs use BootX, right? BootX is not supported ATM, as I don't 
have the HW to test. Is there an emulator for it?

If anyone what's to make patches for BootX, I'd be happy to add them. 
The offb driver also supports a number of special cases for palette 
handling. That might be necessary for ofdrm as well.

Best regards
Thomas

> 
> Gr{oetje,eeting}s,
> 
>                          Geert
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> 
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                  -- Linus Torvalds
Benjamin Herrenschmidt May 21, 2022, 2:49 a.m. UTC | #3
On Thu, 2022-05-19 at 09:27 +0200, Thomas Zimmermann wrote:

> to build without PCI to see what happens.

If you bring any of the "heuristic" and palette support code in, you
need PCI. I don't see any reason to take it out.

> Those old Macs use BootX, right? BootX is not supported ATM, as I don't 
> have the HW to test. Is there an emulator for it?

It isn't ? When did it break ? :-)

> If anyone what's to make patches for BootX, I'd be happy to add them. 
> The offb driver also supports a number of special cases for palette 
> handling. That might be necessary for ofdrm as well.

The palette handling is useful when using a real Open Firmware
implementation which tends to boot in 8-bit mode, so without palette
things will look ... bad.

It's not necessary when using 16/32 bpp framebuffers which is typically
... what BootX provides :-)

Cheers,
Ben.

> Best regards
> Thomas
> 
> > Gr{oetje,eeting}s,
> > 
> >                          Geert
> > 
> > --
> > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> > 
> > In personal conversations with technical people, I call myself a hacker. But
> > when I'm talking to journalists I just say "programmer" or something like that.
> >                                  -- Linus Torvalds
> 
> -- 
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Software Solutions Germany GmbH
> Maxfeldstr. 5, 90409 Nürnberg, Germany
> (HRB 36809, AG Nürnberg)
> Geschäftsführer: Ivo Totev
Thomas Zimmermann May 25, 2022, 4:45 p.m. UTC | #4
Hi

Am 21.05.22 um 04:49 schrieb Benjamin Herrenschmidt:
> On Thu, 2022-05-19 at 09:27 +0200, Thomas Zimmermann wrote:
> 
>> to build without PCI to see what happens.
> 
> If you bring any of the "heuristic" and palette support code in, you
> need PCI. I don't see any reason to take it out.
> 
>> Those old Macs use BootX, right? BootX is not supported ATM, as I don't
>> have the HW to test. Is there an emulator for it?
> 
> It isn't ? When did it break ? :-)

I meant that BootX is not (yet) supported by this new driver. The Linux 
kernel overall probably supports it.


> 
>> If anyone what's to make patches for BootX, I'd be happy to add them.
>> The offb driver also supports a number of special cases for palette
>> handling. That might be necessary for ofdrm as well.
> 
> The palette handling is useful when using a real Open Firmware
> implementation which tends to boot in 8-bit mode, so without palette
> things will look ... bad.
> 
> It's not necessary when using 16/32 bpp framebuffers which is typically
> ... what BootX provides :-)

Maybe the odd color formats can be tested via qemu.

I don't mind adding DRM support for BootX displays, but getting the 
necessary test HW with a suitable Linux seems to be laborious. Would a 
G4 Powerbook work?

Best regard
Thomas

> 
> Cheers,
> Ben.
> 
>> Best regards
>> Thomas
>>
>>> Gr{oetje,eeting}s,
>>>
>>>                           Geert
>>>
>>> --
>>> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>>>
>>> In personal conversations with technical people, I call myself a hacker. But
>>> when I'm talking to journalists I just say "programmer" or something like that.
>>>                                   -- Linus Torvalds
>>
>> -- 
>> Thomas Zimmermann
>> Graphics Driver Developer
>> SUSE Software Solutions Germany GmbH
>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>> (HRB 36809, AG Nürnberg)
>> Geschäftsführer: Ivo Totev
>
Benjamin Herrenschmidt May 27, 2022, 12:17 a.m. UTC | #5
On Wed, 2022-05-25 at 18:45 +0200, Thomas Zimmermann wrote:
> 
> > The palette handling is useful when using a real Open Firmware
> > implementation which tends to boot in 8-bit mode, so without palette
> > things will look ... bad.
> > 
> > It's not necessary when using 16/32 bpp framebuffers which is typically
> > ... what BootX provides :-)
> 
> Maybe the odd color formats can be tested via qemu.
> 
> I don't mind adding DRM support for BootX displays, but getting the 
> necessary test HW with a suitable Linux seems to be laborious. Would a 
> G4 Powerbook work?

My point was that it's the non-BootX case that cares about the palette
hacks :-)

Cheers,
Ben.
Benjamin Herrenschmidt May 27, 2022, 12:19 a.m. UTC | #6
On Wed, 2022-05-25 at 18:45 +0200, Thomas Zimmermann wrote:
> I don't mind adding DRM support for BootX displays, but getting the 
> necessary test HW with a suitable Linux seems to be laborious. Would
> a  G4 Powerbook work?

Probably not unfortunately... it's going to be tricky. I might sitll
have some old BootX-based machines somewhere in storage I could try to
dig out, but it might not be worth bothering too much...

Cheers,
Ben.