From mboxrd@z Thu Jan 1 00:00:00 1970 From: Konrad Rzeszutek Wilk Subject: Re: [PATCH] RFCv2: omapdrm DRM/KMS driver for TI OMAP platforms Date: Wed, 21 Sep 2011 18:32:10 -0400 Message-ID: <20110921223210.GA26166@phenom.oracle.com> References: <1316295129-3600-1-git-send-email-rob.clark@linaro.org> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: Received: from acsinet15.oracle.com (acsinet15.oracle.com [141.146.126.227]) by gabe.freedesktop.org (Postfix) with ESMTP id DC9519ECFE for ; Wed, 21 Sep 2011 15:32:17 -0700 (PDT) Content-Disposition: inline In-Reply-To: <1316295129-3600-1-git-send-email-rob.clark@linaro.org> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: dri-devel-bounces+sf-dri-devel=m.gmane.org@lists.freedesktop.org Errors-To: dri-devel-bounces+sf-dri-devel=m.gmane.org@lists.freedesktop.org To: Rob Clark Cc: Inki Dae , Rob Clark , dri-devel@lists.freedesktop.org, patches@linaro.org List-Id: dri-devel@lists.freedesktop.org So I did a cursory look and stopped at omap_drv.h due to the lack of time. Some comments below. > +++ b/drivers/staging/omapdrm/Kconfig > @@ -0,0 +1,24 @@ > + > +config DRM_OMAP > + tristate "OMAP DRM (EXPERIMENTAL)" > + depends on DRM && !CONFIG_FB_OMAP2 Since it says EXPERIMENTAL should it depend on that config option as well? The CONFIG_EXPERIMENTAL one. > + select DRM_KMS_HELPER > + select OMAP2_DSS > + select FB_SYS_FILLRECT > + select FB_SYS_COPYAREA > + select FB_SYS_IMAGEBLIT > + select FB_SYS_FOPS > + default n > + help > + DRM display driver for OMAP2/3/4 based boards. > + > +config DRM_OMAP_NUM_CRTCS > + int "Number of CRTCs" > + range 1 10 > + default 1 if ARCH_OMAP2 || ARCH_OMAP3 > + default 2 if ARCH_OMAP4 > + depends on DRM_OMAP > + help > + Select the number of video overlays which can be used as framebuffers. > + The remaining overlays are reserved for video. > + ..snip.. > +void omap_connector_dpms(struct drm_connector *connector, int mode) > +{ > + struct omap_connector *omap_connector = to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + int ret; > + > + DBG("%s: %d", dssdev->name, mode); > + > + if (mode == DRM_MODE_DPMS_ON) { > + /* store resume info for suspended displays */ > + switch (dssdev->state) { > + case OMAP_DSS_DISPLAY_SUSPENDED: > + dssdev->activate_after_resume = true; > + break; > + case OMAP_DSS_DISPLAY_DISABLED: Move the 'int ret' to here since you only seem to be using it within this code logic. > + ret = dssdev->driver->enable(dssdev); > + if (ret) { > + DBG("%s: failed to enable: %d", dssdev->name, ret); > + dssdev->driver->disable(dssdev); > + } > + break; > + default: > + break; > + } > + } else { > + /* TODO */ > + } > +} > + .. snip.. > +#define MAX_EDID 512 > + > +static int omap_connector_get_modes(struct drm_connector *connector) > +{ > + struct omap_connector *omap_connector = to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + struct drm_device *dev = connector->dev; > + int n = 0; > + > + DBG("%s", omap_connector->dssdev->name); > + > + /* if display exposes EDID, then we parse that in the normal way to > + * build table of supported modes.. otherwise (ie. fixed resolution > + * LCD panels) we just return a single mode corresponding to the > + * currently configured timings: > + */ > + if (dssdrv->read_edid) { > + void *edid = kzalloc(MAX_EDID, GFP_KERNEL); > + > + if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) && > + drm_edid_is_valid(edid)) { > + drm_mode_connector_update_edid_property(connector, edid); > + n = drm_add_edid_modes(connector, edid); > + kfree(connector->display_info.raw_edid); > + connector->display_info.raw_edid = edid; > + } else { > + drm_mode_connector_update_edid_property(connector, NULL); > + connector->display_info.raw_edid = NULL; > + kfree(edid); > + } > + } else { > + struct drm_display_mode *mode = drm_mode_create(dev); > + struct omap_video_timings timings; > + > + dssdrv->get_timings(dssdev, &timings); > + > + copy_timings_omap_to_drm(mode, &timings); > + > + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; > + drm_mode_set_name(mode); > + drm_mode_probed_add(connector, mode); > + > + n = 1; > + } > + > + return n; > +} > + > +static int omap_connector_mode_valid(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct omap_connector *omap_connector = to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + struct omap_video_timings timings = {0}; > + struct drm_device *dev = connector->dev; > + struct drm_display_mode *new_mode; > + int ret = MODE_BAD; > + > + copy_timings_drm_to_omap(&timings, mode); > + mode->vrefresh = drm_mode_vrefresh(mode); > + > + if (!dssdrv->check_timings(dssdev, &timings)) { > + /* check if vrefresh is still valid */ > + new_mode = drm_mode_duplicate(dev, mode); > + new_mode->clock = timings.pixel_clock; > + new_mode->vrefresh = 0; > + if (mode->vrefresh == drm_mode_vrefresh(new_mode)) > + ret = MODE_OK; > + drm_mode_destroy(dev, new_mode); > + } > + > + DBG("connector: mode %s: " > + "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", > + (ret == MODE_OK) ? "valid" : "invalid", > + mode->base.id, mode->name, mode->vrefresh, mode->clock, > + mode->hdisplay, mode->hsync_start, > + mode->hsync_end, mode->htotal, > + mode->vdisplay, mode->vsync_start, > + mode->vsync_end, mode->vtotal, mode->type, mode->flags); > + > + return ret; > +} > + > +struct drm_encoder * omap_connector_attached_encoder( > + struct drm_connector *connector) > +{ > + int i; > + struct omap_connector *omap_connector = to_omap_connector(connector); > + > + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { > + struct drm_mode_object *obj; > + > + if (connector->encoder_ids[i] == 0) > + break; > + > + obj = drm_mode_object_find(connector->dev, > + connector->encoder_ids[i], > + DRM_MODE_OBJECT_ENCODER); > + > + if (obj) { > + struct drm_encoder *encoder = obj_to_encoder(obj); > + struct omap_overlay_manager *mgr = > + omap_encoder_get_manager(encoder); > + DBG("%s: found %s", omap_connector->dssdev->name, > + mgr->name); > + return encoder; > + } > + } > + > + DBG("%s: no encoder", omap_connector->dssdev->name); > + > + return NULL; > +} > + > +static const struct drm_connector_funcs omap_connector_funcs = { > + .dpms = drm_helper_connector_dpms, > + .detect = omap_connector_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = omap_connector_destroy, > +}; > + > +static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { > + .get_modes = omap_connector_get_modes, > + .mode_valid = omap_connector_mode_valid, > + .best_encoder = omap_connector_attached_encoder, > +}; > + > +/* called from encoder when mode is set, to propagate settings to the dssdev */ > +void omap_connector_mode_set(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct drm_device *dev = connector->dev; > + struct omap_connector *omap_connector = to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + struct omap_video_timings timings; > + > + copy_timings_drm_to_omap(&timings, mode); > + > + DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", > + omap_connector->dssdev->name, > + mode->base.id, mode->name, mode->vrefresh, mode->clock, > + mode->hdisplay, mode->hsync_start, > + mode->hsync_end, mode->htotal, > + mode->vdisplay, mode->vsync_start, > + mode->vsync_end, mode->vtotal, mode->type, mode->flags); > + > + if (dssdrv->check_timings(dssdev, &timings)) { > + dev_err(dev->dev, "could not set timings\n"); > + return; > + } > + > + dssdrv->set_timings(dssdev, &timings); > +} > + > +/* flush an area of the framebuffer (in case of manual update display that > + * is not automatically flushed) > + */ > +void omap_connector_flush(struct drm_connector *connector, > + int x, int y, int w, int h) > +{ > + struct omap_connector *omap_connector = to_omap_connector(connector); > + > + /* TODO: enable when supported in dss */ > + VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h); > +} > + > +/* initialize connector */ > +struct drm_connector * omap_connector_init(struct drm_device *dev, > + int connector_type, struct omap_dss_device *dssdev) > +{ > + struct drm_connector *connector = NULL; > + struct omap_connector *omap_connector; > + > + DBG("%s", dssdev->name); > + > + omap_dss_get_device(dssdev); > + > + omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL); > + if (!omap_connector) { > + dev_err(dev->dev, "could not allocate connector\n"); > + goto fail; > + } > + > + omap_connector->dssdev = dssdev; > + connector = &omap_connector->base; > + > + drm_connector_init(dev, connector, &omap_connector_funcs, > + connector_type); > + drm_connector_helper_add(connector, &omap_connector_helper_funcs); > + > +#if 0 /* enable when dss2 supports hotplug */ > + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD) > + connector->polled = 0; > + else > +#endif > + connector->polled = DRM_CONNECTOR_POLL_CONNECT | > + DRM_CONNECTOR_POLL_DISCONNECT; > + > + connector->interlace_allowed = 1; > + connector->doublescan_allowed = 0; > + > + drm_sysfs_connector_add(connector); > + > + return connector; > + > +fail: > + if (connector) { > + omap_connector_destroy(connector); > + } > + > + return NULL; > +} > diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c > new file mode 100644 > index 0000000..7da36ba > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_crtc.c > @@ -0,0 +1,332 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_crtc.c Remove the linux part > + * > + * Copyright (C) 2011 Texas Instruments > + * Author: Rob Clark > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License version 2 as published by > + * the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see . > + */ > + > +#include "omap_drv.h" > + > +#include "drm_mode.h" > +#include "drm_crtc.h" > +#include "drm_crtc_helper.h" > + > +#define to_omap_crtc(x) container_of(x, struct omap_crtc, base) > + > +struct omap_crtc { > + struct drm_crtc base; > + struct omap_overlay *ovl; Where is this defined? > + struct omap_overlay_info info; > + int id; > + > + /* if there is a pending flip, this will be non-null: */ > + struct drm_pending_vblank_event *event; > +}; > + > +/* push changes down to dss2 */ > +static int commit(struct drm_crtc *crtc) > +{ > + struct drm_device *dev = crtc->dev; > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + struct omap_overlay *ovl = omap_crtc->ovl; > + struct omap_overlay_info *info = &omap_crtc->info; > + int ret; > + > + DBG("%s", omap_crtc->ovl->name); > + DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, > + info->out_height, info->screen_width); > + DBG("%d,%d %p %08x", info->pos_x, info->pos_y, info->vaddr, > + info->paddr); > + > + /* NOTE: do we want to do this at all here, or just wait > + * for dpms(ON) since other CRTC's may not have their mode > + * set yet, so fb dimensions may still change.. > + */ > + ret = ovl->set_overlay_info(ovl, info); Any chance that olv->set_overlayinfo could be NULL? > + if (ret) { > + dev_err(dev->dev, "could not set overlay info\n"); > + return ret; > + } > + > + /* our encoder doesn't necessarily get a commit() after this, in > + * particular in the dpms() and mode_set_base() cases, so force the > + * manager to update: > + * > + * could this be in the encoder somehow? > + */ > + if (ovl->manager) { > + ret = ovl->manager->apply(ovl->manager); > + if (ret) { > + dev_err(dev->dev, "could not apply\n"); Make that more verbose so that in the field you have an idea of what it could not apply. > + return ret; > + } > + } > + > + if (info->enabled) { > + omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y, > + crtc->fb->width, crtc->fb->height); > + } > + > + return 0; > +} > + > +/* update parameters that are dependent on the framebuffer dimensions and > + * position within the fb that this crtc scans out from. This is called > + * when framebuffer dimensions or x,y base may have changed, either due > + * to our mode, or a change in another crtc that is scanning out of the > + * same fb. > + */ > +static void update_scanout(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + dma_addr_t paddr; > + void __iomem *vaddr; > + int screen_width; Not unsigned int? Can widths become negative? > + > + omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, > + &vaddr, &paddr, &screen_width); > + > + DBG("%s: %d,%d: %p %08x (%d)", omap_crtc->ovl->name, > + crtc->x, crtc->y, vaddr, (u32)paddr, screen_width); So.. never going to run this on a box with more than 4GB? Perhaps the (u32) casting should be dropped. Does it make sense to report also the return value from omap_framebuffer_get_buffer? > + > + omap_crtc->info.paddr = paddr; > + omap_crtc->info.vaddr = vaddr; > + omap_crtc->info.screen_width = screen_width; > +} > + > +static void omap_crtc_gamma_set(struct drm_crtc *crtc, > + u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > +} > + > +static void omap_crtc_destroy(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > + drm_crtc_cleanup(crtc); > + kfree(omap_crtc); > +} > + > +static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%s: %d", omap_crtc->ovl->name, mode); > + > + if (mode == DRM_MODE_DPMS_ON) { > + update_scanout(crtc); > + omap_crtc->info.enabled = true; > + } else { > + omap_crtc->info.enabled = false; > + } > + > + commit(crtc); So no checking of its return value.. Should you at least wrap it with WARN_ON(?) > +} > + > +static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > + return true; > +} > + > +static int omap_crtc_mode_set(struct drm_crtc *crtc, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode, > + int x, int y, > + struct drm_framebuffer *old_fb) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%s: %d,%d: %dx%d",omap_crtc->ovl->name, x, y, > + mode->hdisplay, mode->vdisplay); > + > + /* just use adjusted mode */ > + mode = adjusted_mode; > + > + omap_crtc->info.width = mode->hdisplay; > + omap_crtc->info.height = mode->vdisplay; > + omap_crtc->info.out_width = mode->hdisplay; > + omap_crtc->info.out_height = mode->vdisplay; > + omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U; > + omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA; > + omap_crtc->info.rotation = OMAP_DSS_ROT_0; > + omap_crtc->info.global_alpha = 0xff; > + omap_crtc->info.mirror = 0; > + omap_crtc->info.mirror = 0; > + omap_crtc->info.pos_x = 0; > + omap_crtc->info.pos_y = 0; > +#if 0 /* re-enable when these are available in DSS2 driver */ > + omap_crtc->info.zorder = 3; /* GUI in the front, video behind */ > + omap_crtc->info.min_x_decim = 1; > + omap_crtc->info.max_x_decim = 1; > + omap_crtc->info.min_y_decim = 1; > + omap_crtc->info.max_y_decim = 1; > +#endif > + > + update_scanout(crtc); > + > + return 0; > +} > + > +static void omap_crtc_prepare(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + struct omap_overlay *ovl = omap_crtc->ovl; > + > + DBG("%s", omap_crtc->ovl->name); > + > + ovl->get_overlay_info(ovl, &omap_crtc->info); > + > + omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); > +} > + > +static void omap_crtc_commit(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > + omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); > +} > + > +static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, > + struct drm_framebuffer *old_fb) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb); > + > + update_scanout(crtc); > + > + return commit(crtc); > +} > + > +static void omap_crtc_load_lut(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > +} > + > +static void page_flip_cb(void *arg) > +{ > + struct drm_crtc *crtc = arg; > + struct drm_device *dev = crtc->dev; > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + struct drm_pending_vblank_event *event = omap_crtc->event; > + struct timeval now; > + unsigned long flags; > + > + WARN_ON(!event); > + > + omap_crtc->event = NULL; > + > + update_scanout(crtc); > + commit(crtc); > + > + /* wakeup userspace */ > + // TODO: this should happen *after* flip.. somehow.. > + if (event) { > + spin_lock_irqsave(&dev->event_lock, flags); So this can be called from an IRQ handler? Why the need to disable the IRQs? Can you just use spin_lock? > + event->event.sequence = > + drm_vblank_count_and_time(dev, omap_crtc->id, &now); > + event->event.tv_sec = now.tv_sec; > + event->event.tv_usec = now.tv_usec; > + list_add_tail(&event->base.link, > + &event->base.file_priv->event_list); > + wake_up_interruptible(&event->base.file_priv->event_wait); > + spin_unlock_irqrestore(&dev->event_lock, flags); > + } > +} > + > +static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, > + struct drm_framebuffer *fb, > + struct drm_pending_vblank_event *event) > +{ > + struct drm_device *dev = crtc->dev; > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id); > + > + if (omap_crtc->event) { > + dev_err(dev->dev, "already a pending flip\n"); > + return -EINVAL; > + } > + > + crtc->fb = fb; > + omap_crtc->event = event; > + > + omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, > + page_flip_cb, crtc); > + > + return 0; > +} > + > +static const struct drm_crtc_funcs omap_crtc_funcs = { > + .gamma_set = omap_crtc_gamma_set, > + .set_config = drm_crtc_helper_set_config, > + .destroy = omap_crtc_destroy, > + .page_flip = omap_crtc_page_flip_locked, > +}; > + > +static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { > + .dpms = omap_crtc_dpms, > + .mode_fixup = omap_crtc_mode_fixup, > + .mode_set = omap_crtc_mode_set, > + .prepare = omap_crtc_prepare, > + .commit = omap_crtc_commit, > + .mode_set_base = omap_crtc_mode_set_base, > + .load_lut = omap_crtc_load_lut, > +}; > + > +struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + return omap_crtc->ovl; > +} > + > +/* initialize crtc */ > +struct drm_crtc * omap_crtc_init(struct drm_device *dev, > + struct omap_overlay *ovl, int id) > +{ > + struct drm_crtc *crtc = NULL; > + struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); > + > + DBG("%s", ovl->name); > + > + if (!omap_crtc) { > + dev_err(dev->dev, "could not allocate CRTC\n"); > + goto fail; > + } > + > + omap_crtc->ovl = ovl; > + omap_crtc->id = id; > + crtc = &omap_crtc->base; > + drm_crtc_init(dev, crtc, &omap_crtc_funcs); > + drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); > + > + return crtc; > + > +fail: > + if (crtc) { > + drm_crtc_cleanup(crtc); > + kfree(omap_crtc); > + } > + return NULL; > +} > diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c > new file mode 100644 > index 0000000..88209f4 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.c > @@ -0,0 +1,766 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_drv.c Remoev the linux > + * > + * Copyright (C) 2011 Texas Instruments > + * Author: Rob Clark > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License version 2 as published by > + * the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see . > + */ > + > +#include "omap_drv.h" > + > +#include "drm_crtc_helper.h" > +#include "drm_fb_helper.h" > + > +#define DRIVER_NAME MODULE_NAME > +#define DRIVER_DESC "OMAP DRM" > +#define DRIVER_DATE "20110917" > +#define DRIVER_MAJOR 1 > +#define DRIVER_MINOR 0 > +#define DRIVER_PATCHLEVEL 0 > + > +struct drm_device *drm_device; > + > +/* keep track of whether we are already loaded.. we may need to call > + * plugin's load() if they register after we are already loaded > + */ > +static bool loaded = false; Add __read_mostly.. You don't need to set false as by default it will be 0 (false). > + > +/* > + * mode config funcs > + */ > + > +/* Notes about mapping DSS and DRM entities: > + * CRTC: overlay > + * encoder: manager.. with some extension to allow one primary CRTC > + * and zero or more video CRTC's to be mapped to one encoder? > + * connector: dssdev.. manager can be attached/detached from different > + * devices > + */ > + > +static void omap_fb_output_poll_changed(struct drm_device *dev) > +{ > + struct omap_drm_private *priv = dev->dev_private; > + DBG("dev=%p", dev); > + if (priv->fbdev) { > + drm_fb_helper_hotplug_event(priv->fbdev); > + } > +} > + > +static struct drm_mode_config_funcs omap_mode_config_funcs = { > + .fb_create = omap_framebuffer_create, > + .output_poll_changed = omap_fb_output_poll_changed, > +}; > + > +static int get_connector_type(struct omap_dss_device *dssdev) > +{ > + switch (dssdev->type) { > + case OMAP_DISPLAY_TYPE_HDMI: > + return DRM_MODE_CONNECTOR_HDMIA; > + case OMAP_DISPLAY_TYPE_DPI: > + if (!strcmp(dssdev->name, "dvi")) strncmp > + return DRM_MODE_CONNECTOR_DVID; > + default: > + return DRM_MODE_CONNECTOR_Unknown; > + } > +} > + > +#if 0 /* enable when dss2 supports hotplug */ > +static int omap_drm_notifier(struct notifier_block *nb, > + unsigned long evt, void *arg) > +{ > + switch (evt) { > + case OMAP_DSS_SIZE_CHANGE: > + case OMAP_DSS_HOTPLUG_CONNECT: > + case OMAP_DSS_HOTPLUG_DISCONNECT: { > + struct drm_device *dev = drm_device; > + DBG("hotplug event: evt=%d, dev=%p", evt, dev); > + if (dev) { > + drm_sysfs_hotplug_event(dev); > + } > + return NOTIFY_OK; > + } > + default: /* don't care about other events for now */ > + return NOTIFY_DONE; > + } > +} > +#endif > + > +static void dump_video_chains(void) > +{ > + int i; > + > + DBG("dumping video chains: "); > + for (i = 0; i < omap_dss_get_num_overlays(); i++) { Any chance omap_dss_get_num_overlays can return a negative value? > + struct omap_overlay *ovl = omap_dss_get_overlay(i); and if so, this could cause this guy to explode. > + struct omap_overlay_manager *mgr = ovl->manager; > + struct omap_dss_device *dssdev = mgr ? mgr->device : NULL; > + if (dssdev) { > + DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name, > + dssdev->name); > + } else if (mgr) { > + DBG("%d: %s -> %s", i, ovl->name, mgr->name); > + } else { > + DBG("%d: %s", i, ovl->name); > + } > + } > +} > + > +static int omap_modeset_init(struct drm_device *dev) > +{ > + const struct omap_drm_platform_data *pdata = dev->dev->platform_data; > + struct omap_drm_private *priv = dev->dev_private; > + struct omap_dss_device *dssdev = NULL; > + int i, j; > + unsigned int connected_connectors = 0; > + > + /* create encoders for each manager */ > + int create_encoder(int i) { > + struct omap_overlay_manager *mgr = > + omap_dss_get_overlay_manager(i); > + struct drm_encoder *encoder = omap_encoder_init(dev, mgr); > + > + if (!encoder) { > + dev_err(dev->dev, "could not create encoder\n"); > + return -ENOMEM; > + } > + > + priv->encoders[priv->num_encoders++] = encoder; Should you check priv->num_encoders to be sure you are not referencing past the priv->encoders size? Or if num_encoders is -1 then hitting some other code and potentially causing a security hole. > + > + return 0; > + } > + > + /* create connectors for each display device */ > + int create_connector(struct omap_dss_device *dssdev) { > + static struct notifier_block *notifier; > + struct drm_connector *connector; > + > + if (!dssdev->driver) { > + dev_warn(dev->dev, "%s has no driver.. skipping it\n", > + dssdev->name); > + return 0; > + } > + > + if (!(dssdev->driver->get_timings || > + dssdev->driver->read_edid)) { > + dev_warn(dev->dev, "%s driver does not support " > + "get_timings or read_edid.. skipping it!\n", > + dssdev->name); > + return 0; > + } > + > + connector = omap_connector_init(dev, > + get_connector_type(dssdev), dssdev); > + > + if (!connector) { > + dev_err(dev->dev, "could not create connector\n"); > + return -ENOMEM; > + } > + > + /* track what is already connected.. rather than looping thru > + * all connectors twice later, first for connected then for > + * remainder (which could be a race condition if connected > + * status changes) > + */ > + if (omap_connector_detect(connector, true) == > + connector_status_connected) { > + connected_connectors |= (1 << priv->num_connectors); > + } > + > + priv->connectors[priv->num_connectors++] = connector; Ditto on this check? > + > +#if 0 /* enable when dss2 supports hotplug */ > + notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); > + notifier->notifier_call = omap_drm_notifier; > + omap_dss_add_notify(dssdev, notifier); > +#else > + notifier = NULL; > +#endif > + > + for (j = 0; j < priv->num_encoders; j++) { > + struct omap_overlay_manager *mgr = > + omap_encoder_get_manager(priv->encoders[j]); > + if (mgr->device == dssdev) { > + drm_mode_connector_attach_encoder(connector, > + priv->encoders[j]); > + } > + } > + > + return 0; > + } > + > + /* create up to max_overlays CRTCs mapping to overlays.. by default, > + * connect the overlays to different managers/encoders, giving priority > + * to encoders connected to connectors with a detected connection > + */ > + int create_crtc(int i) { > + struct omap_overlay *ovl = omap_dss_get_overlay(i); > + struct omap_overlay_manager *mgr = NULL; > + struct drm_crtc *crtc; > + > + if (ovl->manager) { > + DBG("disconnecting %s from %s", ovl->name, > + ovl->manager->name); > + ovl->unset_manager(ovl); > + } > + > + /* find next best connector, ones with detected connection first > + */ > + while (j < priv->num_connectors && !mgr) { > + if (connected_connectors & (1 << j)) { > + struct drm_encoder * encoder = > + omap_connector_attached_encoder( > + priv->connectors[j]); > + if (encoder) { > + mgr = omap_encoder_get_manager(encoder); > + } > + } > + j++; > + } > + > + /* if we couldn't find another connected connector, lets start > + * looking at the unconnected connectors: > + */ > + while (j < 2 * priv->num_connectors && !mgr) { > + int idx = j - priv->num_connectors; You might want to use: unsigned int idx = max(0, j - priv->num_connectors); jsut ot make sure you don't go negative. > + if (!(connected_connectors & (1 << idx))) { > + struct drm_encoder * encoder = > + omap_connector_attached_encoder( > + priv->connectors[idx]); .. and reference in the unknown memory location. > + if (encoder) { > + mgr = omap_encoder_get_manager(encoder); > + } > + } > + j++; > + } > + > + if (mgr) { > + DBG("connecting %s to %s", ovl->name, mgr->name); > + ovl->set_manager(ovl, mgr); > + } > + > + crtc = omap_crtc_init(dev, ovl, priv->num_crtcs); > + > + if (!crtc) { > + dev_err(dev->dev, "could not create CRTC\n"); > + return -ENOMEM; > + } > + > + priv->crtcs[priv->num_crtcs++] = crtc; > + > + return 0; > + } > + > + drm_mode_config_init(dev); > + > + if (pdata) { > + /* if platform data is provided by the board file, use it to > + * control which overlays, managers, and devices we own. > + */ > + for (i = 0; i < pdata->mgr_cnt; i++) { > + create_encoder(pdata->mgr_ids[i]); No check for return value? What if we get -ENOMEM? > + } > + > + for (i = 0; i < pdata->dev_cnt; i++) { > + int m(struct omap_dss_device *dssdev, void *data) { > + return ! strcmp(dssdev->name, data); > + } > + struct omap_dss_device *dssdev = > + omap_dss_find_device( > + (void *)pdata->dev_names[i], m); > + if (!dssdev) { > + dev_warn(dev->dev, "no such dssdev: %s\n", > + pdata->dev_names[i]); > + continue; > + } > + create_connector(dssdev); > + } > + > + j = 0; > + for (i = 0; i < pdata->ovl_cnt; i++) { > + create_crtc(pdata->ovl_ids[i]); > + } > + } else { > + /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try > + * to make educated guesses about everything else > + */ > + int max_overlays = min(omap_dss_get_num_overlays(), > + CONFIG_DRM_OMAP_NUM_CRTCS); > + > + for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) { > + create_encoder(i); > + } > + > + for_each_dss_dev(dssdev) { > + create_connector(dssdev); > + } > + > + j = 0; > + for (i = 0; i < max_overlays; i++) { > + create_crtc(i); > + } > + } > + > + /* for now keep the mapping of CRTCs and encoders static.. */ > + for (i = 0; i < priv->num_encoders; i++) { > + struct drm_encoder *encoder = priv->encoders[i]; > + struct omap_overlay_manager *mgr = > + omap_encoder_get_manager(encoder); > + > + encoder->possible_crtcs = 0; > + > + for (j = 0; j < priv->num_crtcs; j++) { > + struct omap_overlay *ovl = > + omap_crtc_get_overlay(priv->crtcs[j]); > + if (ovl->manager == mgr) { > + encoder->possible_crtcs |= (1 << j); > + } > + } > + > + DBG("%s: possible_crtcs=%08x", mgr->name, > + encoder->possible_crtcs); > + } > + > + dump_video_chains(); > + > + dev->mode_config.min_width = 256; > + dev->mode_config.min_height = 256; > + > + /* note: pvr can't currently handle dst surfaces larger than 2k by 2k */ > + dev->mode_config.max_width = 2048; > + dev->mode_config.max_height = 2048; > + > + dev->mode_config.funcs = &omap_mode_config_funcs; > + > + return 0; > +} > + > +static void omap_modeset_free(struct drm_device *dev) > +{ > + drm_mode_config_cleanup(dev); > +} > + > +/* > + * drm ioctl funcs > + */ > + > + > +static int ioctl_get_param(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_param *args = data; > + > + DBG("%p: param=%llu", dev, args->param); > + > + switch (args->param) { > + case OMAP_PARAM_CHIPSET_ID: > + args->value = GET_OMAP_TYPE; > + break; > + default: > + DBG("unknown parameter %lld", args->param); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int ioctl_set_param(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_param *args = data; > + > + switch (args->param) { > + default: > + DBG("unknown parameter %lld", args->param); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int ioctl_gem_new(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_new *args = data; > + DBG("%p:%p: size=%d, flags=%08x", dev, file_priv, > + args->size.bytes, args->flags); > + return omap_gem_new_handle(dev, file_priv, args->size.bytes, > + args->flags, &args->handle); > +} > + > +static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_cpu_prep *args = data; > + struct drm_gem_object *obj; > + int ret; > + > + VERB("%p:%p: handle=%d, op=%x", dev, file_priv, args->handle, args->op); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + ret = omap_gem_op_sync(obj, args->op); > + > + if (!ret) { > + ret = omap_gem_op_start(obj, args->op); > + } > + > + drm_gem_object_unreference_unlocked(obj); > + > + return ret; > +} > + > +static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_cpu_fini *args = data; > + struct drm_gem_object *obj; > + int ret; > + > + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + /* XXX flushy, flushy */ > + ret = 0; > + > + if (!ret) { > + ret = omap_gem_op_finish(obj, args->op); > + } > + > + drm_gem_object_unreference_unlocked(obj); > + > + return ret; > +} > + > +static int ioctl_gem_info(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_info *args = data; > + struct drm_gem_object *obj; > + int ret = 0; > + > + DBG("%p:%p: handle=%d", dev, file_priv, args->handle); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + args->offset = omap_gem_mmap_offset(obj); > + > + drm_gem_object_unreference_unlocked(obj); > + > + return ret; > +} > + > +struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { > + DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH), > +}; > + > +/* > + * drm driver funcs > + */ > + > +/** > + * load - setup chip and create an initial config > + * @dev: DRM device > + * @flags: startup flags > + * > + * The driver load routine has to do several things: > + * - initialize the memory manager > + * - allocate initial config memory > + * - setup the DRM framebuffer with the allocated memory > + */ > +static int dev_load(struct drm_device *dev, unsigned long flags) > +{ > + struct omap_drm_private *priv; > + int ret; > + > + DBG("load: dev=%p", dev); > + > + drm_device = dev; > + > + priv = kzalloc(sizeof(*priv), GFP_KERNEL); > + if (!priv) { > + dev_err(dev->dev, "could not allocate priv\n"); > + return -1; -ENOMEM? > + } > + > + dev->dev_private = priv; > + > + ret = omap_modeset_init(dev); > + if (ret) { > + dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); > + // hmm > + //return ret; > + } > + > + priv->fbdev = omap_fbdev_init(dev); > + if (!priv->fbdev) { > + dev_err(dev->dev, "omap_fbdev_init failed\n"); > + ret = -ENOMEM; > + // hmm > + //return ret; Why not? > + } > + > + drm_kms_helper_poll_init(dev); > + > + ret = drm_vblank_init(dev, priv->num_crtcs); > + if (ret) { > + dev_err(dev->dev, "could not init vblank\n"); > + } > + > + loaded = true; > + > + return 0; > +} > + > +static int dev_unload(struct drm_device *dev) > +{ > + DBG("unload: dev=%p", dev); > + > + drm_vblank_cleanup(dev); > + drm_kms_helper_poll_fini(dev); > + > + omap_fbdev_free(dev); > + > + omap_modeset_free(dev); > + > + kfree(dev->dev_private); > + dev->dev_private = NULL; > + > + loaded = false; > + > + return 0; > +} > + > +static int dev_open(struct drm_device *dev, struct drm_file *file) > +{ > + file->driver_priv = NULL; > + > + DBG("open: dev=%p, file=%p", dev, file); > + > + return 0; > +} > + > +static int dev_firstopen(struct drm_device *dev) > +{ > + DBG("firstopen: dev=%p", dev); > + return 0; > +} > + > +/** > + * lastclose - clean up after all DRM clients have exited > + * @dev: DRM device > + * > + * Take care of cleaning up after all DRM clients have exited. In the > + * mode setting case, we want to restore the kernel's initial mode (just > + * in case the last client left us in a bad state). > + * > + * Additionally, in the non-mode setting case, we'll tear down the AGP > + * and DMA structures, since the kernel won't be using them, and clean > + * up any GEM state. > + */ > +static void dev_lastclose(struct drm_device * dev) > +{ > + DBG("lastclose: dev=%p", dev); > +} > + > +static void dev_preclose(struct drm_device * dev, struct drm_file *file) > +{ > + DBG("preclose: dev=%p", dev); > +} > + > +static void dev_postclose(struct drm_device *dev, struct drm_file *file) > +{ > + DBG("postclose: dev=%p, file=%p", dev, file); > +} > + > +/** > + * enable_vblank - enable vblank interrupt events > + * @dev: DRM device > + * @crtc: which irq to enable > + * > + * Enable vblank interrupts for @crtc. If the device doesn't have > + * a hardware vblank counter, this routine should be a no-op, since > + * interrupts will have to stay on to keep the count accurate. > + * > + * RETURNS > + * Zero on success, appropriate errno if the given @crtc's vblank > + * interrupt cannot be enabled. > + */ > +static int dev_enable_vblank(struct drm_device *dev, int crtc) > +{ > + DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc); > + return 0; > +} > + > +/** > + * disable_vblank - disable vblank interrupt events > + * @dev: DRM device > + * @crtc: which irq to enable > + * > + * Disable vblank interrupts for @crtc. If the device doesn't have > + * a hardware vblank counter, this routine should be a no-op, since > + * interrupts will have to stay on to keep the count accurate. > + */ > +static void dev_disable_vblank(struct drm_device *dev, int crtc) > +{ > + DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc); > +} > + > +static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS) > +{ > + return IRQ_HANDLED; > +} > + > +static void dev_irq_preinstall(struct drm_device *dev) > +{ > + DBG("irq_preinstall: dev=%p", dev); > +} > + > +static int dev_irq_postinstall(struct drm_device *dev) > +{ > + DBG("irq_postinstall: dev=%p", dev); > + return 0; > +} > + > +static void dev_irq_uninstall(struct drm_device *dev) > +{ > + DBG("irq_uninstall: dev=%p", dev); > +} > + > +static struct vm_operations_struct omap_gem_vm_ops = { > + .fault = omap_gem_fault, > + .open = drm_gem_vm_open, > + .close = drm_gem_vm_close, > +}; > + > +static struct drm_driver omap_drm_driver = { > + .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM, > + .load = dev_load, > + .unload = dev_unload, > + .open = dev_open, > + .firstopen = dev_firstopen, > + .lastclose = dev_lastclose, > + .preclose = dev_preclose, > + .postclose = dev_postclose, > + .get_vblank_counter = drm_vblank_count, > + .enable_vblank = dev_enable_vblank, > + .disable_vblank = dev_disable_vblank, > + .irq_preinstall = dev_irq_preinstall, > + .irq_postinstall = dev_irq_postinstall, > + .irq_uninstall = dev_irq_uninstall, > + .irq_handler = dev_irq_handler, > + .reclaim_buffers = drm_core_reclaim_buffers, > + .gem_init_object = omap_gem_init_object, > + .gem_free_object = omap_gem_free_object, > + .gem_vm_ops = &omap_gem_vm_ops, > + .dumb_create = omap_gem_dumb_create, > + .dumb_map_offset = omap_gem_dumb_map_offset, > + .dumb_destroy = omap_gem_dumb_destroy, > + .ioctls = ioctls, > + .num_ioctls = DRM_OMAP_NUM_IOCTLS, > + .fops = { > + .owner = THIS_MODULE, > + .open = drm_open, > + .unlocked_ioctl = drm_ioctl, > + .release = drm_release, > + .mmap = omap_gem_mmap, > + .poll = drm_poll, > + .fasync = drm_fasync, > + .read = drm_read, > + .llseek = noop_llseek, > + }, > + .name = DRIVER_NAME, > + .desc = DRIVER_DESC, > + .date = DRIVER_DATE, > + .major = DRIVER_MAJOR, > + .minor = DRIVER_MINOR, > + .patchlevel = DRIVER_PATCHLEVEL, > +}; > + > +static int pdev_suspend(struct platform_device *pDevice, pm_message_t state) > +{ > + DBG(""); > + return 0; > +} > + > +static int pdev_resume(struct platform_device *device) > +{ > + DBG(""); > + return 0; > +} > + > +static void pdev_shutdown(struct platform_device *device) > +{ > + DBG(""); > +} > + > +static int pdev_probe(struct platform_device *device) > +{ > + DBG("%s", device->name); > + return drm_platform_init(&omap_drm_driver, device); > +} > + > +static int pdev_remove(struct platform_device *device) > +{ > + DBG(""); > + drm_platform_exit(&omap_drm_driver, device); > + return 0; > +} > + > +struct platform_driver pdev = { > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + }, > + .probe = pdev_probe, > + .remove = pdev_remove, > + .suspend = pdev_suspend, > + .resume = pdev_resume, > + .shutdown = pdev_shutdown, > +}; > + > +static int __init omap_drm_init(void) > +{ > + DBG("init"); > + return platform_driver_register(&pdev); > +} > + > +static void __exit omap_drm_fini(void) > +{ > + DBG("fini"); > + platform_driver_unregister(&pdev); > +} > + > +/* need late_initcall() so we load after dss_driver's are loaded */ > +late_initcall(omap_drm_init); > +module_exit(omap_drm_fini); > + > +MODULE_AUTHOR("Rob Clark "); > +MODULE_DESCRIPTION("OMAP DRM Display Driver"); > +MODULE_ALIAS("platform:" DRIVER_NAME); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h > new file mode 100644 > index 0000000..acd567d > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.h > @@ -0,0 +1,126 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_drv.h > + * > + * Copyright (C) 2011 Texas Instruments > + * Author: Rob Clark > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License version 2 as published by > + * the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see . > + */ > + > +#ifndef __OMAP_DRV_H__ > +#define __OMAP_DRV_H__ > + > +#include