162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/of_address.h> 462306a36Sopenharmony_ci#include <linux/pci.h> 562306a36Sopenharmony_ci#include <linux/platform_device.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <drm/drm_aperture.h> 862306a36Sopenharmony_ci#include <drm/drm_atomic.h> 962306a36Sopenharmony_ci#include <drm/drm_atomic_state_helper.h> 1062306a36Sopenharmony_ci#include <drm/drm_connector.h> 1162306a36Sopenharmony_ci#include <drm/drm_damage_helper.h> 1262306a36Sopenharmony_ci#include <drm/drm_device.h> 1362306a36Sopenharmony_ci#include <drm/drm_drv.h> 1462306a36Sopenharmony_ci#include <drm/drm_fbdev_generic.h> 1562306a36Sopenharmony_ci#include <drm/drm_format_helper.h> 1662306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1762306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 1862306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 1962306a36Sopenharmony_ci#include <drm/drm_gem_shmem_helper.h> 2062306a36Sopenharmony_ci#include <drm/drm_managed.h> 2162306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h> 2262306a36Sopenharmony_ci#include <drm/drm_plane_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2462306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRIVER_NAME "ofdrm" 2762306a36Sopenharmony_ci#define DRIVER_DESC "DRM driver for OF platform devices" 2862306a36Sopenharmony_ci#define DRIVER_DATE "20220501" 2962306a36Sopenharmony_ci#define DRIVER_MAJOR 1 3062306a36Sopenharmony_ci#define DRIVER_MINOR 0 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define PCI_VENDOR_ID_ATI_R520 0x7100 3362306a36Sopenharmony_ci#define PCI_VENDOR_ID_ATI_R600 0x9400 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define OFDRM_GAMMA_LUT_SIZE 256 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Definitions used by the Avivo palette */ 3862306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_SELECT 0x6480 3962306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_MODE 0x6484 4062306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_INDEX 0x6488 4162306a36Sopenharmony_ci#define AVIVO_DC_LUT_SEQ_COLOR 0x648c 4262306a36Sopenharmony_ci#define AVIVO_DC_LUT_PWL_DATA 0x6490 4362306a36Sopenharmony_ci#define AVIVO_DC_LUT_30_COLOR 0x6494 4462306a36Sopenharmony_ci#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 4562306a36Sopenharmony_ci#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c 4662306a36Sopenharmony_ci#define AVIVO_DC_LUT_AUTOFILL 0x64a0 4762306a36Sopenharmony_ci#define AVIVO_DC_LUTA_CONTROL 0x64c0 4862306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 4962306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 5062306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc 5162306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 5262306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 5362306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 5462306a36Sopenharmony_ci#define AVIVO_DC_LUTB_CONTROL 0x6cc0 5562306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4 5662306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8 5762306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc 5862306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0 5962306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4 6062306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cienum ofdrm_model { 6362306a36Sopenharmony_ci OFDRM_MODEL_UNKNOWN, 6462306a36Sopenharmony_ci OFDRM_MODEL_MACH64, /* ATI Mach64 */ 6562306a36Sopenharmony_ci OFDRM_MODEL_RAGE128, /* ATI Rage128 */ 6662306a36Sopenharmony_ci OFDRM_MODEL_RAGE_M3A, /* ATI Rage Mobility M3 Head A */ 6762306a36Sopenharmony_ci OFDRM_MODEL_RAGE_M3B, /* ATI Rage Mobility M3 Head B */ 6862306a36Sopenharmony_ci OFDRM_MODEL_RADEON, /* ATI Radeon */ 6962306a36Sopenharmony_ci OFDRM_MODEL_GXT2000, /* IBM GXT2000 */ 7062306a36Sopenharmony_ci OFDRM_MODEL_AVIVO, /* ATI R5xx */ 7162306a36Sopenharmony_ci OFDRM_MODEL_QEMU, /* QEMU VGA */ 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Helpers for display nodes 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int display_get_validated_int(struct drm_device *dev, const char *name, uint32_t value) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (value > INT_MAX) { 8162306a36Sopenharmony_ci drm_err(dev, "invalid framebuffer %s of %u\n", name, value); 8262306a36Sopenharmony_ci return -EINVAL; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci return (int)value; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int display_get_validated_int0(struct drm_device *dev, const char *name, uint32_t value) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci if (!value) { 9062306a36Sopenharmony_ci drm_err(dev, "invalid framebuffer %s of %u\n", name, value); 9162306a36Sopenharmony_ci return -EINVAL; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci return display_get_validated_int(dev, name, value); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct drm_format_info *display_get_validated_format(struct drm_device *dev, 9762306a36Sopenharmony_ci u32 depth, bool big_endian) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci const struct drm_format_info *info; 10062306a36Sopenharmony_ci u32 format; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci switch (depth) { 10362306a36Sopenharmony_ci case 8: 10462306a36Sopenharmony_ci format = drm_mode_legacy_fb_format(8, 8); 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci case 15: 10762306a36Sopenharmony_ci case 16: 10862306a36Sopenharmony_ci format = drm_mode_legacy_fb_format(16, depth); 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case 32: 11162306a36Sopenharmony_ci format = drm_mode_legacy_fb_format(32, 24); 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci default: 11462306a36Sopenharmony_ci drm_err(dev, "unsupported framebuffer depth %u\n", depth); 11562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* 11962306a36Sopenharmony_ci * DRM formats assume little-endian byte order. Update the format 12062306a36Sopenharmony_ci * if the scanout buffer uses big-endian ordering. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci if (big_endian) { 12362306a36Sopenharmony_ci switch (format) { 12462306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 12562306a36Sopenharmony_ci format = DRM_FORMAT_BGRX8888; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 12862306a36Sopenharmony_ci format = DRM_FORMAT_BGRA8888; 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 13162306a36Sopenharmony_ci format = DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN; 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case DRM_FORMAT_XRGB1555: 13462306a36Sopenharmony_ci format = DRM_FORMAT_XRGB1555 | DRM_FORMAT_BIG_ENDIAN; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci default: 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci info = drm_format_info(format); 14262306a36Sopenharmony_ci if (!info) { 14362306a36Sopenharmony_ci drm_err(dev, "cannot find framebuffer format for depth %u\n", depth); 14462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return info; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int display_read_u32_of(struct drm_device *dev, struct device_node *of_node, 15162306a36Sopenharmony_ci const char *name, u32 *value) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int ret = of_property_read_u32(of_node, name, value); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (ret) 15662306a36Sopenharmony_ci drm_err(dev, "cannot parse framebuffer %s: error %d\n", name, ret); 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic bool display_get_big_endian_of(struct drm_device *dev, struct device_node *of_node) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci bool big_endian; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 16562306a36Sopenharmony_ci big_endian = !of_property_read_bool(of_node, "little-endian"); 16662306a36Sopenharmony_ci#else 16762306a36Sopenharmony_ci big_endian = of_property_read_bool(of_node, "big-endian"); 16862306a36Sopenharmony_ci#endif 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return big_endian; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int display_get_width_of(struct drm_device *dev, struct device_node *of_node) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci u32 width; 17662306a36Sopenharmony_ci int ret = display_read_u32_of(dev, of_node, "width", &width); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (ret) 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci return display_get_validated_int0(dev, "width", width); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int display_get_height_of(struct drm_device *dev, struct device_node *of_node) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci u32 height; 18662306a36Sopenharmony_ci int ret = display_read_u32_of(dev, of_node, "height", &height); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (ret) 18962306a36Sopenharmony_ci return ret; 19062306a36Sopenharmony_ci return display_get_validated_int0(dev, "height", height); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int display_get_depth_of(struct drm_device *dev, struct device_node *of_node) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u32 depth; 19662306a36Sopenharmony_ci int ret = display_read_u32_of(dev, of_node, "depth", &depth); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (ret) 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci return display_get_validated_int0(dev, "depth", depth); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int display_get_linebytes_of(struct drm_device *dev, struct device_node *of_node) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u32 linebytes; 20662306a36Sopenharmony_ci int ret = display_read_u32_of(dev, of_node, "linebytes", &linebytes); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (ret) 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci return display_get_validated_int(dev, "linebytes", linebytes); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic u64 display_get_address_of(struct drm_device *dev, struct device_node *of_node) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci u32 address; 21662306a36Sopenharmony_ci int ret; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * Not all devices provide an address property, it's not 22062306a36Sopenharmony_ci * a bug if this fails. The driver will try to find the 22162306a36Sopenharmony_ci * framebuffer base address from the device's memory regions. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci ret = of_property_read_u32(of_node, "address", &address); 22462306a36Sopenharmony_ci if (ret) 22562306a36Sopenharmony_ci return OF_BAD_ADDR; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return address; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic bool is_avivo(u32 vendor, u32 device) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci /* This will match most R5xx */ 23362306a36Sopenharmony_ci return (vendor == PCI_VENDOR_ID_ATI) && 23462306a36Sopenharmony_ci ((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) || 23562306a36Sopenharmony_ci (PCI_VENDOR_ID_ATI_R600 >= 0x9400)); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci enum ofdrm_model model = OFDRM_MODEL_UNKNOWN; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (of_node_name_prefix(of_node, "ATY,Rage128")) { 24362306a36Sopenharmony_ci model = OFDRM_MODEL_RAGE128; 24462306a36Sopenharmony_ci } else if (of_node_name_prefix(of_node, "ATY,RageM3pA") || 24562306a36Sopenharmony_ci of_node_name_prefix(of_node, "ATY,RageM3p12A")) { 24662306a36Sopenharmony_ci model = OFDRM_MODEL_RAGE_M3A; 24762306a36Sopenharmony_ci } else if (of_node_name_prefix(of_node, "ATY,RageM3pB")) { 24862306a36Sopenharmony_ci model = OFDRM_MODEL_RAGE_M3B; 24962306a36Sopenharmony_ci } else if (of_node_name_prefix(of_node, "ATY,Rage6")) { 25062306a36Sopenharmony_ci model = OFDRM_MODEL_RADEON; 25162306a36Sopenharmony_ci } else if (of_node_name_prefix(of_node, "ATY,")) { 25262306a36Sopenharmony_ci return OFDRM_MODEL_MACH64; 25362306a36Sopenharmony_ci } else if (of_device_is_compatible(of_node, "pci1014,b7") || 25462306a36Sopenharmony_ci of_device_is_compatible(of_node, "pci1014,21c")) { 25562306a36Sopenharmony_ci model = OFDRM_MODEL_GXT2000; 25662306a36Sopenharmony_ci } else if (of_node_name_prefix(of_node, "vga,Display-")) { 25762306a36Sopenharmony_ci struct device_node *of_parent; 25862306a36Sopenharmony_ci const __be32 *vendor_p, *device_p; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Look for AVIVO initialized by SLOF */ 26162306a36Sopenharmony_ci of_parent = of_get_parent(of_node); 26262306a36Sopenharmony_ci vendor_p = of_get_property(of_parent, "vendor-id", NULL); 26362306a36Sopenharmony_ci device_p = of_get_property(of_parent, "device-id", NULL); 26462306a36Sopenharmony_ci if (vendor_p && device_p) { 26562306a36Sopenharmony_ci u32 vendor = be32_to_cpup(vendor_p); 26662306a36Sopenharmony_ci u32 device = be32_to_cpup(device_p); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (is_avivo(vendor, device)) 26962306a36Sopenharmony_ci model = OFDRM_MODEL_AVIVO; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci of_node_put(of_parent); 27262306a36Sopenharmony_ci } else if (of_device_is_compatible(of_node, "qemu,std-vga")) { 27362306a36Sopenharmony_ci model = OFDRM_MODEL_QEMU; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return model; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * Open Firmware display device 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistruct ofdrm_device; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistruct ofdrm_device_funcs { 28662306a36Sopenharmony_ci void __iomem *(*cmap_ioremap)(struct ofdrm_device *odev, 28762306a36Sopenharmony_ci struct device_node *of_node, 28862306a36Sopenharmony_ci u64 fb_bas); 28962306a36Sopenharmony_ci void (*cmap_write)(struct ofdrm_device *odev, unsigned char index, 29062306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b); 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistruct ofdrm_device { 29462306a36Sopenharmony_ci struct drm_device dev; 29562306a36Sopenharmony_ci struct platform_device *pdev; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci const struct ofdrm_device_funcs *funcs; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* firmware-buffer settings */ 30062306a36Sopenharmony_ci struct iosys_map screen_base; 30162306a36Sopenharmony_ci struct drm_display_mode mode; 30262306a36Sopenharmony_ci const struct drm_format_info *format; 30362306a36Sopenharmony_ci unsigned int pitch; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* colormap */ 30662306a36Sopenharmony_ci void __iomem *cmap_base; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* modesetting */ 30962306a36Sopenharmony_ci uint32_t formats[8]; 31062306a36Sopenharmony_ci struct drm_plane primary_plane; 31162306a36Sopenharmony_ci struct drm_crtc crtc; 31262306a36Sopenharmony_ci struct drm_encoder encoder; 31362306a36Sopenharmony_ci struct drm_connector connector; 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic struct ofdrm_device *ofdrm_device_of_dev(struct drm_device *dev) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci return container_of(dev, struct ofdrm_device, dev); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* 32262306a36Sopenharmony_ci * Hardware 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci#if defined(CONFIG_PCI) 32662306a36Sopenharmony_cistatic struct pci_dev *display_get_pci_dev_of(struct drm_device *dev, struct device_node *of_node) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci const __be32 *vendor_p, *device_p; 32962306a36Sopenharmony_ci u32 vendor, device; 33062306a36Sopenharmony_ci struct pci_dev *pcidev; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci vendor_p = of_get_property(of_node, "vendor-id", NULL); 33362306a36Sopenharmony_ci if (!vendor_p) 33462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 33562306a36Sopenharmony_ci vendor = be32_to_cpup(vendor_p); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci device_p = of_get_property(of_node, "device-id", NULL); 33862306a36Sopenharmony_ci if (!device_p) 33962306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 34062306a36Sopenharmony_ci device = be32_to_cpup(device_p); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci pcidev = pci_get_device(vendor, device, NULL); 34362306a36Sopenharmony_ci if (!pcidev) 34462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return pcidev; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void ofdrm_pci_release(void *data) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct pci_dev *pcidev = data; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci pci_disable_device(pcidev); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int ofdrm_device_init_pci(struct ofdrm_device *odev) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct drm_device *dev = &odev->dev; 35962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev->dev); 36062306a36Sopenharmony_ci struct device_node *of_node = pdev->dev.of_node; 36162306a36Sopenharmony_ci struct pci_dev *pcidev; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* 36562306a36Sopenharmony_ci * Never use pcim_ or other managed helpers on the returned PCI 36662306a36Sopenharmony_ci * device. Otherwise, probing the native driver will fail for 36762306a36Sopenharmony_ci * resource conflicts. PCI-device management has to be tied to 36862306a36Sopenharmony_ci * the lifetime of the platform device until the native driver 36962306a36Sopenharmony_ci * takes over. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci pcidev = display_get_pci_dev_of(dev, of_node); 37262306a36Sopenharmony_ci if (IS_ERR(pcidev)) 37362306a36Sopenharmony_ci return 0; /* no PCI device found; ignore the error */ 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = pci_enable_device(pcidev); 37662306a36Sopenharmony_ci if (ret) { 37762306a36Sopenharmony_ci drm_err(dev, "pci_enable_device(%s) failed: %d\n", 37862306a36Sopenharmony_ci dev_name(&pcidev->dev), ret); 37962306a36Sopenharmony_ci return ret; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci ret = devm_add_action_or_reset(&pdev->dev, ofdrm_pci_release, pcidev); 38262306a36Sopenharmony_ci if (ret) 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci#else 38862306a36Sopenharmony_cistatic int ofdrm_device_init_pci(struct ofdrm_device *odev) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci#endif 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* 39562306a36Sopenharmony_ci * OF display settings 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev, 39962306a36Sopenharmony_ci struct resource *fb_res) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(odev->dev.dev); 40262306a36Sopenharmony_ci struct resource *res, *max_res = NULL; 40362306a36Sopenharmony_ci u32 i; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci for (i = 0; pdev->num_resources; ++i) { 40662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, i); 40762306a36Sopenharmony_ci if (!res) 40862306a36Sopenharmony_ci break; /* all resources processed */ 40962306a36Sopenharmony_ci if (resource_size(res) < resource_size(fb_res)) 41062306a36Sopenharmony_ci continue; /* resource too small */ 41162306a36Sopenharmony_ci if (fb_res->start && resource_contains(res, fb_res)) 41262306a36Sopenharmony_ci return res; /* resource contains framebuffer */ 41362306a36Sopenharmony_ci if (!max_res || resource_size(res) > resource_size(max_res)) 41462306a36Sopenharmony_ci max_res = res; /* store largest resource as fallback */ 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return max_res; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* 42162306a36Sopenharmony_ci * Colormap / Palette 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void __iomem *get_cmap_address_of(struct ofdrm_device *odev, struct device_node *of_node, 42562306a36Sopenharmony_ci int bar_no, unsigned long offset, unsigned long size) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct drm_device *dev = &odev->dev; 42862306a36Sopenharmony_ci const __be32 *addr_p; 42962306a36Sopenharmony_ci u64 max_size, address; 43062306a36Sopenharmony_ci unsigned int flags; 43162306a36Sopenharmony_ci void __iomem *mem; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci addr_p = of_get_pci_address(of_node, bar_no, &max_size, &flags); 43462306a36Sopenharmony_ci if (!addr_p) 43562306a36Sopenharmony_ci addr_p = of_get_address(of_node, bar_no, &max_size, &flags); 43662306a36Sopenharmony_ci if (!addr_p) 43762306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENODEV); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) 44062306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENODEV); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if ((offset + size) >= max_size) 44362306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENODEV); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci address = of_translate_address(of_node, addr_p); 44662306a36Sopenharmony_ci if (address == OF_BAD_ADDR) 44762306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENODEV); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci mem = devm_ioremap(dev->dev, address + offset, size); 45062306a36Sopenharmony_ci if (!mem) 45162306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENOMEM); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return mem; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic void __iomem *ofdrm_mach64_cmap_ioremap(struct ofdrm_device *odev, 45762306a36Sopenharmony_ci struct device_node *of_node, 45862306a36Sopenharmony_ci u64 fb_base) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct drm_device *dev = &odev->dev; 46162306a36Sopenharmony_ci u64 address; 46262306a36Sopenharmony_ci void __iomem *cmap_base; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci address = fb_base & 0xff000000ul; 46562306a36Sopenharmony_ci address += 0x7ff000; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci cmap_base = devm_ioremap(dev->dev, address, 0x1000); 46862306a36Sopenharmony_ci if (!cmap_base) 46962306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENOMEM); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return cmap_base; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic void ofdrm_mach64_cmap_write(struct ofdrm_device *odev, unsigned char index, 47562306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci void __iomem *addr = odev->cmap_base + 0xcc0; 47862306a36Sopenharmony_ci void __iomem *data = odev->cmap_base + 0xcc0 + 1; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci writeb(index, addr); 48162306a36Sopenharmony_ci writeb(r, data); 48262306a36Sopenharmony_ci writeb(g, data); 48362306a36Sopenharmony_ci writeb(b, data); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void __iomem *ofdrm_rage128_cmap_ioremap(struct ofdrm_device *odev, 48762306a36Sopenharmony_ci struct device_node *of_node, 48862306a36Sopenharmony_ci u64 fb_base) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic void ofdrm_rage128_cmap_write(struct ofdrm_device *odev, unsigned char index, 49462306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci void __iomem *addr = odev->cmap_base + 0xb0; 49762306a36Sopenharmony_ci void __iomem *data = odev->cmap_base + 0xb4; 49862306a36Sopenharmony_ci u32 color = (r << 16) | (g << 8) | b; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci writeb(index, addr); 50162306a36Sopenharmony_ci writel(color, data); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic void __iomem *ofdrm_rage_m3a_cmap_ioremap(struct ofdrm_device *odev, 50562306a36Sopenharmony_ci struct device_node *of_node, 50662306a36Sopenharmony_ci u64 fb_base) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void ofdrm_rage_m3a_cmap_write(struct ofdrm_device *odev, unsigned char index, 51262306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci void __iomem *dac_ctl = odev->cmap_base + 0x58; 51562306a36Sopenharmony_ci void __iomem *addr = odev->cmap_base + 0xb0; 51662306a36Sopenharmony_ci void __iomem *data = odev->cmap_base + 0xb4; 51762306a36Sopenharmony_ci u32 color = (r << 16) | (g << 8) | b; 51862306a36Sopenharmony_ci u32 val; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ 52162306a36Sopenharmony_ci val = readl(dac_ctl); 52262306a36Sopenharmony_ci val &= ~0x20; 52362306a36Sopenharmony_ci writel(val, dac_ctl); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Set color at palette index */ 52662306a36Sopenharmony_ci writeb(index, addr); 52762306a36Sopenharmony_ci writel(color, data); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic void __iomem *ofdrm_rage_m3b_cmap_ioremap(struct ofdrm_device *odev, 53162306a36Sopenharmony_ci struct device_node *of_node, 53262306a36Sopenharmony_ci u64 fb_base) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic void ofdrm_rage_m3b_cmap_write(struct ofdrm_device *odev, unsigned char index, 53862306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci void __iomem *dac_ctl = odev->cmap_base + 0x58; 54162306a36Sopenharmony_ci void __iomem *addr = odev->cmap_base + 0xb0; 54262306a36Sopenharmony_ci void __iomem *data = odev->cmap_base + 0xb4; 54362306a36Sopenharmony_ci u32 color = (r << 16) | (g << 8) | b; 54462306a36Sopenharmony_ci u32 val; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */ 54762306a36Sopenharmony_ci val = readl(dac_ctl); 54862306a36Sopenharmony_ci val |= 0x20; 54962306a36Sopenharmony_ci writel(val, dac_ctl); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Set color at palette index */ 55262306a36Sopenharmony_ci writeb(index, addr); 55362306a36Sopenharmony_ci writel(color, data); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void __iomem *ofdrm_radeon_cmap_ioremap(struct ofdrm_device *odev, 55762306a36Sopenharmony_ci struct device_node *of_node, 55862306a36Sopenharmony_ci u64 fb_base) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci return get_cmap_address_of(odev, of_node, 1, 0, 0x1fff); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic void __iomem *ofdrm_gxt2000_cmap_ioremap(struct ofdrm_device *odev, 56462306a36Sopenharmony_ci struct device_node *of_node, 56562306a36Sopenharmony_ci u64 fb_base) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci return get_cmap_address_of(odev, of_node, 0, 0x6000, 0x1000); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic void ofdrm_gxt2000_cmap_write(struct ofdrm_device *odev, unsigned char index, 57162306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci void __iomem *data = ((unsigned int __iomem *)odev->cmap_base) + index; 57462306a36Sopenharmony_ci u32 color = (r << 16) | (g << 8) | b; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci writel(color, data); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic void __iomem *ofdrm_avivo_cmap_ioremap(struct ofdrm_device *odev, 58062306a36Sopenharmony_ci struct device_node *of_node, 58162306a36Sopenharmony_ci u64 fb_base) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct device_node *of_parent; 58462306a36Sopenharmony_ci void __iomem *cmap_base; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci of_parent = of_get_parent(of_node); 58762306a36Sopenharmony_ci cmap_base = get_cmap_address_of(odev, of_parent, 0, 0, 0x10000); 58862306a36Sopenharmony_ci of_node_put(of_parent); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return cmap_base; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void ofdrm_avivo_cmap_write(struct ofdrm_device *odev, unsigned char index, 59462306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci void __iomem *lutsel = odev->cmap_base + AVIVO_DC_LUT_RW_SELECT; 59762306a36Sopenharmony_ci void __iomem *addr = odev->cmap_base + AVIVO_DC_LUT_RW_INDEX; 59862306a36Sopenharmony_ci void __iomem *data = odev->cmap_base + AVIVO_DC_LUT_30_COLOR; 59962306a36Sopenharmony_ci u32 color = (r << 22) | (g << 12) | (b << 2); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Write to both LUTs for now */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci writel(1, lutsel); 60462306a36Sopenharmony_ci writeb(index, addr); 60562306a36Sopenharmony_ci writel(color, data); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci writel(0, lutsel); 60862306a36Sopenharmony_ci writeb(index, addr); 60962306a36Sopenharmony_ci writel(color, data); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic void __iomem *ofdrm_qemu_cmap_ioremap(struct ofdrm_device *odev, 61362306a36Sopenharmony_ci struct device_node *of_node, 61462306a36Sopenharmony_ci u64 fb_base) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci static const __be32 io_of_addr[3] = { 61762306a36Sopenharmony_ci cpu_to_be32(0x01000000), 61862306a36Sopenharmony_ci cpu_to_be32(0x00), 61962306a36Sopenharmony_ci cpu_to_be32(0x00), 62062306a36Sopenharmony_ci }; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci struct drm_device *dev = &odev->dev; 62362306a36Sopenharmony_ci u64 address; 62462306a36Sopenharmony_ci void __iomem *cmap_base; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci address = of_translate_address(of_node, io_of_addr); 62762306a36Sopenharmony_ci if (address == OF_BAD_ADDR) 62862306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENODEV); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci cmap_base = devm_ioremap(dev->dev, address + 0x3c8, 2); 63162306a36Sopenharmony_ci if (!cmap_base) 63262306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENOMEM); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return cmap_base; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic void ofdrm_qemu_cmap_write(struct ofdrm_device *odev, unsigned char index, 63862306a36Sopenharmony_ci unsigned char r, unsigned char g, unsigned char b) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci void __iomem *addr = odev->cmap_base; 64162306a36Sopenharmony_ci void __iomem *data = odev->cmap_base + 1; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci writeb(index, addr); 64462306a36Sopenharmony_ci writeb(r, data); 64562306a36Sopenharmony_ci writeb(g, data); 64662306a36Sopenharmony_ci writeb(b, data); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void ofdrm_device_set_gamma_linear(struct ofdrm_device *odev, 65062306a36Sopenharmony_ci const struct drm_format_info *format) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct drm_device *dev = &odev->dev; 65362306a36Sopenharmony_ci int i; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci switch (format->format) { 65662306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 65762306a36Sopenharmony_ci case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: 65862306a36Sopenharmony_ci /* Use better interpolation, to take 32 values from 0 to 255 */ 65962306a36Sopenharmony_ci for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { 66062306a36Sopenharmony_ci unsigned char r = i * 8 + i / 4; 66162306a36Sopenharmony_ci unsigned char g = i * 4 + i / 16; 66262306a36Sopenharmony_ci unsigned char b = i * 8 + i / 4; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci odev->funcs->cmap_write(odev, i, r, g, b); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci /* Green has one more bit, so add padding with 0 for red and blue. */ 66762306a36Sopenharmony_ci for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { 66862306a36Sopenharmony_ci unsigned char r = 0; 66962306a36Sopenharmony_ci unsigned char g = i * 4 + i / 16; 67062306a36Sopenharmony_ci unsigned char b = 0; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci odev->funcs->cmap_write(odev, i, r, g, b); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 67662306a36Sopenharmony_ci case DRM_FORMAT_BGRX8888: 67762306a36Sopenharmony_ci for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) 67862306a36Sopenharmony_ci odev->funcs->cmap_write(odev, i, i, i, i); 67962306a36Sopenharmony_ci break; 68062306a36Sopenharmony_ci default: 68162306a36Sopenharmony_ci drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", 68262306a36Sopenharmony_ci &format->format); 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic void ofdrm_device_set_gamma(struct ofdrm_device *odev, 68862306a36Sopenharmony_ci const struct drm_format_info *format, 68962306a36Sopenharmony_ci struct drm_color_lut *lut) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct drm_device *dev = &odev->dev; 69262306a36Sopenharmony_ci int i; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci switch (format->format) { 69562306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 69662306a36Sopenharmony_ci case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: 69762306a36Sopenharmony_ci /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ 69862306a36Sopenharmony_ci for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { 69962306a36Sopenharmony_ci unsigned char r = lut[i * 8 + i / 4].red >> 8; 70062306a36Sopenharmony_ci unsigned char g = lut[i * 4 + i / 16].green >> 8; 70162306a36Sopenharmony_ci unsigned char b = lut[i * 8 + i / 4].blue >> 8; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci odev->funcs->cmap_write(odev, i, r, g, b); 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci /* Green has one more bit, so add padding with 0 for red and blue. */ 70662306a36Sopenharmony_ci for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { 70762306a36Sopenharmony_ci unsigned char r = 0; 70862306a36Sopenharmony_ci unsigned char g = lut[i * 4 + i / 16].green >> 8; 70962306a36Sopenharmony_ci unsigned char b = 0; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci odev->funcs->cmap_write(odev, i, r, g, b); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci break; 71462306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 71562306a36Sopenharmony_ci case DRM_FORMAT_BGRX8888: 71662306a36Sopenharmony_ci for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) { 71762306a36Sopenharmony_ci unsigned char r = lut[i].red >> 8; 71862306a36Sopenharmony_ci unsigned char g = lut[i].green >> 8; 71962306a36Sopenharmony_ci unsigned char b = lut[i].blue >> 8; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci odev->funcs->cmap_write(odev, i, r, g, b); 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci default: 72562306a36Sopenharmony_ci drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", 72662306a36Sopenharmony_ci &format->format); 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* 73262306a36Sopenharmony_ci * Modesetting 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistruct ofdrm_crtc_state { 73662306a36Sopenharmony_ci struct drm_crtc_state base; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Primary-plane format; required for color mgmt. */ 73962306a36Sopenharmony_ci const struct drm_format_info *format; 74062306a36Sopenharmony_ci}; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic struct ofdrm_crtc_state *to_ofdrm_crtc_state(struct drm_crtc_state *base) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci return container_of(base, struct ofdrm_crtc_state, base); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic void ofdrm_crtc_state_destroy(struct ofdrm_crtc_state *ofdrm_crtc_state) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(&ofdrm_crtc_state->base); 75062306a36Sopenharmony_ci kfree(ofdrm_crtc_state); 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic const uint64_t ofdrm_primary_plane_format_modifiers[] = { 75462306a36Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 75562306a36Sopenharmony_ci DRM_FORMAT_MOD_INVALID 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, 75962306a36Sopenharmony_ci struct drm_atomic_state *new_state) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); 76262306a36Sopenharmony_ci struct drm_framebuffer *new_fb = new_plane_state->fb; 76362306a36Sopenharmony_ci struct drm_crtc *new_crtc = new_plane_state->crtc; 76462306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state = NULL; 76562306a36Sopenharmony_ci struct ofdrm_crtc_state *new_ofdrm_crtc_state; 76662306a36Sopenharmony_ci int ret; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (new_crtc) 76962306a36Sopenharmony_ci new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 77262306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 77362306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 77462306a36Sopenharmony_ci false, false); 77562306a36Sopenharmony_ci if (ret) 77662306a36Sopenharmony_ci return ret; 77762306a36Sopenharmony_ci else if (!new_plane_state->visible) 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci new_ofdrm_crtc_state = to_ofdrm_crtc_state(new_crtc_state); 78362306a36Sopenharmony_ci new_ofdrm_crtc_state->format = new_fb->format; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void ofdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, 78962306a36Sopenharmony_ci struct drm_atomic_state *state) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct drm_device *dev = plane->dev; 79262306a36Sopenharmony_ci struct ofdrm_device *odev = ofdrm_device_of_dev(dev); 79362306a36Sopenharmony_ci struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 79462306a36Sopenharmony_ci struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 79562306a36Sopenharmony_ci struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 79662306a36Sopenharmony_ci struct drm_framebuffer *fb = plane_state->fb; 79762306a36Sopenharmony_ci unsigned int dst_pitch = odev->pitch; 79862306a36Sopenharmony_ci const struct drm_format_info *dst_format = odev->format; 79962306a36Sopenharmony_ci struct drm_atomic_helper_damage_iter iter; 80062306a36Sopenharmony_ci struct drm_rect damage; 80162306a36Sopenharmony_ci int ret, idx; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 80462306a36Sopenharmony_ci if (ret) 80562306a36Sopenharmony_ci return; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (!drm_dev_enter(dev, &idx)) 80862306a36Sopenharmony_ci goto out_drm_gem_fb_end_cpu_access; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 81162306a36Sopenharmony_ci drm_atomic_for_each_plane_damage(&iter, &damage) { 81262306a36Sopenharmony_ci struct iosys_map dst = odev->screen_base; 81362306a36Sopenharmony_ci struct drm_rect dst_clip = plane_state->dst; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (!drm_rect_intersect(&dst_clip, &damage)) 81662306a36Sopenharmony_ci continue; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); 81962306a36Sopenharmony_ci drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, 82062306a36Sopenharmony_ci &damage); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci drm_dev_exit(idx); 82462306a36Sopenharmony_ciout_drm_gem_fb_end_cpu_access: 82562306a36Sopenharmony_ci drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic void ofdrm_primary_plane_helper_atomic_disable(struct drm_plane *plane, 82962306a36Sopenharmony_ci struct drm_atomic_state *state) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct drm_device *dev = plane->dev; 83262306a36Sopenharmony_ci struct ofdrm_device *odev = ofdrm_device_of_dev(dev); 83362306a36Sopenharmony_ci struct iosys_map dst = odev->screen_base; 83462306a36Sopenharmony_ci struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 83562306a36Sopenharmony_ci void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ 83662306a36Sopenharmony_ci unsigned int dst_pitch = odev->pitch; 83762306a36Sopenharmony_ci const struct drm_format_info *dst_format = odev->format; 83862306a36Sopenharmony_ci struct drm_rect dst_clip; 83962306a36Sopenharmony_ci unsigned long lines, linepixels, i; 84062306a36Sopenharmony_ci int idx; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci drm_rect_init(&dst_clip, 84362306a36Sopenharmony_ci plane_state->src_x >> 16, plane_state->src_y >> 16, 84462306a36Sopenharmony_ci plane_state->src_w >> 16, plane_state->src_h >> 16); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci lines = drm_rect_height(&dst_clip); 84762306a36Sopenharmony_ci linepixels = drm_rect_width(&dst_clip); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (!drm_dev_enter(dev, &idx)) 85062306a36Sopenharmony_ci return; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Clear buffer to black if disabled */ 85362306a36Sopenharmony_ci dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); 85462306a36Sopenharmony_ci for (i = 0; i < lines; ++i) { 85562306a36Sopenharmony_ci memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); 85662306a36Sopenharmony_ci dst_vmap += dst_pitch; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci drm_dev_exit(idx); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs ofdrm_primary_plane_helper_funcs = { 86362306a36Sopenharmony_ci DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 86462306a36Sopenharmony_ci .atomic_check = ofdrm_primary_plane_helper_atomic_check, 86562306a36Sopenharmony_ci .atomic_update = ofdrm_primary_plane_helper_atomic_update, 86662306a36Sopenharmony_ci .atomic_disable = ofdrm_primary_plane_helper_atomic_disable, 86762306a36Sopenharmony_ci}; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic const struct drm_plane_funcs ofdrm_primary_plane_funcs = { 87062306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 87162306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 87262306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 87362306a36Sopenharmony_ci DRM_GEM_SHADOW_PLANE_FUNCS, 87462306a36Sopenharmony_ci}; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic enum drm_mode_status ofdrm_crtc_helper_mode_valid(struct drm_crtc *crtc, 87762306a36Sopenharmony_ci const struct drm_display_mode *mode) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci struct ofdrm_device *odev = ofdrm_device_of_dev(crtc->dev); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return drm_crtc_helper_mode_valid_fixed(crtc, mode, &odev->mode); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int ofdrm_crtc_helper_atomic_check(struct drm_crtc *crtc, 88562306a36Sopenharmony_ci struct drm_atomic_state *new_state) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci static const size_t gamma_lut_length = OFDRM_GAMMA_LUT_SIZE * sizeof(struct drm_color_lut); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 89062306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); 89162306a36Sopenharmony_ci int ret; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (!new_crtc_state->enable) 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); 89762306a36Sopenharmony_ci if (ret) 89862306a36Sopenharmony_ci return ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (new_crtc_state->color_mgmt_changed) { 90162306a36Sopenharmony_ci struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { 90462306a36Sopenharmony_ci drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length); 90562306a36Sopenharmony_ci return -EINVAL; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci return 0; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic void ofdrm_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct ofdrm_device *odev = ofdrm_device_of_dev(crtc->dev); 91562306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 91662306a36Sopenharmony_ci struct ofdrm_crtc_state *ofdrm_crtc_state = to_ofdrm_crtc_state(crtc_state); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (crtc_state->enable && crtc_state->color_mgmt_changed) { 91962306a36Sopenharmony_ci const struct drm_format_info *format = ofdrm_crtc_state->format; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (crtc_state->gamma_lut) 92262306a36Sopenharmony_ci ofdrm_device_set_gamma(odev, format, crtc_state->gamma_lut->data); 92362306a36Sopenharmony_ci else 92462306a36Sopenharmony_ci ofdrm_device_set_gamma_linear(odev, format); 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/* 92962306a36Sopenharmony_ci * The CRTC is always enabled. Screen updates are performed by 93062306a36Sopenharmony_ci * the primary plane's atomic_update function. Disabling clears 93162306a36Sopenharmony_ci * the screen in the primary plane's atomic_disable function. 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs ofdrm_crtc_helper_funcs = { 93462306a36Sopenharmony_ci .mode_valid = ofdrm_crtc_helper_mode_valid, 93562306a36Sopenharmony_ci .atomic_check = ofdrm_crtc_helper_atomic_check, 93662306a36Sopenharmony_ci .atomic_flush = ofdrm_crtc_helper_atomic_flush, 93762306a36Sopenharmony_ci}; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void ofdrm_crtc_reset(struct drm_crtc *crtc) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct ofdrm_crtc_state *ofdrm_crtc_state = 94262306a36Sopenharmony_ci kzalloc(sizeof(*ofdrm_crtc_state), GFP_KERNEL); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (crtc->state) 94562306a36Sopenharmony_ci ofdrm_crtc_state_destroy(to_ofdrm_crtc_state(crtc->state)); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (ofdrm_crtc_state) 94862306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &ofdrm_crtc_state->base); 94962306a36Sopenharmony_ci else 95062306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, NULL); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic struct drm_crtc_state *ofdrm_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 95662306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = crtc->state; 95762306a36Sopenharmony_ci struct ofdrm_crtc_state *new_ofdrm_crtc_state; 95862306a36Sopenharmony_ci struct ofdrm_crtc_state *ofdrm_crtc_state; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (drm_WARN_ON(dev, !crtc_state)) 96162306a36Sopenharmony_ci return NULL; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci new_ofdrm_crtc_state = kzalloc(sizeof(*new_ofdrm_crtc_state), GFP_KERNEL); 96462306a36Sopenharmony_ci if (!new_ofdrm_crtc_state) 96562306a36Sopenharmony_ci return NULL; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci ofdrm_crtc_state = to_ofdrm_crtc_state(crtc_state); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &new_ofdrm_crtc_state->base); 97062306a36Sopenharmony_ci new_ofdrm_crtc_state->format = ofdrm_crtc_state->format; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return &new_ofdrm_crtc_state->base; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic void ofdrm_crtc_atomic_destroy_state(struct drm_crtc *crtc, 97662306a36Sopenharmony_ci struct drm_crtc_state *crtc_state) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci ofdrm_crtc_state_destroy(to_ofdrm_crtc_state(crtc_state)); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic const struct drm_crtc_funcs ofdrm_crtc_funcs = { 98262306a36Sopenharmony_ci .reset = ofdrm_crtc_reset, 98362306a36Sopenharmony_ci .destroy = drm_crtc_cleanup, 98462306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 98562306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 98662306a36Sopenharmony_ci .atomic_duplicate_state = ofdrm_crtc_atomic_duplicate_state, 98762306a36Sopenharmony_ci .atomic_destroy_state = ofdrm_crtc_atomic_destroy_state, 98862306a36Sopenharmony_ci}; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int ofdrm_connector_helper_get_modes(struct drm_connector *connector) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct ofdrm_device *odev = ofdrm_device_of_dev(connector->dev); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci return drm_connector_helper_get_modes_fixed(connector, &odev->mode); 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs ofdrm_connector_helper_funcs = { 99862306a36Sopenharmony_ci .get_modes = ofdrm_connector_helper_get_modes, 99962306a36Sopenharmony_ci}; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic const struct drm_connector_funcs ofdrm_connector_funcs = { 100262306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 100362306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 100462306a36Sopenharmony_ci .destroy = drm_connector_cleanup, 100562306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 100662306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 100762306a36Sopenharmony_ci}; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic const struct drm_mode_config_funcs ofdrm_mode_config_funcs = { 101062306a36Sopenharmony_ci .fb_create = drm_gem_fb_create_with_dirty, 101162306a36Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 101262306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 101362306a36Sopenharmony_ci}; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci/* 101662306a36Sopenharmony_ci * Init / Cleanup 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_unknown_device_funcs = { 102062306a36Sopenharmony_ci}; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_mach64_device_funcs = { 102362306a36Sopenharmony_ci .cmap_ioremap = ofdrm_mach64_cmap_ioremap, 102462306a36Sopenharmony_ci .cmap_write = ofdrm_mach64_cmap_write, 102562306a36Sopenharmony_ci}; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_rage128_device_funcs = { 102862306a36Sopenharmony_ci .cmap_ioremap = ofdrm_rage128_cmap_ioremap, 102962306a36Sopenharmony_ci .cmap_write = ofdrm_rage128_cmap_write, 103062306a36Sopenharmony_ci}; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_rage_m3a_device_funcs = { 103362306a36Sopenharmony_ci .cmap_ioremap = ofdrm_rage_m3a_cmap_ioremap, 103462306a36Sopenharmony_ci .cmap_write = ofdrm_rage_m3a_cmap_write, 103562306a36Sopenharmony_ci}; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_rage_m3b_device_funcs = { 103862306a36Sopenharmony_ci .cmap_ioremap = ofdrm_rage_m3b_cmap_ioremap, 103962306a36Sopenharmony_ci .cmap_write = ofdrm_rage_m3b_cmap_write, 104062306a36Sopenharmony_ci}; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_radeon_device_funcs = { 104362306a36Sopenharmony_ci .cmap_ioremap = ofdrm_radeon_cmap_ioremap, 104462306a36Sopenharmony_ci .cmap_write = ofdrm_rage128_cmap_write, /* same as Rage128 */ 104562306a36Sopenharmony_ci}; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_gxt2000_device_funcs = { 104862306a36Sopenharmony_ci .cmap_ioremap = ofdrm_gxt2000_cmap_ioremap, 104962306a36Sopenharmony_ci .cmap_write = ofdrm_gxt2000_cmap_write, 105062306a36Sopenharmony_ci}; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_avivo_device_funcs = { 105362306a36Sopenharmony_ci .cmap_ioremap = ofdrm_avivo_cmap_ioremap, 105462306a36Sopenharmony_ci .cmap_write = ofdrm_avivo_cmap_write, 105562306a36Sopenharmony_ci}; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic const struct ofdrm_device_funcs ofdrm_qemu_device_funcs = { 105862306a36Sopenharmony_ci .cmap_ioremap = ofdrm_qemu_cmap_ioremap, 105962306a36Sopenharmony_ci .cmap_write = ofdrm_qemu_cmap_write, 106062306a36Sopenharmony_ci}; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic struct drm_display_mode ofdrm_mode(unsigned int width, unsigned int height) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci /* 106562306a36Sopenharmony_ci * Assume a monitor resolution of 96 dpi to 106662306a36Sopenharmony_ci * get a somewhat reasonable screen size. 106762306a36Sopenharmony_ci */ 106862306a36Sopenharmony_ci const struct drm_display_mode mode = { 106962306a36Sopenharmony_ci DRM_MODE_INIT(60, width, height, 107062306a36Sopenharmony_ci DRM_MODE_RES_MM(width, 96ul), 107162306a36Sopenharmony_ci DRM_MODE_RES_MM(height, 96ul)) 107262306a36Sopenharmony_ci }; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci return mode; 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, 107862306a36Sopenharmony_ci struct platform_device *pdev) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct device_node *of_node = pdev->dev.of_node; 108162306a36Sopenharmony_ci struct ofdrm_device *odev; 108262306a36Sopenharmony_ci struct drm_device *dev; 108362306a36Sopenharmony_ci enum ofdrm_model model; 108462306a36Sopenharmony_ci bool big_endian; 108562306a36Sopenharmony_ci int width, height, depth, linebytes; 108662306a36Sopenharmony_ci const struct drm_format_info *format; 108762306a36Sopenharmony_ci u64 address; 108862306a36Sopenharmony_ci resource_size_t fb_size, fb_base, fb_pgbase, fb_pgsize; 108962306a36Sopenharmony_ci struct resource *res, *mem; 109062306a36Sopenharmony_ci void __iomem *screen_base; 109162306a36Sopenharmony_ci struct drm_plane *primary_plane; 109262306a36Sopenharmony_ci struct drm_crtc *crtc; 109362306a36Sopenharmony_ci struct drm_encoder *encoder; 109462306a36Sopenharmony_ci struct drm_connector *connector; 109562306a36Sopenharmony_ci unsigned long max_width, max_height; 109662306a36Sopenharmony_ci size_t nformats; 109762306a36Sopenharmony_ci int ret; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci odev = devm_drm_dev_alloc(&pdev->dev, drv, struct ofdrm_device, dev); 110062306a36Sopenharmony_ci if (IS_ERR(odev)) 110162306a36Sopenharmony_ci return ERR_CAST(odev); 110262306a36Sopenharmony_ci dev = &odev->dev; 110362306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci ret = ofdrm_device_init_pci(odev); 110662306a36Sopenharmony_ci if (ret) 110762306a36Sopenharmony_ci return ERR_PTR(ret); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * OF display-node settings 111162306a36Sopenharmony_ci */ 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci model = display_get_model_of(dev, of_node); 111462306a36Sopenharmony_ci drm_dbg(dev, "detected model %d\n", model); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci switch (model) { 111762306a36Sopenharmony_ci case OFDRM_MODEL_UNKNOWN: 111862306a36Sopenharmony_ci odev->funcs = &ofdrm_unknown_device_funcs; 111962306a36Sopenharmony_ci break; 112062306a36Sopenharmony_ci case OFDRM_MODEL_MACH64: 112162306a36Sopenharmony_ci odev->funcs = &ofdrm_mach64_device_funcs; 112262306a36Sopenharmony_ci break; 112362306a36Sopenharmony_ci case OFDRM_MODEL_RAGE128: 112462306a36Sopenharmony_ci odev->funcs = &ofdrm_rage128_device_funcs; 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci case OFDRM_MODEL_RAGE_M3A: 112762306a36Sopenharmony_ci odev->funcs = &ofdrm_rage_m3a_device_funcs; 112862306a36Sopenharmony_ci break; 112962306a36Sopenharmony_ci case OFDRM_MODEL_RAGE_M3B: 113062306a36Sopenharmony_ci odev->funcs = &ofdrm_rage_m3b_device_funcs; 113162306a36Sopenharmony_ci break; 113262306a36Sopenharmony_ci case OFDRM_MODEL_RADEON: 113362306a36Sopenharmony_ci odev->funcs = &ofdrm_radeon_device_funcs; 113462306a36Sopenharmony_ci break; 113562306a36Sopenharmony_ci case OFDRM_MODEL_GXT2000: 113662306a36Sopenharmony_ci odev->funcs = &ofdrm_gxt2000_device_funcs; 113762306a36Sopenharmony_ci break; 113862306a36Sopenharmony_ci case OFDRM_MODEL_AVIVO: 113962306a36Sopenharmony_ci odev->funcs = &ofdrm_avivo_device_funcs; 114062306a36Sopenharmony_ci break; 114162306a36Sopenharmony_ci case OFDRM_MODEL_QEMU: 114262306a36Sopenharmony_ci odev->funcs = &ofdrm_qemu_device_funcs; 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci big_endian = display_get_big_endian_of(dev, of_node); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci width = display_get_width_of(dev, of_node); 114962306a36Sopenharmony_ci if (width < 0) 115062306a36Sopenharmony_ci return ERR_PTR(width); 115162306a36Sopenharmony_ci height = display_get_height_of(dev, of_node); 115262306a36Sopenharmony_ci if (height < 0) 115362306a36Sopenharmony_ci return ERR_PTR(height); 115462306a36Sopenharmony_ci depth = display_get_depth_of(dev, of_node); 115562306a36Sopenharmony_ci if (depth < 0) 115662306a36Sopenharmony_ci return ERR_PTR(depth); 115762306a36Sopenharmony_ci linebytes = display_get_linebytes_of(dev, of_node); 115862306a36Sopenharmony_ci if (linebytes < 0) 115962306a36Sopenharmony_ci return ERR_PTR(linebytes); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci format = display_get_validated_format(dev, depth, big_endian); 116262306a36Sopenharmony_ci if (IS_ERR(format)) 116362306a36Sopenharmony_ci return ERR_CAST(format); 116462306a36Sopenharmony_ci if (!linebytes) { 116562306a36Sopenharmony_ci linebytes = drm_format_info_min_pitch(format, 0, width); 116662306a36Sopenharmony_ci if (drm_WARN_ON(dev, !linebytes)) 116762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci fb_size = linebytes * height; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* 117362306a36Sopenharmony_ci * Try to figure out the address of the framebuffer. Unfortunately, Open 117462306a36Sopenharmony_ci * Firmware doesn't provide a standard way to do so. All we can do is a 117562306a36Sopenharmony_ci * dodgy heuristic that happens to work in practice. 117662306a36Sopenharmony_ci * 117762306a36Sopenharmony_ci * On most machines, the "address" property contains what we need, though 117862306a36Sopenharmony_ci * not on Matrox cards found in IBM machines. What appears to give good 117962306a36Sopenharmony_ci * results is to go through the PCI ranges and pick one that encloses the 118062306a36Sopenharmony_ci * "address" property. If none match, we pick the largest. 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ci address = display_get_address_of(dev, of_node); 118362306a36Sopenharmony_ci if (address != OF_BAD_ADDR) { 118462306a36Sopenharmony_ci struct resource fb_res = DEFINE_RES_MEM(address, fb_size); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci res = ofdrm_find_fb_resource(odev, &fb_res); 118762306a36Sopenharmony_ci if (!res) 118862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 118962306a36Sopenharmony_ci if (resource_contains(res, &fb_res)) 119062306a36Sopenharmony_ci fb_base = address; 119162306a36Sopenharmony_ci else 119262306a36Sopenharmony_ci fb_base = res->start; 119362306a36Sopenharmony_ci } else { 119462306a36Sopenharmony_ci struct resource fb_res = DEFINE_RES_MEM(0u, fb_size); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci res = ofdrm_find_fb_resource(odev, &fb_res); 119762306a36Sopenharmony_ci if (!res) 119862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 119962306a36Sopenharmony_ci fb_base = res->start; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* 120362306a36Sopenharmony_ci * I/O resources 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci fb_pgbase = round_down(fb_base, PAGE_SIZE); 120762306a36Sopenharmony_ci fb_pgsize = fb_base - fb_pgbase + round_up(fb_size, PAGE_SIZE); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci ret = devm_aperture_acquire_from_firmware(dev, fb_pgbase, fb_pgsize); 121062306a36Sopenharmony_ci if (ret) { 121162306a36Sopenharmony_ci drm_err(dev, "could not acquire memory range %pr: error %d\n", &res, ret); 121262306a36Sopenharmony_ci return ERR_PTR(ret); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci mem = devm_request_mem_region(&pdev->dev, fb_pgbase, fb_pgsize, drv->name); 121662306a36Sopenharmony_ci if (!mem) { 121762306a36Sopenharmony_ci drm_warn(dev, "could not acquire memory region %pr\n", &res); 121862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); 122262306a36Sopenharmony_ci if (!screen_base) 122362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (odev->funcs->cmap_ioremap) { 122662306a36Sopenharmony_ci void __iomem *cmap_base = odev->funcs->cmap_ioremap(odev, of_node, fb_base); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (IS_ERR(cmap_base)) { 122962306a36Sopenharmony_ci /* Don't fail; continue without colormap */ 123062306a36Sopenharmony_ci drm_warn(dev, "could not find colormap: error %ld\n", PTR_ERR(cmap_base)); 123162306a36Sopenharmony_ci } else { 123262306a36Sopenharmony_ci odev->cmap_base = cmap_base; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* 123762306a36Sopenharmony_ci * Firmware framebuffer 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci iosys_map_set_vaddr_iomem(&odev->screen_base, screen_base); 124162306a36Sopenharmony_ci odev->mode = ofdrm_mode(width, height); 124262306a36Sopenharmony_ci odev->format = format; 124362306a36Sopenharmony_ci odev->pitch = linebytes; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&odev->mode)); 124662306a36Sopenharmony_ci drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, linebytes=%d byte\n", 124762306a36Sopenharmony_ci &format->format, width, height, linebytes); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* 125062306a36Sopenharmony_ci * Mode-setting pipeline 125162306a36Sopenharmony_ci */ 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci ret = drmm_mode_config_init(dev); 125462306a36Sopenharmony_ci if (ret) 125562306a36Sopenharmony_ci return ERR_PTR(ret); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH); 125862306a36Sopenharmony_ci max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci dev->mode_config.min_width = width; 126162306a36Sopenharmony_ci dev->mode_config.max_width = max_width; 126262306a36Sopenharmony_ci dev->mode_config.min_height = height; 126362306a36Sopenharmony_ci dev->mode_config.max_height = max_height; 126462306a36Sopenharmony_ci dev->mode_config.funcs = &ofdrm_mode_config_funcs; 126562306a36Sopenharmony_ci dev->mode_config.preferred_depth = format->depth; 126662306a36Sopenharmony_ci dev->mode_config.quirk_addfb_prefer_host_byte_order = true; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci /* Primary plane */ 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci nformats = drm_fb_build_fourcc_list(dev, &format->format, 1, 127162306a36Sopenharmony_ci odev->formats, ARRAY_SIZE(odev->formats)); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci primary_plane = &odev->primary_plane; 127462306a36Sopenharmony_ci ret = drm_universal_plane_init(dev, primary_plane, 0, &ofdrm_primary_plane_funcs, 127562306a36Sopenharmony_ci odev->formats, nformats, 127662306a36Sopenharmony_ci ofdrm_primary_plane_format_modifiers, 127762306a36Sopenharmony_ci DRM_PLANE_TYPE_PRIMARY, NULL); 127862306a36Sopenharmony_ci if (ret) 127962306a36Sopenharmony_ci return ERR_PTR(ret); 128062306a36Sopenharmony_ci drm_plane_helper_add(primary_plane, &ofdrm_primary_plane_helper_funcs); 128162306a36Sopenharmony_ci drm_plane_enable_fb_damage_clips(primary_plane); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* CRTC */ 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci crtc = &odev->crtc; 128662306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, 128762306a36Sopenharmony_ci &ofdrm_crtc_funcs, NULL); 128862306a36Sopenharmony_ci if (ret) 128962306a36Sopenharmony_ci return ERR_PTR(ret); 129062306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &ofdrm_crtc_helper_funcs); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (odev->cmap_base) { 129362306a36Sopenharmony_ci drm_mode_crtc_set_gamma_size(crtc, OFDRM_GAMMA_LUT_SIZE); 129462306a36Sopenharmony_ci drm_crtc_enable_color_mgmt(crtc, 0, false, OFDRM_GAMMA_LUT_SIZE); 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci /* Encoder */ 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci encoder = &odev->encoder; 130062306a36Sopenharmony_ci ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE); 130162306a36Sopenharmony_ci if (ret) 130262306a36Sopenharmony_ci return ERR_PTR(ret); 130362306a36Sopenharmony_ci encoder->possible_crtcs = drm_crtc_mask(crtc); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* Connector */ 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci connector = &odev->connector; 130862306a36Sopenharmony_ci ret = drm_connector_init(dev, connector, &ofdrm_connector_funcs, 130962306a36Sopenharmony_ci DRM_MODE_CONNECTOR_Unknown); 131062306a36Sopenharmony_ci if (ret) 131162306a36Sopenharmony_ci return ERR_PTR(ret); 131262306a36Sopenharmony_ci drm_connector_helper_add(connector, &ofdrm_connector_helper_funcs); 131362306a36Sopenharmony_ci drm_connector_set_panel_orientation_with_quirk(connector, 131462306a36Sopenharmony_ci DRM_MODE_PANEL_ORIENTATION_UNKNOWN, 131562306a36Sopenharmony_ci width, height); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci ret = drm_connector_attach_encoder(connector, encoder); 131862306a36Sopenharmony_ci if (ret) 131962306a36Sopenharmony_ci return ERR_PTR(ret); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci drm_mode_config_reset(dev); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci return odev; 132462306a36Sopenharmony_ci} 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci/* 132762306a36Sopenharmony_ci * DRM driver 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ciDEFINE_DRM_GEM_FOPS(ofdrm_fops); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic struct drm_driver ofdrm_driver = { 133362306a36Sopenharmony_ci DRM_GEM_SHMEM_DRIVER_OPS, 133462306a36Sopenharmony_ci .name = DRIVER_NAME, 133562306a36Sopenharmony_ci .desc = DRIVER_DESC, 133662306a36Sopenharmony_ci .date = DRIVER_DATE, 133762306a36Sopenharmony_ci .major = DRIVER_MAJOR, 133862306a36Sopenharmony_ci .minor = DRIVER_MINOR, 133962306a36Sopenharmony_ci .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, 134062306a36Sopenharmony_ci .fops = &ofdrm_fops, 134162306a36Sopenharmony_ci}; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci/* 134462306a36Sopenharmony_ci * Platform driver 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic int ofdrm_probe(struct platform_device *pdev) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct ofdrm_device *odev; 135062306a36Sopenharmony_ci struct drm_device *dev; 135162306a36Sopenharmony_ci unsigned int color_mode; 135262306a36Sopenharmony_ci int ret; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci odev = ofdrm_device_create(&ofdrm_driver, pdev); 135562306a36Sopenharmony_ci if (IS_ERR(odev)) 135662306a36Sopenharmony_ci return PTR_ERR(odev); 135762306a36Sopenharmony_ci dev = &odev->dev; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci ret = drm_dev_register(dev, 0); 136062306a36Sopenharmony_ci if (ret) 136162306a36Sopenharmony_ci return ret; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci color_mode = drm_format_info_bpp(odev->format, 0); 136462306a36Sopenharmony_ci if (color_mode == 16) 136562306a36Sopenharmony_ci color_mode = odev->format->depth; // can be 15 or 16 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci drm_fbdev_generic_setup(dev, color_mode); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci return 0; 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_cistatic void ofdrm_remove(struct platform_device *pdev) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci struct drm_device *dev = platform_get_drvdata(pdev); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci drm_dev_unplug(dev); 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic const struct of_device_id ofdrm_of_match_display[] = { 138062306a36Sopenharmony_ci { .compatible = "display", }, 138162306a36Sopenharmony_ci { }, 138262306a36Sopenharmony_ci}; 138362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ofdrm_of_match_display); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_cistatic struct platform_driver ofdrm_platform_driver = { 138662306a36Sopenharmony_ci .driver = { 138762306a36Sopenharmony_ci .name = "of-display", 138862306a36Sopenharmony_ci .of_match_table = ofdrm_of_match_display, 138962306a36Sopenharmony_ci }, 139062306a36Sopenharmony_ci .probe = ofdrm_probe, 139162306a36Sopenharmony_ci .remove_new = ofdrm_remove, 139262306a36Sopenharmony_ci}; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cimodule_platform_driver(ofdrm_platform_driver); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 139762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1398