diff options
Diffstat (limited to 'drivers')
82 files changed, 3121 insertions, 255 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 85249395623..c7358dd68b3 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -564,7 +564,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) /* Indicate support for various _OSC capabilities. */ if (pci_ext_cfg_avail(root->bus->self)) flags |= OSC_EXT_PCI_CONFIG_SUPPORT; - if (pcie_aspm_enabled()) + if (pcie_aspm_support_enabled()) flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | OSC_CLOCK_PWR_CAPABILITY_SUPPORT; if (pci_msi_enabled()) diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 25ef1a4556e..b836e11a8a3 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -165,7 +165,6 @@ static uint32_t fpga_tx(struct solos_card *); static irqreturn_t solos_irq(int irq, void *dev_id); static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); static int list_vccs(int vci); -static void release_vccs(struct atm_dev *dev); static int atm_init(struct solos_card *, struct device *); static void atm_remove(struct solos_card *); static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); @@ -384,7 +383,6 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb /* Anything but 'Showtime' is down */ if (strcmp(state_str, "Showtime")) { atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_LOST); - release_vccs(card->atmdev[port]); dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str); return 0; } @@ -697,7 +695,7 @@ void solos_bh(unsigned long card_arg) size); } if (atmdebug) { - dev_info(&card->dev->dev, "Received: device %d\n", port); + dev_info(&card->dev->dev, "Received: port %d\n", port); dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", size, le16_to_cpu(header->vpi), le16_to_cpu(header->vci)); @@ -830,28 +828,6 @@ static int list_vccs(int vci) return num_found; } -static void release_vccs(struct atm_dev *dev) -{ - int i; - - write_lock_irq(&vcc_sklist_lock); - for (i = 0; i < VCC_HTABLE_SIZE; i++) { - struct hlist_head *head = &vcc_hash[i]; - struct hlist_node *node, *tmp; - struct sock *s; - struct atm_vcc *vcc; - - sk_for_each_safe(s, node, tmp, head) { - vcc = atm_sk(s); - if (vcc->dev == dev) { - vcc_release_async(vcc, -EPIPE); - sk_del_node_init(s); - } - } - } - write_unlock_irq(&vcc_sklist_lock); -} - static int popen(struct atm_vcc *vcc) { @@ -1018,8 +994,15 @@ static uint32_t fpga_tx(struct solos_card *card) /* Clean up and free oldskb now it's gone */ if (atmdebug) { + struct pkt_hdr *header = (void *)oldskb->data; + int size = le16_to_cpu(header->size); + + skb_pull(oldskb, sizeof(*header)); dev_info(&card->dev->dev, "Transmitted: port %d\n", port); + dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", + size, le16_to_cpu(header->vpi), + le16_to_cpu(header->vci)); print_buffer(oldskb); } @@ -1262,7 +1245,7 @@ static int atm_init(struct solos_card *card, struct device *parent) card->atmdev[i]->ci_range.vci_bits = 16; card->atmdev[i]->dev_data = card; card->atmdev[i]->phy_data = (void *)(unsigned long)i; - atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_UNKNOWN); + atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_FOUND); skb = alloc_skb(sizeof(*header), GFP_ATOMIC); if (!skb) { diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 579f7491849..554bbd907d1 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -222,6 +222,7 @@ static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c) h->ctlr, c->busaddr); #endif /* CCISS_DEBUG */ writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); + readl(h->vaddr + SA5_REQUEST_PORT_OFFSET); h->commands_outstanding++; if ( h->commands_outstanding > h->max_outstanding) h->max_outstanding = h->commands_outstanding; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 700a3840fdd..f44ca40a28f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -71,6 +71,9 @@ static struct usb_device_id btusb_table[] = { /* Apple MacBookAir3,1, MacBookAir3,2 */ { USB_DEVICE(0x05ac, 0x821b) }, + /* Apple MacBookPro8,2 */ + { USB_DEVICE(0x05ac, 0x821a) }, + /* AVM BlueFRITZ! USB v2.0 */ { USB_DEVICE(0x057c, 0x3800) }, diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 1f46f1cd922..7beb0e25f1e 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -980,7 +980,7 @@ int tpm_open(struct inode *inode, struct file *file) return -EBUSY; } - chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + chip->data_buffer = kzalloc(TPM_BUFSIZE, GFP_KERNEL); if (chip->data_buffer == NULL) { clear_bit(0, &chip->is_open); put_device(chip->dev); diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 23e03554f0d..7e0e66037e0 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2765,7 +2765,7 @@ static int __init amd64_edac_init(void) mcis = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL); ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); if (!(mcis && ecc_stngs)) - goto err_ret; + goto err_free; msrs = msrs_alloc(); if (!msrs) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0902d446003..9c103b2e2f4 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -165,3 +165,27 @@ config DRM_SAVAGE help Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister chipset. If M is selected the module will be called savage. + +config DRM_OMAP + tristate "OMAP GPU (EXPERIMENTAL)" + depends on DRM && !CONFIG_FB_OMAP2 + select DRM_KMS_HELPER + select OMAP2_VRAM + select OMAP2_DSS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + default y + 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. diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 997c43d0490..dc1a49620e7 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,4 +36,5 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VIA) +=via/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ +obj-$(CONFIG_DRM_OMAP) += omap/ obj-y += i2c/ diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 271835a7157..7301d5e7fda 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -262,15 +262,12 @@ EXPORT_SYMBOL(drm_init); void drm_exit(struct drm_driver *driver) { - struct drm_device *dev, *tmp; DRM_DEBUG("\n"); - if (driver->driver_features & DRIVER_MODESET) { - pci_unregister_driver(&driver->pci_driver); - } else { - list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) - drm_put_dev(dev); - } + if (driver->driver_features & DRIVER_USE_PLATFORM_DEVICE) + drm_platform_exit(driver); + else + drm_pci_exit(driver); DRM_INFO("Module unloaded\n"); } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index f73ef4390db..95072047396 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -627,6 +627,11 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, value = (red << info->var.red.offset) | (green << info->var.green.offset) | (blue << info->var.blue.offset); + if (info->var.transp.length > 0) { + u32 mask = (1 << info->var.transp.length) - 1; + mask <<= info->var.transp.offset; + value |= mask; + } palette[regno] = value; return 0; } diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index be9a9c07d15..73bf49360c9 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -54,11 +54,11 @@ int drm_name_info(struct seq_file *m, void *data) if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) { if (master->unique) { seq_printf(m, "%s %s %s\n", - dev->driver->platform_device->name, + dev->platformdev->name, dev_name(dev->dev), master->unique); } else { seq_printf(m, "%s\n", - dev->driver->platform_device->name); + dev->platformdev->name); } } else { if (master->unique) { diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 47db4df37a6..075024b88f4 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -165,14 +165,15 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) drm_unset_busid(dev, master); if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) { - master->unique_len = 10 + strlen(dev->platformdev->name); + master->unique_len = 13 + strlen(dev->platformdev->name); + master->unique_size = master->unique_len; master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); if (master->unique == NULL) return -ENOMEM; len = snprintf(master->unique, master->unique_len, - "platform:%s", dev->platformdev->name); + "platform:%s:%02d", dev->platformdev->name, dev->primary->index); if (len > master->unique_len) { DRM_ERROR("Unique buffer overflowed\n"); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index f5bd9e590c8..6f7e41b5816 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -263,6 +263,19 @@ int drm_pci_init(struct drm_driver *driver) return 0; } +void drm_pci_exit(struct drm_driver *driver) +{ + struct drm_device *dev, *tmp; + + if (driver->driver_features & DRIVER_MODESET) { + pci_unregister_driver(&driver->pci_driver); + } else { + list_for_each_entry_safe(dev, tmp, &driver->device_list, + driver_item) + drm_put_dev(dev); + } +} + #else int drm_pci_init(struct drm_driver *driver) @@ -270,5 +283,9 @@ int drm_pci_init(struct drm_driver *driver) return -1; } +void drm_pci_exit(struct drm_driver *driver) +{ +} + #endif /*@}*/ diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 92d1d0fb7b7..8f68a5431db 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -123,5 +123,10 @@ EXPORT_SYMBOL(drm_get_platform_dev); int drm_platform_init(struct drm_driver *driver) { - return drm_get_platform_dev(driver->platform_device, driver); + return platform_driver_register(&driver->platform_driver); +} + +void drm_platform_exit(struct drm_driver *driver) +{ + platform_driver_unregister(&driver->platform_driver); } diff --git a/drivers/gpu/drm/omap/Makefile b/drivers/gpu/drm/omap/Makefile new file mode 100644 index 00000000000..0a2513f563f --- /dev/null +++ b/drivers/gpu/drm/omap/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm +omapgpu-y := omap_gpu.o omap_crtc.o omap_encoder.o omap_connector.o omap_fb.o omap_fbdev.o + +obj-$(CONFIG_DRM_OMAP) += omapgpu.o diff --git a/drivers/gpu/drm/omap/omap_connector.c b/drivers/gpu/drm/omap/omap_connector.c new file mode 100644 index 00000000000..aefafd79a1f --- /dev/null +++ b/drivers/gpu/drm/omap/omap_connector.c @@ -0,0 +1,504 @@ +/* + * linux/drivers/gpu/drm/omap/omap_connector.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/omap_gpu.h> +#include "omap_gpu_priv.h" + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +/* + * connector funcs + */ + +#define to_omap_connector(x) container_of(x, struct omap_connector, base) + +struct omap_connector { + struct drm_connector base; + struct omap_dss_device *dssdev; + struct drm_display_mode *native_mode; +}; + +static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode, + struct omap_video_timings *timings) +{ + mode->clock = timings->pixel_clock; + + mode->hdisplay = timings->x_res; + mode->hsync_start = mode->hdisplay + timings->hfp; + mode->hsync_end = mode->hsync_start + timings->hsw; + mode->htotal = mode->hsync_end + timings->hbp; + + mode->vdisplay = timings->y_res; + mode->vsync_start = mode->vdisplay + timings->vfp; + mode->vsync_end = mode->vsync_start + timings->vsw; + mode->vtotal = mode->vsync_end + timings->vbp; + + /* note: whether or not it is interlaced, +/- h/vsync, etc, + * which should be set in the mode flags, is not exposed in + * the omap_video_timings struct.. but hdmi driver tracks + * those separately so all we have to have to set the mode + * is the way to recover these timings values, and the + * omap_dss_driver would do the rest. + */ +} + +static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings, + struct drm_display_mode *mode) +{ + timings->pixel_clock = mode->clock; + + timings->x_res = mode->hdisplay; + timings->hfp = mode->hsync_start - mode->hdisplay; + timings->hsw = mode->hsync_end - mode->hsync_start; + timings->hbp = mode->htotal - mode->hsync_end; + + timings->y_res = mode->vdisplay; + timings->vfp = mode->vsync_start - mode->vdisplay; + timings->vsw = mode->vsync_end - mode->vsync_start; + timings->vbp = mode->vtotal - mode->vsync_end; +} + +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; + + /* TODO: add API in DSS to suspend/resume individual displays.. */ + + DBG("%s: %d", dssdev->name, mode); +} + +enum drm_connector_status omap_connector_detect( + struct drm_connector *connector, bool force) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct omap_dss_device *dssdev = omap_connector->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + enum drm_connector_status ret; + + if (dssdrv->is_detected(dssdev)) { + ret = connector_status_connected; + } else { + ret = connector_status_disconnected; + } + + DBG("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force); + + return ret; +} + +static void omap_connector_destroy(struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + + DBG("%s", omap_connector->dssdev->name); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(omap_connector); +} + +static struct drm_display_mode * omap_connector_native_mode( + struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *largest = NULL; + int high_w = 0, high_h = 0, high_v = 0; + + list_for_each_entry(mode, &omap_connector->base.probed_modes, head) { + mode->vrefresh = drm_mode_vrefresh(mode); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + continue; + + /* Use preferred mode if there is one */ + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + DBG("native mode from preferred: %dx%d@%d", + mode->hdisplay, mode->vdisplay, mode->vrefresh); + return drm_mode_duplicate(dev, mode); + } + + /* Otherwise, take the resolution with the largest width, then + * height, then vertical refresh + */ + if (mode->hdisplay < high_w) + continue; + + if (mode->hdisplay == high_w && mode->vdisplay < high_h) + continue; + + if (mode->hdisplay == high_w && mode->vdisplay == high_h && + mode->vrefresh < high_v) + continue; + + high_w = mode->hdisplay; + high_h = mode->vdisplay; + high_v = mode->vrefresh; + largest = mode; + } + + DBG("native mode from largest: %dx%d@%d", high_w, high_h, high_v); + return largest ? drm_mode_duplicate(dev, largest) : NULL; +} + +struct moderec { + int hdisplay; + int vdisplay; +}; + +static struct moderec scaler_modes[] = { + { 1920, 1200 }, + { 1920, 1080 }, + { 1680, 1050 }, + { 1600, 1200 }, + { 1400, 1050 }, + { 1400, 900 }, + { 1280, 1024 }, + { 1280, 960 }, + { 1280, 720 }, + { 1152, 768 }, + { 1024, 768 }, + { 800, 600 }, + { 720, 480 }, + { 640, 480 }, + {} +}; + +static int omap_connector_scaler_modes_add(struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct drm_display_mode *native = omap_connector->native_mode, *m; + struct drm_device *dev = connector->dev; + struct moderec *mode = &scaler_modes[0]; + int modes = 0; + + if (!native) + return 0; + + while (mode->hdisplay) { + if (mode->hdisplay <= native->hdisplay && + mode->vdisplay <= native->vdisplay) { + m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay, + 60, true, false, false); + if (!m) + continue; + + m->type |= DRM_MODE_TYPE_DRIVER; + + DBG("adding scaler mode: %dx%d@%d", mode->hdisplay, + mode->vdisplay, drm_mode_vrefresh(m)); + drm_mode_probed_add(connector, m); + modes++; + } + mode++; + } + + return modes; +} + +#define MAX_EDID 256 + +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 (omap_connector->native_mode) { + drm_mode_destroy(dev, omap_connector->native_mode); + omap_connector->native_mode = NULL; + } + + /* 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->get_edid) { + void *edid = kzalloc(MAX_EDID, GFP_KERNEL); + + if ((dssdrv->get_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); + omap_connector->native_mode = + omap_connector_native_mode(connector); + n += omap_connector_scaler_modes_add(connector); + 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); +} + +enum omap_dss_update_mode omap_connector_get_update_mode( + 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; + + DBG("%s", omap_connector->dssdev->name); + + if (dssdrv->get_update_mode) { + return dssdrv->get_update_mode(dssdev); + } + + return -1; +} +EXPORT_SYMBOL(omap_connector_get_update_mode); + +int omap_connector_set_update_mode(struct drm_connector *connector, + enum omap_dss_update_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; + + DBG("%s: %d", omap_connector->dssdev->name, mode); + + if (dssdrv->set_update_mode) { + return dssdrv->set_update_mode(dssdev, mode); + } + + return -EINVAL; +} +EXPORT_SYMBOL(omap_connector_set_update_mode); + +int omap_connector_sync(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; + + DBG("%s", omap_connector->dssdev->name); + + if (dssdrv->sync) { + return dssdrv->sync(dssdev); + } + + return -EINVAL; +} +EXPORT_SYMBOL(omap_connector_sync); + +/* 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_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 (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD) { + connector->polled = 0; + } else { + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + } + + connector->interlace_allowed = 1; + connector->doublescan_allowed = 0; + + drm_sysfs_connector_add(connector); + + /* 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: + if (dssdev->driver) { + int ret = dssdev->driver->enable(dssdev); + if (ret) { + DBG("%s: failed to enable: %d", + dssdev->name, ret); + dssdev->driver->disable(dssdev); + } + } + break; + default: + break; + } + + return connector; + +fail: + if (connector) { + drm_connector_cleanup(connector); + kfree(omap_connector); + } + + return NULL; +} diff --git a/drivers/gpu/drm/omap/omap_crtc.c b/drivers/gpu/drm/omap/omap_crtc.c new file mode 100644 index 00000000000..1726fd76f14 --- /dev/null +++ b/drivers/gpu/drm/omap/omap_crtc.c @@ -0,0 +1,277 @@ +/* + * linux/drivers/gpu/drm/omap/omap_crtc.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/omap_gpu.h> +#include "omap_gpu_priv.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; + struct omap_overlay_info info; +}; + +static int commit(struct drm_crtc *crtc); + +/* 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 omap_crtc_update_scanout(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + unsigned long paddr; + void __iomem *vaddr; + int screen_width; + + 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, paddr, screen_width); + + 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); + // XXX ignore? +} + +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) { + omap_crtc_update_scanout(crtc); + omap_crtc->info.enabled = true; + } else { + omap_crtc->info.enabled = false; + } + + commit(crtc); +} + +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); + // XXX I guess we support anything? + 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_ARGB32; + omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA; + omap_crtc->info.rotation = OMAP_DSS_ROT_0; +#if 0 /* enable when supported in dss */ + omap_crtc->info.zorder = 3; /* GUI in the front, video behind */ +#endif + 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 /* enable when supported in dss */ + 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 + + omap_crtc_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 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); + 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"); + return ret; + } + } + + if (info->enabled) { + omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y, + crtc->fb->width, crtc->fb->height); + } + + return 0; +} + +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); + + omap_crtc_update_scanout(crtc); + + return commit(crtc); +} + +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 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, +//TODO .page_flip = omap_page_flip, +}; + +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) +{ + 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; + 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/gpu/drm/omap/omap_encoder.c b/drivers/gpu/drm/omap/omap_encoder.c new file mode 100644 index 00000000000..e60fe81073b --- /dev/null +++ b/drivers/gpu/drm/omap/omap_encoder.c @@ -0,0 +1,198 @@ +/* + * linux/drivers/gpu/drm/omap/omap_encoder.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/omap_gpu.h> +#include "omap_gpu_priv.h" + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +/* + * encoder funcs + */ + +#define to_omap_encoder(x) container_of(x, struct omap_encoder, base) + +struct omap_encoder { + struct drm_encoder base; + struct omap_overlay_manager *mgr; +}; + +static void omap_encoder_destroy(struct drm_encoder *encoder) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + DBG("%s", omap_encoder->mgr->name); + drm_encoder_cleanup(encoder); + kfree(omap_encoder); +} + +static void omap_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct omap_gpu_private *priv = dev->dev_private; + int i; + + DBG("%s: %d", omap_encoder->mgr->name, mode); + + /* managers don't need to do anything for DPMS.. but we do + * need to propagate to the connector, who is actually going + * to enable/disable as needed: + */ + for (i = 0; i < priv->num_connectors; i++) { + struct drm_connector *connector = priv->connectors[i]; + if (connector->encoder == encoder) { + omap_connector_dpms(connector, mode); + } + } +} + +static bool omap_encoder_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + DBG("%s", omap_encoder->mgr->name); + return true; +} + +static void omap_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct omap_gpu_private *priv = dev->dev_private; + int i; + + mode = adjusted_mode; + + DBG("%s: set mode: %dx%d", omap_encoder->mgr->name, + mode->hdisplay, mode->vdisplay); + + for (i = 0; i < priv->num_connectors; i++) { + struct drm_connector *connector = priv->connectors[i]; + if (connector->encoder == encoder) { + omap_connector_mode_set(connector, mode); + } + } +} + +static void omap_encoder_prepare(struct drm_encoder *encoder) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + DBG("%s", omap_encoder->mgr->name); + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void omap_encoder_commit(struct drm_encoder *encoder) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + DBG("%s", omap_encoder->mgr->name); + omap_encoder->mgr->apply(omap_encoder->mgr); + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_funcs omap_encoder_funcs = { + .destroy = omap_encoder_destroy, +}; + +static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { + .dpms = omap_encoder_dpms, + .mode_fixup = omap_encoder_mode_fixup, + .mode_set = omap_encoder_mode_set, + .prepare = omap_encoder_prepare, + .commit = omap_encoder_commit, +}; + +struct omap_overlay_manager * omap_encoder_get_manager( + struct drm_encoder *encoder) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + return omap_encoder->mgr; +} + +/* maybe this could go away and we just use drm_vblank_wait()? */ +int omap_encoder_wait_for_vsync(struct drm_encoder *encoder) +{ + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + DBG("%s", omap_encoder->mgr->name); + return omap_encoder->mgr->wait_for_vsync(omap_encoder->mgr); +} +EXPORT_SYMBOL(omap_encoder_wait_for_vsync); + +/* initialize encoder */ +struct drm_encoder * omap_encoder_init(struct drm_device *dev, + struct omap_overlay_manager *mgr) +{ + struct drm_encoder *encoder = NULL; + struct omap_encoder *omap_encoder; + struct omap_overlay_manager_info info; + int ret; + + DBG("%s", mgr->name); + + omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL); + if (!omap_encoder) { + dev_err(dev->dev, "could not allocate encoder\n"); + goto fail; + } + + omap_encoder->mgr = mgr; + encoder = &omap_encoder->base; + + mgr->get_manager_info(mgr, &info); + + /* TODO: fix hard-coded setup.. */ + info.default_color = 0x00000000; + info.trans_key = 0x00000000; + info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + info.trans_enabled = false; + info.alpha_enabled = false; + + ret = mgr->set_manager_info(mgr, &info); + if (ret) { + dev_err(dev->dev, "could not set manager info\n"); + goto fail; + } + + ret = mgr->apply(mgr); + if (ret) { + dev_err(dev->dev, "could not apply\n"); + goto fail; + } + + drm_encoder_init(dev, encoder, &omap_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs); + + return encoder; + +fail: + if (encoder) { + drm_encoder_cleanup(encoder); + kfree(omap_encoder); + } + + return NULL; +} diff --git a/drivers/gpu/drm/omap/omap_fb.c b/drivers/gpu/drm/omap/omap_fb.c new file mode 100644 index 00000000000..b3ac7fb21ac --- /dev/null +++ b/drivers/gpu/drm/omap/omap_fb.c @@ -0,0 +1,368 @@ +/* + * linux/drivers/gpu/drm/omap/omap_fb.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <plat/vram.h> + +#include <linux/omap_gpu.h> +#include "omap_gpu_priv.h" + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + + +static char *def_vram; +module_param_named(vram, def_vram, charp, 0); + +/* + * framebuffer funcs + */ + +#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) + +struct omap_framebuffer { + struct drm_framebuffer base; + + /* framebuffer size/phys-addr/virt-addr */ + int size; + unsigned long paddr; + void __iomem *vaddr; +}; + + +/* copied from omapfb-main.c to preserve vram param syntax */ +static int parse_vram_param(const char *param, int max_entries, + unsigned long *sizes, unsigned long *paddrs) +{ + int fbnum; + unsigned long size; + unsigned long paddr = 0; + char *p, *start; + + DBG("vram: %s", param); + + start = (char *)param; + + while (1) { + p = start; + + fbnum = simple_strtoul(p, &p, 10); + + if ((p == param) || (*p != ':') || (fbnum >= max_entries)) + return -EINVAL; + + size = memparse(p + 1, &p); + + if (!size) + return -EINVAL; + + paddr = 0; + + if (*p == '@') { + paddr = simple_strtoul(p + 1, &p, 16); + + if (!paddr) + return -EINVAL; + } + + paddrs[fbnum] = paddr; + sizes[fbnum] = size; + + if (*p == 0) + break; + + if (*p != ',') + return -EINVAL; + + ++p; + + start = p; + } + + return 0; +} + +#define MAX_FBS 10 + +static int allocate_vram(struct drm_framebuffer *fb, int idx, int size) +{ + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + struct drm_device *dev = fb->dev; + unsigned long sizes[MAX_FBS] = {0}; + unsigned long paddrs[MAX_FBS] = {0}; + unsigned long paddr = 0; + int ret = ret; + + if (idx >= MAX_FBS) { + dev_err(dev->dev, "invalid fb number: %d\n", idx); + goto fail; + } + + if (def_vram) { + if (parse_vram_param(def_vram, MAX_FBS, sizes, paddrs)) { + dev_err(dev->dev, "failed to parse vram parameter\n"); + memset(&sizes, 0, sizeof(sizes)); + memset(&paddrs, 0, sizeof(paddrs)); + } else { + size = sizes[idx]; + paddr = paddrs[idx]; + } + } + + size = PAGE_ALIGN(size); + + if (paddr) { + DBG("reserving %d bytes at %lx for fb %d", size, paddr, idx); + ret = omap_vram_reserve(paddr, size); + } else { + DBG("allocating %d bytes for fb %d", size, idx); + ret = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr); + } + + if (ret) { + dev_err(dev->dev, "failed to allocate vram\n"); + goto fail; + } + + omap_fb->size = size; + omap_fb->paddr = paddr; + omap_fb->vaddr = ioremap_wc(paddr, size); + + if (!omap_fb->vaddr) { + dev_err(dev->dev, "failed to ioremap framebuffer\n"); + ret = -ENOMEM; + goto fail; + } + + //memset(vaddr, 0, size); + + return 0; + +fail: + if (omap_fb->paddr) { + omap_vram_free(paddr, size); + } + + omap_fb->size = 0; + omap_fb->vaddr = NULL; + omap_fb->paddr = 0; + + return ret; +} + +static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + DBG("framebuffer: get handle: %p", omap_fb); + + // TODO, I suppose this really should be some sort of GEM handle + // to the framebuffer object, in case it needs to be mapped or + // something. Right now this will go-exist badly with PVR, who + // implements the mmap() fxn.. need to think about how to handle + // this.. + + *handle = 42; + + return 0; +} + +static void omap_framebuffer_destroy(struct drm_framebuffer *fb) +{ + /* omap_vram_free() doesn't really do what you'd think.. (or at + * least not if you think it'd return vram to the pool), so + * disabling this for now until there is a better way to alloc + * and free coherant.. + */ + DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); +} + +static int omap_framebuffer_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned flags, unsigned color, + struct drm_clip_rect *clips, unsigned num_clips) +{ + int i; + + for (i = 0; i < num_clips; i++) { + omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1, + clips[i].x2 - clips[i].x1, + clips[i].y2 - clips[i].y1); + } + + return 0; +} + +static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { + .create_handle = omap_framebuffer_create_handle, + .destroy = omap_framebuffer_destroy, + .dirty = omap_framebuffer_dirty, +}; + +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, + void **vaddr, unsigned long *paddr, int *screen_width) +{ + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + int bpp = 4; //XXX fb->depth / 8; + unsigned long offset; + + offset = (x * bpp) + (y * fb->pitch); + + *vaddr = omap_fb->vaddr + offset; + *paddr = omap_fb->paddr + offset; + *screen_width = fb->pitch / bpp; + + return omap_fb->size; +} + +/* iterate thru all the connectors, returning ones that are attached + * to the same fb.. + */ +struct drm_connector * omap_framebuffer_get_next_connector( + struct drm_framebuffer *fb, struct drm_connector *from) +{ + struct drm_device *dev = fb->dev; + struct list_head *connector_list = &dev->mode_config.connector_list; + struct drm_connector *connector = from; + + if (!from) { + return list_first_entry(connector_list, typeof(*from), head); + } + + list_for_each_entry_from(connector, connector_list, head) { + if (connector != from) { + struct drm_encoder *encoder = connector->encoder; + struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; + if (crtc && crtc->fb == fb) { + return connector; + } + } + } + + return NULL; +} +EXPORT_SYMBOL(omap_framebuffer_get_next_connector); + +/* flush an area of the framebuffer (in case of manual update display that + * is not automatically flushed) + */ +void omap_framebuffer_flush(struct drm_framebuffer *fb, + int x, int y, int w, int h) +{ + struct drm_connector *connector = NULL; + + VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb); + + while ((connector = + omap_framebuffer_get_next_connector(fb, connector))) { + /* only consider connectors that are part of a chain */ + if (connector->encoder && connector->encoder->crtc) { + /* TODO: maybe this should propagate thru the crtc who + * could do the coordinate translation.. + */ + struct drm_crtc *crtc = connector->encoder->crtc; + int cx = max(0, x - crtc->x); + int cy = max(0, y - crtc->y); + int cw = w + (x - crtc->x) - cx; + int ch = h + (y - crtc->y) - cy; + + omap_connector_flush(connector, cx, cy, cw, ch); + } + } +} +EXPORT_SYMBOL(omap_framebuffer_flush); + +struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev, + struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd) +{ + return omap_framebuffer_init(dev, mode_cmd); +} + +struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd) +{ + struct omap_gpu_private *priv = dev->dev_private; + struct omap_framebuffer *omap_fb = NULL; + struct drm_framebuffer *fb; + int ret; + + /* in case someone tries to feed us a completely bogus stride: */ + mode_cmd->pitch = max(mode_cmd->pitch, + mode_cmd->width * mode_cmd->bpp / 8); + + /* pvr needs to have a stride that is a multiple of 8 pixels: */ + mode_cmd->pitch = ALIGN(mode_cmd->pitch, 8 * (mode_cmd->bpp / 8)); + + /* for now, we can only support a single fb.. so we need to resize + * the old one instead of creating a new one of different size. If + * we can eventually get rid of the vram pool and had a better way + * to allocate contiguous memory after boot time, this restriction + * should be lifted. + */ + if (priv->fb) { + fb = priv->fb; + DBG("recycle: FB ID: %d (%p)", fb->base.id, fb); + goto recycle; /* ugg, we need CMA! */ + } + + DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d)", dev, + mode_cmd, mode_cmd->width, mode_cmd->height); + + omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); + if (!omap_fb) { + dev_err(dev->dev, "could not allocate fb\n"); + goto fail; + } + + fb = &omap_fb->base; + ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); + if (ret) { + dev_err(dev->dev, "framebuffer init failed: %d\n", ret); + goto fail; + } + + DBG("create: FB ID: %d (%p)", fb->base.id, fb); + + ret = allocate_vram(fb, dev->primary->index, + mode_cmd->pitch * mode_cmd->height); + if (ret) { + dev_err(dev->dev, "failed to allocate framebuffer\n"); + goto fail; + } + +recycle: + drm_helper_mode_fill_fb_struct(fb, mode_cmd); + + priv->fb = fb; + + if (priv->fbdev) { + /* if fbdev is already created, we need to update it to + * be attached to the new fb + */ + omap_fbdev_update(priv->fbdev, fb); + } + + return fb; + +fail: + if (omap_fb) { + kfree(omap_fb); + } + return NULL; +} + diff --git a/drivers/gpu/drm/omap/omap_fbdev.c b/drivers/gpu/drm/omap/omap_fbdev.c new file mode 100644 index 00000000000..84b94dbfea4 --- /dev/null +++ b/drivers/gpu/drm/omap/omap_fbdev.c @@ -0,0 +1,298 @@ +/* + * linux/drivers/gpu/drm/omap/omap_fbdev.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/omap_gpu.h> +#include "omap_gpu_priv.h" + +#include "drm_crtc.h" +#include "drm_fb_helper.h" + +/* + * fbdev funcs, to implement legacy fbdev interface on top of drm driver + */ + +#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base) + +struct omap_fbdev { + struct drm_fb_helper base; + struct drm_framebuffer *fb; +}; + +static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t res; + + res = fb_sys_write(fbi, buf, count, ppos); + omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres); + + return res; +} + +static void omap_fbdev_fillrect(struct fb_info *fbi, + const struct fb_fillrect *rect) +{ + sys_fillrect(fbi, rect); + omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height); +} + +static void omap_fbdev_copyarea(struct fb_info *fbi, + const struct fb_copyarea *area) +{ + sys_copyarea(fbi, area); + omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height); +} + +static void omap_fbdev_imageblit(struct fb_info *fbi, + const struct fb_image *image) +{ + sys_imageblit(fbi, image); + omap_fbdev_flush(fbi, image->dx, image->dy, + image->width, image->height); +} + +static struct fb_ops omap_fb_ops = { + .owner = THIS_MODULE, + + /* Note: to properly handle manual update displays, we wrap the + * basic fbdev ops which write to the framebuffer + */ + .fb_read = fb_sys_read, + .fb_write = omap_fbdev_write, + .fb_fillrect = omap_fbdev_fillrect, + .fb_copyarea = omap_fbdev_copyarea, + .fb_imageblit = omap_fbdev_imageblit, + + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static int omap_fbdev_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct omap_fbdev *fbdev = to_omap_fbdev(helper); + struct drm_device *dev = helper->dev; + struct fb_info *fbi; + struct drm_mode_fb_cmd mode_cmd = {0}; + struct device *device = &dev->platformdev->dev; + int ret; + + /* only doing ARGB32 since this is what is needed to alpha-blend + * with video overlays: + */ + sizes->surface_bpp = 32; + sizes->surface_depth = 32; + + DBG("create fbdev: %dx%d@%d", sizes->surface_width, + sizes->surface_height, sizes->surface_bpp); + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + + mode_cmd.bpp = sizes->surface_bpp; + mode_cmd.depth = sizes->surface_depth; + + mutex_lock(&dev->struct_mutex); + + fbi = framebuffer_alloc(0, device); + if (!fbi) { + dev_err(dev->dev, "failed to allocate fb info\n"); + ret = -ENOMEM; + goto fail; + } + + DBG("fbi=%p, dev=%p", fbi, dev); + + fbdev->fb = omap_framebuffer_init(dev, &mode_cmd); + if (!fbdev->fb) { + dev_err(dev->dev, "failed to allocate fb\n"); + ret = -ENOMEM; + goto fail; + } + + helper->fb = fbdev->fb; + helper->fbdev = fbi; + + fbi->par = helper; + fbi->flags = FBINFO_DEFAULT; + fbi->fbops = &omap_fb_ops; + + strcpy(fbi->fix.id, MODULE_NAME); + + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto fail; + } + + omap_fbdev_update(helper, fbdev->fb); + + DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); + DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height); + + mutex_unlock(&dev->struct_mutex); + + return 0; + +fail: + mutex_unlock(&dev->struct_mutex); + // TODO cleanup? + return ret; +} + +static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc, + u16 red, u16 green, u16 blue, int regno) +{ + DBG("fbdev: set gamma"); + // XXX ignore? +} + +static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, int regno) +{ + DBG("fbdev: get gamma"); + // XXX ignore? +} + +static int omap_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + int new_fb = 0; + int ret; + + if (!helper->fb) { + ret = omap_fbdev_create(helper, sizes); + if (ret) + return ret; + new_fb = 1; + } + return new_fb; +} + +static struct drm_fb_helper_funcs omap_fb_helper_funcs = { + .gamma_set = omap_crtc_fb_gamma_set, + .gamma_get = omap_crtc_fb_gamma_get, + .fb_probe = omap_fbdev_probe, +}; + +static struct drm_fb_helper * get_fb(struct fb_info *fbi) +{ + if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) { + /* these are not the fb's you're looking for */ + return NULL; + } + return fbi->par; +} + +void omap_fbdev_update(struct drm_fb_helper *helper, + struct drm_framebuffer *fb) +{ + struct fb_info *fbi = helper->fbdev; + struct drm_device *dev = helper->dev; + struct omap_fbdev *fbdev = to_omap_fbdev(helper); + unsigned long paddr; + void __iomem *vaddr; + int size, screen_width; + + DBG("update fbdev: %dx%d, fbi=%p", fb->width, fb->height, fbi); + + fbdev->fb = fb; + + drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth); + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); + + size = omap_framebuffer_get_buffer(fbdev->fb, 0, 0, + &vaddr, &paddr, &screen_width); + + dev->mode_config.fb_base = paddr; + + fbi->screen_base = vaddr; + fbi->screen_size = size; + fbi->fix.smem_start = paddr; + fbi->fix.smem_len = size; +} + +struct drm_connector * omap_fbdev_get_next_connector(struct fb_info *fbi, + struct drm_connector *from) +{ + struct drm_fb_helper *helper = get_fb(fbi); + + if (!helper) + return NULL; + + return omap_framebuffer_get_next_connector(helper->fb, from); +} +EXPORT_SYMBOL(omap_fbdev_get_next_connector); + +/* flush an area of the framebuffer (in case of manual update display that + * is not automatically flushed) + */ +void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h) +{ + struct drm_fb_helper *helper = get_fb(fbi); + + if (!helper) + return; + + VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi); + + omap_framebuffer_flush(helper->fb, x, y, w, h); +} +EXPORT_SYMBOL(omap_fbdev_flush); + +/* initialize fbdev helper */ +struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev) +{ + struct omap_gpu_private *priv = dev->dev_private; + struct omap_fbdev *fbdev = NULL; + struct drm_fb_helper *helper; + int ret = 0; + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) { + dev_err(dev->dev, "could not allocate fbdev\n"); + goto fail; + } + + helper = &fbdev->base; + + helper->funcs = &omap_fb_helper_funcs; + + ret = drm_fb_helper_init(dev, helper, priv->num_crtcs, 4); + if (ret) { + dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret); + goto fail; + } + + drm_fb_helper_single_add_all_connectors(helper); + drm_fb_helper_initial_config(helper, 32); + + priv->fbdev = helper; + + return helper; + +fail: + if (fbdev) { + kfree(fbdev); + } + return NULL; +} diff --git a/drivers/gpu/drm/omap/omap_gpu.c b/drivers/gpu/drm/omap/omap_gpu.c new file mode 100644 index 00000000000..e63e27af46b --- /dev/null +++ b/drivers/gpu/drm/omap/omap_gpu.c @@ -0,0 +1,752 @@ +/* + * linux/drivers/gpu/drm/omap/omap_gpu.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/omap_gpu.h> +#include "omap_gpu_priv.h" + +#include "drm_crtc_helper.h" +#include "drm_fb_helper.h" + +#define DRIVER_NAME MODULE_NAME +#define DRIVER_DESC "OMAP GPU" +#define DRIVER_DATE "20110403" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +struct drm_device *drm_device; + +/* TODO: think about how to handle more than one plugin.. ie. some ops + * me might want to stop on the first plugin that doesn't return an + * error, etc.. + */ +LIST_HEAD(plugin_list); + +/* 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; + +/* + * 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_gpu_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_DPI: + if (!strcmp(dssdev->name, "dvi")) + return DRM_MODE_CONNECTOR_DVID; + default: + return DRM_MODE_CONNECTOR_Unknown; + } +} + +static int omap_gpu_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; + } +} + +static void dump_video_chains(void) +{ + int i; + + DBG("dumping video chains: "); + for (i = 0; i < omap_dss_get_num_overlays(); i++) { + struct omap_overlay *ovl = omap_dss_get_overlay(i); + 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_gpu_platform_data *pdata = dev->dev->platform_data; + struct omap_gpu_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; + + 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->get_edid)) { + dev_warn(dev->dev, "%s driver does not support " + "get_timings or get_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; + + notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); + notifier->notifier_call = omap_gpu_notifier; + omap_dss_add_notify(dssdev, notifier); + + 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; + if (!(connected_connectors & (1 << idx))) { + struct drm_encoder * encoder = + omap_connector_attached_encoder( + priv->connectors[idx]); + 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); + + 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++) { + if (create_encoder(pdata->mgr_ids[i])) { + goto fail; + } + } + + 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; + } + if (create_connector(dssdev)) { + goto fail; + } + } + + j = 0; + for (i = 0; i < pdata->ovl_cnt; i++) { + if (create_crtc(pdata->ovl_ids[i])) { + goto fail; + } + } + } 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++) { + if (create_encoder(i)) { + goto fail; + } + } + + for_each_dss_dev(dssdev) { + if (create_connector(dssdev)) { + goto fail; + } + } + + j = 0; + for (i = 0; i < max_overlays; i++) { + if (create_crtc(i)) { + goto fail; + } + } + } + + /* 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 = 640; + dev->mode_config.min_height = 480; + + /* 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; + + drm_kms_helper_poll_init(dev); + + return 0; + +fail: + /* TODO: cleanup what has been created so far */ + return -EINVAL; +} + +/* + * 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_gpu_private *priv; + struct omap_gpu_plugin *plugin; + 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; + } + + 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; + } + + loaded = true; + + list_for_each_entry(plugin, &plugin_list, list) { + ret = plugin->load(dev, flags); + } + + return 0; +} + +static int dev_unload(struct drm_device *dev) +{ + struct omap_gpu_plugin *plugin; + int ret; + + DBG("unload: dev=%p", dev); + + list_for_each_entry(plugin, &plugin_list, list) { + ret = plugin->unload(dev); + } + + drm_kms_helper_poll_fini(dev); + + loaded = false; + + return 0; +} + +static int dev_open(struct drm_device *dev, struct drm_file *file) +{ + struct omap_gpu_plugin *plugin; + bool found_pvr = false; + int ret; + + file->driver_priv = NULL; + + DBG("open: dev=%p, file=%p", dev, file); + + list_for_each_entry(plugin, &plugin_list, list) { + if (!strcmp(DRIVER_NAME "_pvr", plugin->name)) { + found_pvr = true; + break; + } + } + + if (!found_pvr) { + DBG("open: PVR submodule not loaded.. let's try now"); + request_module(DRIVER_NAME "_pvr"); + } + + list_for_each_entry(plugin, &plugin_list, list) { + ret = plugin->open(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) +{ + struct omap_gpu_plugin *plugin; + int ret; + + DBG("postclose: dev=%p, file=%p", dev, file); + + list_for_each_entry(plugin, &plugin_list, list) { + ret = plugin->release(dev, file); + } + + return; +} + +/** + * 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); +} + +/** + * Called by \c drm_device_is_agp. Typically used to determine if a + * card is really attached to AGP or not. + * + * \param dev DRM device handle + * + * \returns + * One of three values is returned depending on whether or not the + * card is absolutely \b not AGP (return of 0), absolutely \b is AGP + * (return of 1), or may or may not be AGP (return of 2). + */ +static int dev_device_is_agp(struct drm_device *dev) +{ + return 0; +} + +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 int fop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct omap_gpu_plugin *plugin; + int ret = 0; + + list_for_each_entry(plugin, &plugin_list, list) { + ret = plugin->mmap(file, vma); + if (!ret) { + /* on first plugin that succeeds, bail out of iteration */ + return ret; + } + } + + return ret; +} + +static struct drm_driver omap_gpu_driver; + +static int pdev_suspend(struct platform_device *pDevice, pm_message_t state) +{ + DBG("pdev_suspend"); + return 0; +} + +static int pdev_resume(struct platform_device *device) +{ + DBG("pdev_resume"); + return 0; +} + +static void pdev_shutdown(struct platform_device *device) +{ + DBG("pdev_shutdown"); +} + +static int pdev_probe(struct platform_device *device) +{ + DBG("pdev_probe: %s", device->name); + return drm_get_platform_dev(device, &omap_gpu_driver); +} + +static int pdev_remove(struct platform_device *device) +{ + DBG("pdev_remove"); + drm_put_dev(drm_device); + return 0; +} + +struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {{0}}; + +static struct drm_driver omap_gpu_driver = { + .driver_features = DRIVER_HAVE_IRQ | + DRIVER_USE_PLATFORM_DEVICE | DRIVER_MODESET, + .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, + .device_is_agp = dev_device_is_agp, + .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, + .ioctls = ioctls, + .num_ioctls = 0, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, + .mmap = fop_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, + }, + .platform_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = pdev_probe, + .remove = pdev_remove, + .suspend = pdev_suspend, + .resume = pdev_resume, + .shutdown = pdev_shutdown, + }, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +int omap_gpu_register_plugin(struct omap_gpu_plugin *plugin) +{ + struct drm_device *dev = drm_device; + int i; + + DBG("register plugin: %p (%s)", plugin, plugin->name); + + /* XXX: PVR code uses drm_file->driver_priv... + * need to come up with some sane way to handle this. + */ + + list_add_tail(&plugin->list, &plugin_list); + + /* register the plugin's ioctl's */ + for (i = 0; i < plugin->num_ioctls; i++) { + int nr = i + plugin->ioctl_start; + + /* check for out of bounds ioctl nr or already registered ioctl */ + if (nr > ARRAY_SIZE(ioctls) || ioctls[nr].func) { + dev_err(dev->dev, "invalid ioctl: %d (nr=%d)\n", i, nr); + return -EINVAL; + } + + DBG("register ioctl: %d %08x", nr, plugin->ioctls[i].cmd); + + ioctls[nr] = plugin->ioctls[i]; + + if (nr >= omap_gpu_driver.num_ioctls) { + omap_gpu_driver.num_ioctls = nr + 1; + } + } + + if (loaded) { + plugin->load(dev, 0); + } + + return 0; +} +EXPORT_SYMBOL(omap_gpu_register_plugin); + +int omap_gpu_unregister_plugin(struct omap_gpu_plugin *plugin) +{ + list_del(&plugin->list); + return 0; +} +EXPORT_SYMBOL(omap_gpu_unregister_plugin); + +struct fb_info * omap_gpu_get_fbdev(struct drm_device *dev) +{ + struct omap_gpu_private *priv = dev->dev_private; + return priv->fbdev->fbdev; +} +EXPORT_SYMBOL(omap_gpu_get_fbdev); + +static int __init omap_gpu_init(void) +{ + DBG("init"); + return drm_init(&omap_gpu_driver); +} + +static void __exit omap_gpu_fini(void) +{ + DBG("fini"); + drm_exit(&omap_gpu_driver); +} + +/* need late_initcall() so we load after dss_driver's are loaded */ +late_initcall(omap_gpu_init); +module_exit(omap_gpu_fini); + +MODULE_AUTHOR("Rob Clark <rob@ti.com>"); +MODULE_DESCRIPTION("OMAP DRM Display Driver"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/omap/omap_gpu_priv.h b/drivers/gpu/drm/omap/omap_gpu_priv.h new file mode 100644 index 00000000000..4af361d83b5 --- /dev/null +++ b/drivers/gpu/drm/omap/omap_gpu_priv.h @@ -0,0 +1,80 @@ +/* + * linux/drivers/gpu/drm/omap/omap_gpu_priv.h + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAP_GPU_PRIV_H__ +#define __OMAP_GPU_PRIV_H__ + +#include <plat/display.h> +#include <linux/module.h> + +#define DBG(fmt,...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) +#define VERB(fmt,...) do { } while (0) /* verbose debug */ + +#define MODULE_NAME "omap_gpu" + +struct omap_gpu_private { + int num_crtcs; + struct drm_crtc *crtcs[8]; + int num_encoders; + struct drm_encoder *encoders[8]; + int num_connectors; + struct drm_connector *connectors[8]; + + struct drm_fb_helper *fbdev; + + /* for now, we statically create a single framebuffer per device, since + * there is not yet any good way to dynamically allocate/free contiguous + * memory.. + */ + struct drm_framebuffer *fb; +}; + +struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev); +void omap_fbdev_update(struct drm_fb_helper *helper, + struct drm_framebuffer *fb); + +struct drm_crtc * omap_crtc_init(struct drm_device *dev, + struct omap_overlay *ovl); +struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc); + +struct drm_encoder * omap_encoder_init(struct drm_device *dev, + struct omap_overlay_manager *mgr); +struct omap_overlay_manager * omap_encoder_get_manager( + struct drm_encoder *encoder); +struct drm_encoder * omap_connector_attached_encoder ( + struct drm_connector *connector); +enum drm_connector_status omap_connector_detect( + struct drm_connector *connector, bool force); + +struct drm_connector * omap_connector_init(struct drm_device *dev, + int connector_type, struct omap_dss_device *dssdev); +void omap_connector_mode_set(struct drm_connector *connector, + struct drm_display_mode *mode); +void omap_connector_flush(struct drm_connector *connector, + int x, int y, int w, int h); +void omap_connector_dpms(struct drm_connector *connector, int mode); + +struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd); +struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev, + struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd); +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, + void **vaddr, unsigned long *paddr, int *screen_width); + +#endif /* __OMAP_GPU_PRIV_H__ */ diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 02d5c415f49..99768d9d91d 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -675,7 +675,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_ENCODER_CAP_RECORD *cap_record; u16 caps = 0; - while (record->ucRecordType > 0 && + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_ENCODER_CAP_RECORD_TYPE: @@ -720,7 +721,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) break; } - while (record->ucRecordType > 0 && + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: @@ -782,10 +784,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_HPD_INT_RECORD *hpd_record; ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; - while (record->ucRecordType > 0 - && record-> - ucRecordType <= - ATOM_MAX_OBJECT_RECORD_NUMBER) { + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: i2c_record = diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 318cc40df92..418c399d3ef 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -418,6 +418,8 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_POSITION_Y, -2456, 2565, 4, 0); } + + input_set_events_per_packet(input, 60); } if (report_undeciphered) { diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index aa186cf6c51..e06e045bf90 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -836,8 +836,8 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = { }, }, - { } #endif + { } }; static bool broken_olpc_ec; @@ -851,8 +851,8 @@ static const struct dmi_system_id __initconst olpc_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "XO"), }, }, - { } #endif + { } }; void __init synaptics_module_init(void) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 80a3ae3c00b..c0cff64a1ae 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -534,7 +534,7 @@ static ssize_t lp5521_selftest(struct device *dev, } /* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); static struct attribute *lp5521_led_attributes[] = { @@ -548,15 +548,15 @@ static struct attribute_group lp5521_led_attribute_group = { }; /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); static struct attribute *lp5521_attributes[] = { diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index d0c4068ecdd..e19fed25f13 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -713,7 +713,7 @@ static ssize_t store_current(struct device *dev, } /* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); static struct attribute *lp5523_led_attributes[] = { @@ -727,21 +727,21 @@ static struct attribute_group lp5523_led_attribute_group = { }; /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUSR, show_engine1_leds, store_engine1_leds); -static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUSR, show_engine2_leds, store_engine2_leds); -static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUSR, show_engine3_leds, store_engine3_leds); -static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); static struct attribute *lp5523_attributes[] = { diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index ecdffa6aac6..c0902465bdf 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -441,6 +441,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" depends on I2C && VIDEO_V4L2 + select MFD_CORE select MFD_WL1273_CORE select FW_LOADER ---help--- diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index df33a1d188b..a794ae62aeb 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -764,10 +764,8 @@ static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) } ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, vid_resol, &cmd_status); - if (ret || cmd_status) { - mutex_unlock(&pd->lock); + if (ret || cmd_status) return -EBUSY; - } pix_def->pixelformat = pix->pixelformat; /* save it */ pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index cb01209754e..47a96708e54 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -719,14 +719,14 @@ static int usbhs_enable(struct device *dev) gpio_request(pdata->ehci_data->reset_gpio_port[0], "USB1 PHY reset"); gpio_direction_output - (pdata->ehci_data->reset_gpio_port[0], 1); + (pdata->ehci_data->reset_gpio_port[0], 0); } if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) { gpio_request(pdata->ehci_data->reset_gpio_port[1], "USB2 PHY reset"); gpio_direction_output - (pdata->ehci_data->reset_gpio_port[1], 1); + (pdata->ehci_data->reset_gpio_port[1], 0); } /* Hold the PHY in RESET for enough time till DIR is high */ @@ -906,11 +906,11 @@ static int usbhs_enable(struct device *dev) if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) gpio_set_value - (pdata->ehci_data->reset_gpio_port[0], 0); + (pdata->ehci_data->reset_gpio_port[0], 1); if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) gpio_set_value - (pdata->ehci_data->reset_gpio_port[1], 0); + (pdata->ehci_data->reset_gpio_port[1], 1); } end_count: diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 46b3439673e..16d7179e2f9 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -249,11 +249,11 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev, static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); -static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO, ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); -static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO, ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); -static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO, ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); static struct attribute *ep93xx_pwm_attrs[] = { diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index ea5cfe2c3a0..24386a804ea 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -3645,6 +3645,7 @@ static void myri10ge_free_slices(struct myri10ge_priv *mgp) dma_free_coherent(&pdev->dev, bytes, ss->fw_stats, ss->fw_stats_bus); ss->fw_stats = NULL; + netif_napi_del(&ss->napi); } } kfree(mgp->ss); diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index 587498e140b..3de98cb002e 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -901,7 +901,7 @@ static int netxen_nic_set_flags(struct net_device *netdev, u32 data) struct netxen_adapter *adapter = netdev_priv(netdev); int hw_lro; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) return -EINVAL; if (!(adapter->capabilities & NX_FW_CAPABILITY_HW_LRO)) diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 4c14510e2a8..45b2755d6cb 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c @@ -1003,7 +1003,7 @@ static int qlcnic_set_flags(struct net_device *netdev, u32 data) struct qlcnic_adapter *adapter = netdev_priv(netdev); int hw_lro; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) return -EINVAL; if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO)) diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 39c17cecb8b..0cdff2baaa3 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -6726,7 +6726,7 @@ static int s2io_ethtool_set_flags(struct net_device *dev, u32 data) int rc = 0; int changed = 0; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(dev, data, ETH_FLAG_LRO)) return -EINVAL; if (data & ETH_FLAG_LRO) { diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 81254be85b9..51f2ef142a5 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -304,8 +304,8 @@ vmxnet3_set_flags(struct net_device *netdev, u32 data) u8 lro_present = (netdev->features & NETIF_F_LRO) == 0 ? 0 : 1; unsigned long flags; - if (data & ~ETH_FLAG_LRO) - return -EOPNOTSUPP; + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) + return -EINVAL; if (lro_requested ^ lro_present) { /* toggle the LRO feature*/ diff --git a/drivers/net/vxge/vxge-ethtool.c b/drivers/net/vxge/vxge-ethtool.c index 1dd3a21b3a4..c5eb034107f 100644 --- a/drivers/net/vxge/vxge-ethtool.c +++ b/drivers/net/vxge/vxge-ethtool.c @@ -1117,8 +1117,8 @@ static int vxge_set_flags(struct net_device *dev, u32 data) struct vxgedev *vdev = netdev_priv(dev); enum vxge_hw_status status; - if (data & ~ETH_FLAG_RXHASH) - return -EOPNOTSUPP; + if (ethtool_invalid_flags(dev, data, ETH_FLAG_RXHASH)) + return -EINVAL; if (!!(data & ETH_FLAG_RXHASH) == vdev->devh->config.rth_en) return 0; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a09d15f7aa6..0848e099547 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1063,6 +1063,8 @@ static int ath9k_start(struct ieee80211_hw *hw) "Starting driver with initial channel: %d MHz\n", curchan->center_freq); + ath9k_ps_wakeup(sc); + mutex_lock(&sc->mutex); if (ath9k_wiphy_started(sc)) { @@ -1179,6 +1181,8 @@ static int ath9k_start(struct ieee80211_hw *hw) mutex_unlock: mutex_unlock(&sc->mutex); + ath9k_ps_restore(sc); + return r; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 07b7804aec5..5c9d83b103f 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1699,8 +1699,8 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf, u8 tidno; spin_lock_bh(&txctl->txq->axq_lock); - - if (ieee80211_is_data_qos(hdr->frame_control) && txctl->an) { + if ((sc->sc_flags & SC_OP_TXAGGR) && txctl->an && + ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; tid = ATH_AN_2_TID(txctl->an, tidno); diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 3d5566e7af0..ff0f5ba14b2 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1536,7 +1536,7 @@ static void dma_rx(struct b43_dmaring *ring, int *slot) dmaaddr = meta->dmaaddr; goto drop_recycle_buffer; } - if (unlikely(len > ring->rx_buffersize)) { + if (unlikely(len + ring->frameoffset > ring->rx_buffersize)) { /* The data did not fit into one descriptor buffer * and is split over multiple buffers. * This should never happen, as we try to allocate buffers diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index a01c2100f16..e8a80a1251b 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h @@ -163,7 +163,7 @@ struct b43_dmadesc_generic { /* DMA engine tuning knobs */ #define B43_TXRING_SLOTS 256 #define B43_RXRING_SLOTS 64 -#define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN +#define B43_DMA0_RX_BUFFERSIZE (B43_DMA0_RX_FRAMEOFFSET + IEEE80211_MAX_FRAME_LEN) /* Pointer poison */ #define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 9e6f31355ee..c0cd307dc2e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -241,7 +241,7 @@ struct iwl_eeprom_enhanced_txpwr { /* 6x00 Specific */ #define EEPROM_6000_TX_POWER_VERSION (4) -#define EEPROM_6000_EEPROM_VERSION (0x434) +#define EEPROM_6000_EEPROM_VERSION (0x423) /* 6x50 Specific */ #define EEPROM_6050_TX_POWER_VERSION (4) diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 9b344a921e7..e18358725b6 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -56,6 +56,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ + {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ @@ -68,6 +69,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ + {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 54917a28139..e2a528da364 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -2810,10 +2810,7 @@ void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); /* Wait for DMA, ignore error */ @@ -2823,9 +2820,6 @@ void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 0); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); - rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); } EXPORT_SYMBOL_GPL(rt2800_disable_radio); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 3b3f1e45ab3..37a38b5a2a8 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -475,39 +475,23 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) { - u32 reg; - - rt2800_disable_radio(rt2x00dev); - - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001280); - - rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, ®); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); - rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg); - - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + if (rt2x00_is_soc(rt2x00dev)) { + rt2800_disable_radio(rt2x00dev); + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); + } } static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - /* - * Always put the device to sleep (even when we intend to wakeup!) - * if the device is booting and wasn't asleep it will return - * failure when attempting to wakeup. - */ - rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2); - if (state == STATE_AWAKE) { - rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0); + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0x02); rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKUP); + } else if (state == STATE_SLEEP) { + rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, 0xffffffff); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, 0xffffffff); + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0x01, 0xff, 0x01); } return 0; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 9597a03242c..2b77a291aa9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1031,8 +1031,10 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) * Stop all work. */ cancel_work_sync(&rt2x00dev->intf_work); - cancel_work_sync(&rt2x00dev->rxdone_work); - cancel_work_sync(&rt2x00dev->txdone_work); + if (rt2x00_is_usb(rt2x00dev)) { + cancel_work_sync(&rt2x00dev->rxdone_work); + cancel_work_sync(&rt2x00dev->txdone_work); + } /* * Free the tx status fifo. diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index e64403b6896..6ec06a4a4c6 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -204,7 +204,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) kfree(wl->nvs); - wl->nvs = kzalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); + if (len != sizeof(struct wl1271_nvs_file)) + return -EINVAL; + + wl->nvs = kzalloc(len, GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); ret = -ENOMEM; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3188cd96b33..bbdb4fd85b9 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -69,6 +69,7 @@ struct pcie_link_state { }; static int aspm_disabled, aspm_force, aspm_clear_state; +static bool aspm_support_enabled = true; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -896,6 +897,7 @@ static int __init pcie_aspm_disable(char *str) { if (!strcmp(str, "off")) { aspm_disabled = 1; + aspm_support_enabled = false; printk(KERN_INFO "PCIe ASPM is disabled\n"); } else if (!strcmp(str, "force")) { aspm_force = 1; @@ -930,3 +932,8 @@ int pcie_aspm_enabled(void) } EXPORT_SYMBOL(pcie_aspm_enabled); +bool pcie_aspm_support_enabled(void) +{ + return aspm_support_enabled; +} +EXPORT_SYMBOL(pcie_aspm_support_enabled); diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 38b34a73866..fa54ba70657 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -222,6 +222,7 @@ struct acer_debug { static struct rfkill *wireless_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *threeg_rfkill; +static bool rfkill_inited; /* Each low-level interface must define at least some of the following */ struct wmi_interface { @@ -1161,9 +1162,13 @@ static int acer_rfkill_set(void *data, bool blocked) { acpi_status status; u32 cap = (unsigned long)data; - status = set_u32(!blocked, cap); - if (ACPI_FAILURE(status)) - return -ENODEV; + + if (rfkill_inited) { + status = set_u32(!blocked, cap); + if (ACPI_FAILURE(status)) + return -ENODEV; + } + return 0; } @@ -1187,14 +1192,16 @@ static struct rfkill *acer_rfkill_register(struct device *dev, return ERR_PTR(-ENOMEM); status = get_device_status(&state, cap); - if (ACPI_SUCCESS(status)) - rfkill_init_sw_state(rfkill_dev, !state); err = rfkill_register(rfkill_dev); if (err) { rfkill_destroy(rfkill_dev); return ERR_PTR(err); } + + if (ACPI_SUCCESS(status)) + rfkill_set_sw_state(rfkill_dev, !state); + return rfkill_dev; } @@ -1229,6 +1236,8 @@ static int acer_rfkill_init(struct device *dev) } } + rfkill_inited = true; + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 37268e97de4..afeb5469708 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -485,7 +485,7 @@ ds1511_nvram_write(struct file *filp, struct kobject *kobj, static struct bin_attribute ds1511_nvram_attr = { .attr = { .name = "nvram", - .mode = S_IRUGO | S_IWUGO, + .mode = S_IRUGO | S_IWUSR, }, .size = DS1511_RAM_MAX, .read = ds1511_nvram_read, diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index f905ecb5704..01543d297b5 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1847,7 +1847,7 @@ store_priv_session_##field(struct device *dev, \ #define iscsi_priv_session_rw_attr(field, format) \ iscsi_priv_session_attr_show(field, format) \ iscsi_priv_session_attr_store(field) \ -static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUGO, \ +static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \ show_priv_session_##field, \ store_priv_session_##field) iscsi_priv_session_rw_attr(recovery_tmo, "%d"); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 7f5a6a86f82..3b00e907b91 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -390,9 +390,9 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, len = (desc_ptr[2] << 8) + desc_ptr[3]; /* skip past overall descriptor */ desc_ptr += len + 4; - if (ses_dev->page10) - addl_desc_ptr = ses_dev->page10 + 8; } + if (ses_dev->page10) + addl_desc_ptr = ses_dev->page10 + 8; type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; components = 0; for (i = 0; i < types; i++, type_ptr += 4) { diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c index 45a627d77b4..09e596a506b 100644 --- a/drivers/staging/hv/channel.c +++ b/drivers/staging/hv/channel.c @@ -76,14 +76,14 @@ static void vmbus_setevent(struct vmbus_channel *channel) if (channel->offermsg.monitor_allocated) { /* Each u32 represents 32 channels */ - set_bit(channel->offermsg.child_relid & 31, + sync_set_bit(channel->offermsg.child_relid & 31, (unsigned long *) gVmbusConnection.SendInterruptPage + (channel->offermsg.child_relid >> 5)); monitorpage = gVmbusConnection.MonitorPages; monitorpage++; /* Get the child to parent monitor page */ - set_bit(channel->monitor_bit, + sync_set_bit(channel->monitor_bit, (unsigned long *)&monitorpage->trigger_group [channel->monitor_grp].pending); @@ -99,7 +99,7 @@ static void VmbusChannelClearEvent(struct vmbus_channel *channel) if (Channel->offermsg.monitor_allocated) { /* Each u32 represents 32 channels */ - clear_bit(Channel->offermsg.child_relid & 31, + sync_clear_bit(Channel->offermsg.child_relid & 31, (unsigned long *)gVmbusConnection.SendInterruptPage + (Channel->offermsg.child_relid >> 5)); @@ -107,7 +107,7 @@ static void VmbusChannelClearEvent(struct vmbus_channel *channel) (struct hv_monitor_page *)gVmbusConnection.MonitorPages; monitorPage++; /* Get the child to parent monitor page */ - clear_bit(Channel->monitor_bit, + sync_clear_bit(Channel->monitor_bit, (unsigned long *)&monitorPage->trigger_group [Channel->monitor_grp].Pending); } diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c index c2e298ff483..0739eb7b6ee 100644 --- a/drivers/staging/hv/connection.c +++ b/drivers/staging/hv/connection.c @@ -281,7 +281,7 @@ void VmbusOnEvents(void) for (dword = 0; dword < maxdword; dword++) { if (recvInterruptPage[dword]) { for (bit = 0; bit < 32; bit++) { - if (test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) { + if (sync_test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) { relid = (dword << 5) + bit; DPRINT_DBG(VMBUS, "event detected for relid - %d", relid); @@ -320,7 +320,7 @@ int VmbusPostMessage(void *buffer, size_t bufferLen) int VmbusSetEvent(u32 childRelId) { /* Each u32 represents 32 channels */ - set_bit(childRelId & 31, + sync_set_bit(childRelId & 31, (unsigned long *)gVmbusConnection.SendInterruptPage + (childRelId >> 5)); diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c index b41c9640b72..f433addaae5 100644 --- a/drivers/staging/hv/netvsc_drv.c +++ b/drivers/staging/hv/netvsc_drv.c @@ -46,6 +46,7 @@ struct net_device_context { /* point back to our device context */ struct vm_device *device_ctx; unsigned long avail; + struct work_struct work; }; struct netvsc_driver_context { @@ -225,6 +226,7 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj, unsigned int status) { struct vm_device *device_ctx = to_vm_device(device_obj); + struct net_device_context *ndev_ctx; struct net_device *net = dev_get_drvdata(&device_ctx->device); if (!net) { @@ -237,6 +239,8 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj, netif_carrier_on(net); netif_wake_queue(net); netif_notify_peers(net); + ndev_ctx = netdev_priv(net); + schedule_work(&ndev_ctx->work); } else { netif_carrier_off(net); netif_stop_queue(net); @@ -336,6 +340,25 @@ static const struct net_device_ops device_ops = { .ndo_set_mac_address = eth_mac_addr, }; +/* + * Send GARP packet to network peers after migrations. + * After Quick Migration, the network is not immediately operational in the + * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add + * another netif_notify_peers() into a scheduled work, otherwise GARP packet + * will not be sent after quick migration, and cause network disconnection. + */ +static void netvsc_send_garp(struct work_struct *w) +{ + struct net_device_context *ndev_ctx; + struct net_device *net; + + msleep(20); + ndev_ctx = container_of(w, struct net_device_context, work); + net = dev_get_drvdata(&ndev_ctx->device_ctx->device); + netif_notify_peers(net); +} + + static int netvsc_probe(struct device *device) { struct driver_context *driver_ctx = @@ -364,6 +387,7 @@ static int netvsc_probe(struct device *device) net_device_ctx->device_ctx = device_ctx; net_device_ctx->avail = ring_size; dev_set_drvdata(device, net); + INIT_WORK(&net_device_ctx->work, netvsc_send_garp); /* Notify the netvsc driver of the new device */ ret = net_drv_obj->base.OnDeviceAdd(device_obj, &device_info); diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c index 84fdb64d3ce..87e6cf2086f 100644 --- a/drivers/staging/hv/vmbus_drv.c +++ b/drivers/staging/hv/vmbus_drv.c @@ -291,7 +291,7 @@ static int vmbus_on_isr(struct hv_driver *drv) event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; /* Since we are a child, we only need to check bit 0 */ - if (test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { + if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { DPRINT_DBG(VMBUS, "received event %d", event->flags32[0]); ret |= 0x2; } diff --git a/drivers/staging/hv/vmbus_private.h b/drivers/staging/hv/vmbus_private.h index 07f6d22eeab..c75b2d7fb2f 100644 --- a/drivers/staging/hv/vmbus_private.h +++ b/drivers/staging/hv/vmbus_private.h @@ -31,6 +31,7 @@ #include "channel_mgmt.h" #include "ring_buffer.h" #include <linux/list.h> +#include <asm/sync_bitops.h> /* diff --git a/drivers/staging/iio/imu/adis16400.h b/drivers/staging/iio/imu/adis16400.h index 6ff33e1ad8c..90e90f0e65e 100644 --- a/drivers/staging/iio/imu/adis16400.h +++ b/drivers/staging/iio/imu/adis16400.h @@ -17,7 +17,8 @@ #ifndef SPI_ADIS16400_H_ #define SPI_ADIS16400_H_ -#define ADIS16400_STARTUP_DELAY 220 /* ms */ +#define ADIS16400_STARTUP_DELAY 290 /* ms */ +#define ADIS16400_MTEST_DELAY 90 /* ms */ #define ADIS16400_READ_REG(a) a #define ADIS16400_WRITE_REG(a) ((a) | 0x80) diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index cfb108a1545..2107edb3ebc 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -93,7 +93,6 @@ static int adis16400_spi_write_reg_16(struct device *dev, .tx_buf = st->tx + 2, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; @@ -137,7 +136,6 @@ static int adis16400_spi_read_reg_16(struct device *dev, .rx_buf = st->rx, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; @@ -375,7 +373,7 @@ static int adis16400_self_test(struct device *dev) dev_err(dev, "problem starting self test"); goto err_ret; } - + msleep(ADIS16400_MTEST_DELAY); adis16400_check_status(dev); err_ret: @@ -497,12 +495,12 @@ err_ret: _reg) static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_X, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_XGYRO_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_YGYRO_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_ZGYRO_OFF); static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_X, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_XACCL_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_YACCL_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_ZACCL_OFF); static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16400_read_14bit_signed, diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index 33293fba9bc..da28cb4288a 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -122,12 +122,10 @@ static int adis16400_spi_read_burst(struct device *dev, u8 *rx) .tx_buf = st->tx, .bits_per_word = 8, .len = 2, - .cs_change = 0, }, { .rx_buf = rx, .bits_per_word = 8, .len = 24, - .cs_change = 1, }, }; @@ -162,9 +160,10 @@ static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) work_trigger_to_ring); struct iio_ring_buffer *ring = st->indio_dev->ring; - int i = 0; + int i = 0, j; s16 *data; size_t datasize = ring->access.get_bytes_per_datum(ring); + unsigned long mask = ring->scan_mask; data = kmalloc(datasize , GFP_KERNEL); if (data == NULL) { @@ -174,9 +173,12 @@ static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) if (ring->scan_count) if (adis16400_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) - for (; i < ring->scan_count; i++) + for (; i < ring->scan_count; i++) { + j = __ffs(mask); + mask &= ~(1 << j); data[i] = be16_to_cpup( - (__be16 *)&(st->rx[i*2])); + (__be16 *)&(st->rx[j*2])); + } /* Guaranteed to be aligned with 8 byte boundary */ if (ring->scan_timestamp) diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c index ae6ac82754a..8e60332efae 100644 --- a/drivers/staging/usbip/stub_rx.c +++ b/drivers/staging/usbip/stub_rx.c @@ -170,33 +170,23 @@ static int tweak_set_configuration_cmd(struct urb *urb) static int tweak_reset_device_cmd(struct urb *urb) { - struct usb_ctrlrequest *req; - __u16 value; - __u16 index; - int ret; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - value = le16_to_cpu(req->wValue); - index = le16_to_cpu(req->wIndex); - - usbip_uinfo("reset_device (port %d) to %s\n", index, - dev_name(&urb->dev->dev)); + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; - /* all interfaces should be owned by usbip driver, so just reset it. */ - ret = usb_lock_device_for_reset(urb->dev, NULL); - if (ret < 0) { - dev_err(&urb->dev->dev, "lock for reset\n"); - return ret; - } - - /* try to reset the device */ - ret = usb_reset_device(urb->dev); - if (ret < 0) - dev_err(&urb->dev->dev, "device reset\n"); + usbip_uinfo("reset_device %s\n", dev_name(&urb->dev->dev)); - usb_unlock_device(urb->dev); - - return ret; + /* + * usb_lock_device_for_reset caused a deadlock: it causes the driver + * to unbind. In the shutdown the rx thread is signalled to shut down + * but this thread is pending in the usb_lock_device_for_reset. + * + * Instead queue the reset. + * + * Unfortunatly an existing usbip connection will be dropped due to + * driver unbinding. + */ + usb_queue_reset_device(sdev->interface); + return 0; } /* diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c index d7136e2c86f..b7a493c1df4 100644 --- a/drivers/staging/usbip/stub_tx.c +++ b/drivers/staging/usbip/stub_tx.c @@ -169,7 +169,6 @@ static int stub_send_ret_submit(struct stub_device *sdev) struct stub_priv *priv, *tmp; struct msghdr msg; - struct kvec iov[3]; size_t txsize; size_t total_size = 0; @@ -179,28 +178,73 @@ static int stub_send_ret_submit(struct stub_device *sdev) struct urb *urb = priv->urb; struct usbip_header pdu_header; void *iso_buffer = NULL; + struct kvec *iov = NULL; + int iovnum = 0; txsize = 0; memset(&pdu_header, 0, sizeof(pdu_header)); memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); - usbip_dbg_stub_tx("setup txdata urb %p\n", urb); + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; + else + iovnum = 2; + + iov = kzalloc(iovnum * sizeof(struct kvec), GFP_KERNEL); + if (!iov) { + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iovnum = 0; /* 1. setup usbip_header */ setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", + pdu_header.base.seqnum, urb); + /*usbip_dump_header(pdu_header);*/ usbip_header_correct_endian(&pdu_header, 1); - iov[0].iov_base = &pdu_header; - iov[0].iov_len = sizeof(pdu_header); + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); + iovnum++; txsize += sizeof(pdu_header); /* 2. setup transfer buffer */ - if (usb_pipein(urb->pipe) && urb->actual_length > 0) { - iov[1].iov_base = urb->transfer_buffer; - iov[1].iov_len = urb->actual_length; + if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * For isochronous packets: actual length is the sum of + * the actual length of the individual, packets, but as + * the packet offsets are not changed there will be + * padding between the packets. To optimally use the + * bandwidth the padding is not transmitted. + */ + + int i; + for (i = 0; i < urb->number_of_packets; i++) { + iov[iovnum].iov_base = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + iov[iovnum].iov_len = urb->iso_frame_desc[i].actual_length; + iovnum++; + txsize += urb->iso_frame_desc[i].actual_length; + } + + if (txsize != sizeof(pdu_header) + urb->actual_length) { + dev_err(&sdev->interface->dev, + "actual length of urb (%d) does not match iso packet sizes (%d)\n", + urb->actual_length, txsize-sizeof(pdu_header)); + kfree(iov); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } } /* 3. setup iso_packet_descriptor */ @@ -211,32 +255,34 @@ static int stub_send_ret_submit(struct stub_device *sdev) if (!iso_buffer) { usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + kfree(iov); return -1; } - iov[2].iov_base = iso_buffer; - iov[2].iov_len = len; + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; txsize += len; + iovnum++; } - ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, - 3, txsize); + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, + iov, iovnum, txsize); if (ret != txsize) { dev_err(&sdev->interface->dev, "sendmsg failed!, retval %d for %zd\n", ret, txsize); + kfree(iov); kfree(iso_buffer); usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); return -1; } + kfree(iov); kfree(iso_buffer); - usbip_dbg_stub_tx("send txdata\n"); total_size += txsize; } - spin_lock_irqsave(&sdev->priv_lock, flags); list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 210ef16bab8..2108ca15542 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -334,10 +334,11 @@ void usbip_dump_header(struct usbip_header *pdu) usbip_udbg("CMD_UNLINK: seq %u\n", pdu->u.cmd_unlink.seqnum); break; case USBIP_RET_SUBMIT: - usbip_udbg("RET_SUBMIT: st %d al %u sf %d ec %d\n", + usbip_udbg("RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", pdu->u.ret_submit.status, pdu->u.ret_submit.actual_length, pdu->u.ret_submit.start_frame, + pdu->u.ret_submit.number_of_packets, pdu->u.ret_submit.error_count); case USBIP_RET_UNLINK: usbip_udbg("RET_UNLINK: status %d\n", pdu->u.ret_unlink.status); @@ -625,6 +626,7 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, rpdu->status = urb->status; rpdu->actual_length = urb->actual_length; rpdu->start_frame = urb->start_frame; + rpdu->number_of_packets = urb->number_of_packets; rpdu->error_count = urb->error_count; } else { /* vhci_rx.c */ @@ -632,6 +634,7 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, urb->status = rpdu->status; urb->actual_length = rpdu->actual_length; urb->start_frame = rpdu->start_frame; + urb->number_of_packets = rpdu->number_of_packets; urb->error_count = rpdu->error_count; } } @@ -700,11 +703,13 @@ static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, cpu_to_be32s(&pdu->status); cpu_to_be32s(&pdu->actual_length); cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); cpu_to_be32s(&pdu->error_count); } else { be32_to_cpus(&pdu->status); be32_to_cpus(&pdu->actual_length); be32_to_cpus(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); be32_to_cpus(&pdu->error_count); } } @@ -830,6 +835,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) int size = np * sizeof(*iso); int i; int ret; + int total_length = 0; if (!usb_pipeisoc(urb->pipe)) return 0; @@ -859,19 +865,75 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) return -EPIPE; } + for (i = 0; i < np; i++) { iso = buff + (i * sizeof(*iso)); usbip_iso_pakcet_correct_endian(iso, 0); usbip_pack_iso(iso, &urb->iso_frame_desc[i], 0); + total_length += urb->iso_frame_desc[i].actual_length; } kfree(buff); + if (total_length != urb->actual_length) { + dev_err(&urb->dev->dev, + "total length of iso packets (%d) not equal to actual length of buffer (%d)\n", + total_length, urb->actual_length); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + return ret; } EXPORT_SYMBOL_GPL(usbip_recv_iso); +/* + * This functions restores the padding which was removed for optimizing + * the bandwidth during transfer over tcp/ip + * + * buffer and iso packets need to be stored and be in propeper endian in urb + * before calling this function + */ +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb) +{ + int np = urb->number_of_packets; + int i; + int ret; + int actualoffset = urb->actual_length; + + if (!usb_pipeisoc(urb->pipe)) + return 0; + + /* if no packets or length of data is 0, then nothing to unpack */ + if (np == 0 || urb->actual_length == 0) + return 0; + + /* + * if actual_length is transfer_buffer_length then no padding is + * present. + */ + if (urb->actual_length == urb->transfer_buffer_length) + return 0; + + /* + * loop over all packets from last to first (to prevent overwritting + * memory when padding) and move them into the proper place + */ + for (i = np-1; i > 0; i--) { + actualoffset -= urb->iso_frame_desc[i].actual_length; + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->transfer_buffer + actualoffset, + urb->iso_frame_desc[i].actual_length); + } + return ret; +} +EXPORT_SYMBOL_GPL(usbip_pad_iso); /* some members of urb must be substituted before. */ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h index d280e234e06..baa4c09bb98 100644 --- a/drivers/staging/usbip/usbip_common.h +++ b/drivers/staging/usbip/usbip_common.h @@ -393,6 +393,8 @@ void usbip_header_correct_endian(struct usbip_header *pdu, int send); int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); /* some members of urb must be substituted before. */ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); +/* some members of urb must be substituted before. */ +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb); void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c index bf699147094..109002a347b 100644 --- a/drivers/staging/usbip/vhci_rx.c +++ b/drivers/staging/usbip/vhci_rx.c @@ -99,6 +99,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, if (usbip_recv_iso(ud, urb) < 0) return; + /* restore the padding in iso packets */ + if (usbip_pad_iso(ud, urb) < 0) + return; if (usbip_dbg_flag_vhci_rx) usbip_dump_urb(urb); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 630ae7f3cd4..a6d3f2ffe81 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1030,6 +1030,8 @@ static void musb_shutdown(struct platform_device *pdev) struct musb *musb = dev_to_musb(&pdev->dev); unsigned long flags; + pm_runtime_get_sync(musb->controller); + spin_lock_irqsave(&musb->lock, flags); musb_platform_disable(musb); musb_generic_disable(musb); @@ -1040,6 +1042,8 @@ static void musb_shutdown(struct platform_device *pdev) musb_writeb(musb->mregs, MUSB_DEVCTL, 0); musb_platform_exit(musb); + pm_runtime_put(musb->controller); + /* FIXME power down */ } @@ -2197,11 +2201,9 @@ static int __exit musb_remove(struct platform_device *pdev) * - Peripheral mode: peripheral is deactivated (or never-activated) * - OTG mode: both roles are deactivated (or never-activated) */ - pm_runtime_get_sync(musb->controller); musb_exit_debugfs(musb); musb_shutdown(pdev); - pm_runtime_put(musb->controller); musb_free(musb); iounmap(ctrl_base); device_init_wakeup(&pdev->dev, 0); diff --git a/drivers/video/cfbfillrect.c b/drivers/video/cfbfillrect.c index ba9f58b2a5e..eb7a164f318 100644 --- a/drivers/video/cfbfillrect.c +++ b/drivers/video/cfbfillrect.c @@ -291,7 +291,7 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) p->fix.visual == FB_VISUAL_DIRECTCOLOR ) fg = ((u32 *) (p->pseudo_palette))[rect->color]; else - fg = rect->color; + fg = solid_color(p, rect->color); pat = pixel_to_pat(bpp, fg); diff --git a/drivers/video/cfbimgblt.c b/drivers/video/cfbimgblt.c index baed57d3cff..a12e545b92c 100644 --- a/drivers/video/cfbimgblt.c +++ b/drivers/video/cfbimgblt.c @@ -106,6 +106,7 @@ static inline void color_imageblit(const struct fb_image *image, else color = *src; color <<= FB_LEFT_POS(p, bpp); + color = solid_color(p, color); val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask); if (shift >= null_bits) { FB_WRITEL(val, dst++); @@ -290,8 +291,8 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image) fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color]; bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color]; } else { - fgcolor = image->fg_color; - bgcolor = image->bg_color; + fgcolor = solid_color(p, image->fg_color); + bgcolor = solid_color(p, image->bg_color); } if (32 % bpp == 0 && !start_index && !pitch_index && diff --git a/drivers/video/fb_draw.h b/drivers/video/fb_draw.h index 04c01faaf77..4ba0e0b1f16 100644 --- a/drivers/video/fb_draw.h +++ b/drivers/video/fb_draw.h @@ -15,6 +15,20 @@ comp(unsigned long a, unsigned long b, unsigned long mask) return ((a ^ b) & mask) ^ b; } +/* if framebuffer has alpha channel, mask in the appropriate + * bit(s) to make the specified color opaque + */ +static inline unsigned long +solid_color(struct fb_info *p, unsigned long color) +{ + if (p->var.transp.length > 0) { + u32 mask = (1 << p->var.transp.length) - 1; + mask <<= p->var.transp.offset; + color |= mask; + } + return color; +} + /* * Create a pattern with the given pixel's color */ diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 687c9c7d219..851833669fe 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -33,6 +33,8 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/i2c.h> +#include "../../edid.h" #include <plat/panel-generic-dpi.h> @@ -380,6 +382,100 @@ static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev, return dpi_check_timings(dssdev, timings); } +/* i2c / edid support */ + +#define DDC_ADDR 0x50 + +static int do_probe_ddc_edid(struct i2c_adapter *adapter, + unsigned char *buf, int block, int len) +{ + unsigned char start = block * EDID_LENGTH; + int i; + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &start, + }, { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + /* try at least 3 times, avoid miss for time-out */ + for (i = 0; i < 3; i++) { + if (i2c_transfer(adapter, msgs, 2) == 2) + return 0; + } + + return -1; +} + +static int generic_dpi_panel_get_edid(struct omap_dss_device *dssdev, + u8 *buf, int len) +{ + struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev); + struct i2c_adapter *adapter; + int i; + u8 *edid, *new; + + adapter = i2c_get_adapter(panel_data->i2c_bus_num); + if (!adapter) { + printk(KERN_ERR "Invalid I2C adapter, bus number: %d\n", + panel_data->i2c_bus_num); + return -EINVAL; + } + + if ((edid = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) + return -EINVAL; + + if (do_probe_ddc_edid(adapter, edid, 0, EDID_LENGTH)) + goto out; + + /* if there are extensions, probe more */ + if (edid[0x7e] != 0) { + new = krealloc(edid, (edid[0x7e] + 1) * EDID_LENGTH, + GFP_KERNEL); + if (!new) + goto out; + edid = new; + + for (i = 1; i <= edid[0x7e]; i++) { + if (do_probe_ddc_edid(adapter, + edid + i * EDID_LENGTH, + i, EDID_LENGTH)) + goto out; + } + } + + if (edid) { + memcpy(buf, edid, len); + kfree(edid); + return 0; + } + +out: + kfree(edid); + return -EINVAL; +} + +static bool generic_dpi_panel_is_detected(struct omap_dss_device *dssdev) +{ + struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev); + struct i2c_adapter *adapter; + unsigned char out; + + adapter = i2c_get_adapter(panel_data->i2c_bus_num); + if (!adapter) { + return omapdss_default_is_detected(dssdev); + } + + return (do_probe_ddc_edid(adapter, &out, 0, 1) == 0); +} + static struct omap_dss_driver dpi_driver = { .probe = generic_dpi_panel_probe, .remove = generic_dpi_panel_remove, @@ -393,6 +489,9 @@ static struct omap_dss_driver dpi_driver = { .get_timings = generic_dpi_panel_get_timings, .check_timings = generic_dpi_panel_check_timings, + .get_edid = generic_dpi_panel_get_edid, + .is_detected = generic_dpi_panel_is_detected, + .driver = { .name = "generic_dpi_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 1aa2ed1e786..7d1b9fecc43 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -434,6 +434,12 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver) if (dssdriver->get_recommended_bpp == NULL) dssdriver->get_recommended_bpp = omapdss_default_get_recommended_bpp; + if (!dssdriver->check_timings) + dssdriver->check_timings = omapdss_default_check_timings; + if (!dssdriver->get_timings) + dssdriver->get_timings = omapdss_default_get_timings; + if (!dssdriver->is_detected) + dssdriver->is_detected = omapdss_default_is_detected; return driver_register(&dssdriver->driver); } @@ -491,6 +497,9 @@ int omap_dss_register_device(struct omap_dss_device *dssdev) dssdev->dev.parent = &dss_bus; dssdev->dev.release = omap_dss_dev_release; dev_set_name(&dssdev->dev, "display%d", dev_num++); + + BLOCKING_INIT_NOTIFIER_HEAD(&dssdev->notifier); + return device_register(&dssdev->dev); } diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index a85a6f38b40..b1466d8d388 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/jiffies.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <plat/display.h> #include "dss.h" @@ -312,6 +313,32 @@ void omapdss_default_get_resolution(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_default_get_resolution); +void omapdss_default_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} +EXPORT_SYMBOL(omapdss_default_get_timings); + +int omapdss_default_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + return memcmp(&dssdev->panel.timings, timings, sizeof(*timings)); +} +EXPORT_SYMBOL(omapdss_default_check_timings); + +bool omapdss_default_is_detected(struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) { + /* show resume info for suspended displays */ + return dssdev->activate_after_resume; + } else { + return dssdev->state != OMAP_DSS_DISPLAY_DISABLED; + } +} +EXPORT_SYMBOL(omapdss_default_is_detected); + + void default_get_overlay_fifo_thresholds(enum omap_plane plane, u32 fifo_size, enum omap_burst_size *burst_size, u32 *fifo_low, u32 *fifo_high) @@ -614,3 +641,50 @@ void omap_dss_stop_device(struct omap_dss_device *dssdev) } EXPORT_SYMBOL(omap_dss_stop_device); +/* since omap_dss_update_size can be called in irq context, schedule work from + * work-queue to deliver notification to client.. + */ +struct notify_work { + struct work_struct work; + struct omap_dss_device *dssdev; + enum omap_dss_event evt; +}; + +static void notify_worker(struct work_struct *work) +{ + struct notify_work *nw = + container_of(work, struct notify_work, work); + struct omap_dss_device *dssdev = nw->dssdev; + blocking_notifier_call_chain(&dssdev->notifier, nw->evt, dssdev); + kfree(work); +} + +/** + * Called by lower level driver to notify about a change in resolution, etc. + */ +void omap_dss_notify(struct omap_dss_device *dssdev, enum omap_dss_event evt) +{ + struct notify_work *nw = + kmalloc(sizeof(struct notify_work), GFP_KERNEL); + if (nw) { + INIT_WORK(&nw->work, notify_worker); + nw->dssdev = dssdev; + nw->evt = evt; + schedule_work(&nw->work); + } +} +EXPORT_SYMBOL(omap_dss_notify); + +void omap_dss_add_notify(struct omap_dss_device *dssdev, + struct notifier_block *nb) +{ + blocking_notifier_chain_register(&dssdev->notifier, nb); +} +EXPORT_SYMBOL(omap_dss_add_notify); + +void omap_dss_remove_notify(struct omap_dss_device *dssdev, + struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&dssdev->notifier, nb); +} +EXPORT_SYMBOL(omap_dss_remove_notify); diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 2d3ca4ca4a0..6b0c0299527 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -145,6 +145,9 @@ static int dpi_basic_init(struct omap_dss_device *dssdev) { bool is_tft; + if (!dssdev->manager) + return -ENODEV; + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; dispc_set_parallel_interface_mode(dssdev->manager->id, diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 179a7a4f63b..b4f63d01180 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -165,9 +165,11 @@ static const enum omap_color_mode omap3_dss_supported_color_modes[] = { OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 | - OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY, + OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY | + OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBA32 | + OMAP_DSS_COLOR_RGBX32, /* OMAP_DSS_VIDEO2 */ OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig index aa33386c81f..231f568b9f9 100644 --- a/drivers/video/omap2/omapfb/Kconfig +++ b/drivers/video/omap2/omapfb/Kconfig @@ -1,6 +1,6 @@ menuconfig FB_OMAP2 tristate "OMAP2+ frame buffer support (EXPERIMENTAL)" - depends on FB && OMAP2_DSS + depends on FB && OMAP2_DSS && !DRM_OMAP select OMAP2_VRAM select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 diff --git a/drivers/video/sysfillrect.c b/drivers/video/sysfillrect.c index 33ee3d34f9d..92fe0be25f6 100644 --- a/drivers/video/sysfillrect.c +++ b/drivers/video/sysfillrect.c @@ -256,7 +256,7 @@ void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect) p->fix.visual == FB_VISUAL_DIRECTCOLOR ) fg = ((u32 *) (p->pseudo_palette))[rect->color]; else - fg = rect->color; + fg = solid_color(p, rect->color); pat = pixel_to_pat( bpp, fg); diff --git a/drivers/video/sysimgblt.c b/drivers/video/sysimgblt.c index 186c6f607be..bdc45dca24e 100644 --- a/drivers/video/sysimgblt.c +++ b/drivers/video/sysimgblt.c @@ -14,6 +14,7 @@ #include <linux/string.h> #include <linux/fb.h> #include <asm/types.h> +#include "fb_draw.h" #define DEBUG @@ -80,6 +81,7 @@ static void color_imageblit(const struct fb_image *image, struct fb_info *p, else color = *src; color <<= FB_LEFT_POS(p, bpp); + color = solid_color(p, color); val |= FB_SHIFT_HIGH(p, color, shift); if (shift >= null_bits) { *dst++ = val; @@ -265,8 +267,8 @@ void sys_imageblit(struct fb_info *p, const struct fb_image *image) fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color]; bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color]; } else { - fgcolor = image->fg_color; - bgcolor = image->bg_color; + fgcolor = solid_color(p, image->fg_color); + bgcolor = solid_color(p, image->bg_color); } if (32 % bpp == 0 && !start_index && !pitch_index && diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 596ba604e78..51b5551b4e3 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -202,7 +202,6 @@ static struct miscdevice davinci_wdt_miscdev = { static int __devinit davinci_wdt_probe(struct platform_device *pdev) { int ret = 0, size; - struct resource *res; struct device *dev = &pdev->dev; wdt_clk = clk_get(dev, NULL); @@ -216,31 +215,31 @@ static int __devinit davinci_wdt_probe(struct platform_device *pdev) dev_info(dev, "heartbeat %d sec\n", heartbeat); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "failed to get memory region resource\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -ENOENT; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (!wdt_base) { dev_err(dev, "failed to map memory region\n"); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return -ENOMEM; } ret = misc_register(&davinci_wdt_miscdev); if (ret < 0) { dev_err(dev, "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; } else { set_bit(WDT_DEVICE_INITED, &wdt_status); } @@ -253,8 +252,7 @@ static int __devexit davinci_wdt_remove(struct platform_device *pdev) { misc_deregister(&davinci_wdt_miscdev); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 3053ff05ca4..1fe9bc5a965 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -270,7 +270,6 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) { int ret = 0; int size; - struct resource *res; struct device *dev = &pdev->dev; struct max63xx_timeout *table; @@ -294,21 +293,19 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) max63xx_pdev = pdev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "failed to get memory region resource\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -ENOENT; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (!wdt_base) { dev_err(dev, "failed to map memory region\n"); ret = -ENOMEM; @@ -326,8 +323,8 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) out_unmap: iounmap(wdt_base); out_request: - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return ret; } @@ -336,8 +333,7 @@ static int __devexit max63xx_wdt_remove(struct platform_device *pdev) { misc_deregister(&max63xx_wdt_miscdev); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index bf5b97c546e..8c8c7d54497 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -254,7 +254,6 @@ static struct miscdevice pnx4008_wdt_miscdev = { static int __devinit pnx4008_wdt_probe(struct platform_device *pdev) { int ret = 0, size; - struct resource *res; if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; @@ -262,42 +261,42 @@ static int __devinit pnx4008_wdt_probe(struct platform_device *pdev) printk(KERN_INFO MODULE_NAME "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { printk(KERN_INFO MODULE_NAME "failed to get memory region resouce\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); + size = resource_size(wdt_mem); - if (wdt_mem == NULL) { + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); return -ENOENT; } - wdt_base = (void __iomem *)IO_ADDRESS(res->start); + wdt_base = (void __iomem *)IO_ADDRESS(wdt_mem->start); wdt_clk = clk_get(&pdev->dev, NULL); if (IS_ERR(wdt_clk)) { ret = PTR_ERR(wdt_clk); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; goto out; } ret = clk_enable(wdt_clk); if (ret) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; + clk_put(wdt_clk); goto out; } ret = misc_register(&pnx4008_wdt_miscdev); if (ret < 0) { printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; clk_disable(wdt_clk); clk_put(wdt_clk); } else { @@ -320,8 +319,7 @@ static int __devexit pnx4008_wdt_remove(struct platform_device *pdev) clk_put(wdt_clk); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } return 0; diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index ae53662c29b..8303c576c57 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -402,7 +402,6 @@ static inline void s3c2410wdt_cpufreq_deregister(void) static int __devinit s3c2410wdt_probe(struct platform_device *pdev) { - struct resource *res; struct device *dev; unsigned int wtcon; int started = 0; @@ -416,20 +415,19 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) /* get the memory region for the watchdog timer */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "no memory resource specified\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -EBUSY; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (wdt_base == NULL) { dev_err(dev, "failed to ioremap() region\n"); ret = -EINVAL; @@ -524,8 +522,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) iounmap(wdt_base); err_req: - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return ret; } @@ -545,8 +543,7 @@ static int __devexit s3c2410wdt_remove(struct platform_device *dev) iounmap(wdt_base); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; return 0; } diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 808372883e8..c7ea4bedfe6 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -42,6 +42,7 @@ #define PFX TCO_MODULE_NAME ": " /* internal variables */ +static u32 tcobase_phys; static void __iomem *tcobase; static unsigned int pm_iobase; static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ @@ -305,10 +306,18 @@ static unsigned char __devinit sp5100_tco_setupdevice(void) /* Low three bits of BASE0 are reserved. */ val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8); + if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, + "SP5100 TCO")) { + printk(KERN_ERR PFX "mmio address 0x%04x already in use\n", + val); + goto unreg_region; + } + tcobase_phys = val; + tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); if (tcobase == 0) { printk(KERN_ERR PFX "failed to get tcobase address\n"); - goto unreg_region; + goto unreg_mem_region; } /* Enable watchdog decode bit */ @@ -346,7 +355,8 @@ static unsigned char __devinit sp5100_tco_setupdevice(void) /* Done */ return 1; - iounmap(tcobase); +unreg_mem_region: + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); unreg_region: release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); exit: @@ -401,6 +411,7 @@ static int __devinit sp5100_tco_init(struct platform_device *dev) exit: iounmap(tcobase); + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); return ret; } @@ -414,6 +425,7 @@ static void __devexit sp5100_tco_cleanup(void) /* Deregister */ misc_deregister(&sp5100_tco_miscdev); iounmap(tcobase); + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); } |