/** * adf_fbdev_init - initialize helper to wrap ADF device in fbdev API * * @fbdev: the fbdev helper * @interface: the ADF interface that will display the framebuffer * @eng: the ADF overlay engine that will scan out the framebuffer * @xres_virtual: the virtual width of the framebuffer * @yres_virtual: the virtual height of the framebuffer * @format: the format of the framebuffer * @fbops: the device's fbdev ops * @fmt: formatting for the framebuffer identification string * @...: variable arguments * * @format must be a standard, non-indexed RGB format, i.e., * adf_format_is_rgb(@format) && @format != @DRM_FORMAT_C8. * * Returns 0 on success or -errno on failure. */ int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface, struct adf_overlay_engine *eng, u16 xres_virtual, u16 yres_virtual, u32 format, struct fb_ops *fbops, const char *fmt, ...) { struct adf_device *parent = adf_interface_parent(interface); struct device *dev = &parent->base.dev; u16 width_mm, height_mm; va_list args; int ret; if (!adf_format_is_rgb(format) || format == DRM_FORMAT_C8) { dev_err(dev, "fbdev helper does not support format %u\n", format); return -EINVAL; } memset(fbdev, 0, sizeof(*fbdev)); fbdev->intf = interface; fbdev->eng = eng; fbdev->info = framebuffer_alloc(0, dev); if (!fbdev->info) { dev_err(dev, "allocating framebuffer device failed\n"); return -ENOMEM; } mutex_init(&fbdev->refcount_lock); fbdev->default_xres_virtual = xres_virtual; fbdev->default_yres_virtual = yres_virtual; fbdev->default_format = format; fbdev->info->flags = FBINFO_FLAG_DEFAULT; ret = adf_interface_get_screen_size(interface, &width_mm, &height_mm); if (ret < 0) { width_mm = 0; height_mm = 0; } fbdev->info->var.width = width_mm; fbdev->info->var.height = height_mm; fbdev->info->var.activate = FB_ACTIVATE_VBL; va_start(args, fmt); vsnprintf(fbdev->info->fix.id, sizeof(fbdev->info->fix.id), fmt, args); va_end(args); fbdev->info->fix.type = FB_TYPE_PACKED_PIXELS; fbdev->info->fix.visual = FB_VISUAL_TRUECOLOR; fbdev->info->fix.xpanstep = 1; fbdev->info->fix.ypanstep = 1; INIT_LIST_HEAD(&fbdev->info->modelist); fbdev->info->fbops = fbops; fbdev->info->pseudo_palette = fbdev->pseudo_palette; fbdev->info->par = fbdev; ret = register_framebuffer(fbdev->info); if (ret < 0) { dev_err(dev, "registering framebuffer failed: %d\n", ret); return ret; } return 0; }
/** * adf_interface_simple_post - flip to a single buffer * * @intf: interface targeted by the flip * @buf: buffer to display * * adf_interface_simple_post() can be used generically for simple display * configurations, since the client does not need to provide any driver-private * configuration data. * * adf_interface_simple_post() has the same copying semantics as * adf_device_post(). * * On success, returns a sync fence which signals when the buffer is removed * from the screen. On failure, returns ERR_PTR(-errno). */ struct sync_fence *adf_interface_simple_post(struct adf_interface *intf, struct adf_buffer *buf) { size_t custom_data_size = 0; void *custom_data = NULL; struct sync_fence *ret; if (intf->ops && intf->ops->describe_simple_post) { int err; custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL); if (!custom_data) { ret = ERR_PTR(-ENOMEM); goto done; } err = intf->ops->describe_simple_post(intf, buf, custom_data, &custom_data_size); if (err < 0) { ret = ERR_PTR(err); goto done; } } ret = adf_device_post(adf_interface_parent(intf), &intf, 1, buf, 1, custom_data, custom_data_size); done: kfree(custom_data); return ret; }
/** * adf_interface_current_mode - get interface's current display mode * * @intf: the interface * @mode: returns the current mode */ void adf_interface_current_mode(struct adf_interface *intf, struct drm_mode_modeinfo *mode) { struct adf_device *dev = adf_interface_parent(intf); mutex_lock(&dev->client_lock); memcpy(mode, &intf->current_mode, sizeof(*mode)); mutex_unlock(&dev->client_lock); }
/** * adf_interface_blank - get interface's current DPMS state * * @intf: the interface * * Returns one of %DRM_MODE_DPMS_*. */ u8 adf_interface_dpms_state(struct adf_interface *intf) { struct adf_device *dev = adf_interface_parent(intf); u8 dpms_state; mutex_lock(&dev->client_lock); dpms_state = intf->dpms_state; mutex_unlock(&dev->client_lock); return dpms_state; }
/** * adf_fbdev_open - default implementation of fbdev open op */ int adf_fbdev_open(struct fb_info *info, int user) { struct adf_fbdev *fbdev = info->par; int ret; mutex_lock(&fbdev->refcount_lock); if (unlikely(fbdev->refcount == UINT_MAX)) { ret = -EMFILE; goto done; } if (!fbdev->refcount) { struct drm_mode_modeinfo mode; struct fb_videomode fbmode; struct adf_device *dev = adf_interface_parent(fbdev->intf); ret = adf_device_attach(dev, fbdev->eng, fbdev->intf); if (ret < 0 && ret != -EALREADY) goto done; ret = adf_fb_alloc(fbdev); if (ret < 0) goto done; adf_interface_current_mode(fbdev->intf, &mode); adf_modeinfo_to_fb_videomode(&mode, &fbmode); fb_videomode_to_var(&fbdev->info->var, &fbmode); adf_fbdev_set_format(fbdev, fbdev->default_format); adf_fbdev_fill_modelist(fbdev); } if (!fbdev_opened_once) { fbdev_opened_once = true; } else { ret = adf_fbdev_post(fbdev); if (ret < 0) { if (!fbdev->refcount) adf_fb_destroy(fbdev); goto done; } } fbdev->refcount++; done: mutex_unlock(&fbdev->refcount_lock); return ret; }
/** * adf_interface_screen_size - get size of screen connected to interface * * @intf: the interface * @width_mm: returns the screen width in mm * @height_mm: returns the screen width in mm * * Returns 0 on success or -errno on failure. */ int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width_mm, u16 *height_mm) { struct adf_device *dev = adf_interface_parent(intf); int ret; if (!intf->ops || !intf->ops->screen_size) return -EOPNOTSUPP; mutex_lock(&dev->client_lock); ret = intf->ops->screen_size(intf, width_mm, height_mm); mutex_unlock(&dev->client_lock); return ret; }
/** * adf_interface_set_mode - set interface's display mode * * @intf: the interface * @mode: the new mode * * Returns 0 on success or -errno on failure. */ int adf_interface_set_mode(struct adf_interface *intf, struct drm_mode_modeinfo *mode) { struct adf_device *dev = adf_interface_parent(intf); int ret = 0; if (!intf->ops || !intf->ops->modeset) return -EOPNOTSUPP; mutex_lock(&dev->client_lock); flush_kthread_worker(&dev->post_worker); ret = intf->ops->modeset(intf, mode); if (ret < 0) goto done; memcpy(&intf->current_mode, mode, sizeof(*mode)); done: mutex_unlock(&dev->client_lock); return ret; }
/** * adf_fbdev_open - default implementation of fbdev open op */ int adf_fbdev_open(struct fb_info *info, int user) { struct adf_fbdev *fbdev = info->par; int ret; if (!fbdev->open) { struct drm_mode_modeinfo mode; struct fb_videomode fbmode; struct adf_device *dev = adf_interface_parent(fbdev->intf); ret = adf_device_attach(dev, fbdev->eng, fbdev->intf); if (ret < 0 && ret != -EALREADY) return ret; ret = adf_fb_alloc(fbdev); if (ret < 0) return ret; adf_interface_current_mode(fbdev->intf, &mode); adf_modeinfo_to_fb_videomode(&mode, &fbmode); fb_videomode_to_var(&fbdev->info->var, &fbmode); adf_fbdev_set_format(fbdev, fbdev->default_format); adf_fbdev_fill_modelist(fbdev); } ret = adf_fbdev_post(fbdev); if (ret < 0) { if (!fbdev->open) adf_fb_destroy(fbdev); return ret; } fbdev->open = true; return 0; }
/** * adf_interface_blank - set interface's DPMS state * * @intf: the interface * @state: one of %DRM_MODE_DPMS_* * * Returns 0 on success or -errno on failure. */ int adf_interface_blank(struct adf_interface *intf, u8 state) { struct adf_device *dev = adf_interface_parent(intf); u8 prev_state; bool disable_vsync; bool enable_vsync; int ret = 0; struct adf_event_refcount *vsync_refcount; if (!intf->ops || !intf->ops->blank) return -EOPNOTSUPP; if (state > DRM_MODE_DPMS_OFF) return -EINVAL; mutex_lock(&dev->client_lock); if (state != DRM_MODE_DPMS_ON) flush_kthread_worker(&dev->post_worker); mutex_lock(&intf->base.event_lock); vsync_refcount = adf_obj_find_event_refcount(&intf->base, ADF_EVENT_VSYNC); if (!vsync_refcount) { ret = -ENOMEM; goto done; } prev_state = intf->dpms_state; if (prev_state == state) { ret = -EBUSY; goto done; } disable_vsync = vsync_active(prev_state) && !vsync_active(state) && vsync_refcount->refcount; enable_vsync = !vsync_active(prev_state) && vsync_active(state) && vsync_refcount->refcount; if (disable_vsync) intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, false); ret = intf->ops->blank(intf, state); if (ret < 0) { if (disable_vsync) intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, true); goto done; } if (enable_vsync) intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, true); intf->dpms_state = state; done: mutex_unlock(&intf->base.event_lock); mutex_unlock(&dev->client_lock); return ret; }