drm/i915: Update to match Linux 3.8.13

This update brings initial support for Haswell GPUs.

Tested by:	Many users of FreeBSD, PC-BSD and HardenedBSD
Relnotes:	yes
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D5554
This commit is contained in:
Jean-Sébastien Pédron 2016-03-08 20:33:02 +00:00
parent a87488d1e4
commit 740be6d755
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=296548
57 changed files with 19874 additions and 8796 deletions

View File

@ -250,6 +250,10 @@ struct agp_i810_driver {
void (*chipset_flush)(device_t);
};
static struct {
struct intel_gtt base;
} intel_private;
static const struct agp_i810_driver agp_i810_i810_driver = {
.chiptype = CHIP_I810,
.gen = 1,
@ -526,6 +530,29 @@ static const struct agp_i810_driver agp_i810_hsw_driver = {
.chipset_flush = agp_i810_chipset_flush,
};
static const struct agp_i810_driver agp_i810_valleyview_driver = {
.chiptype = CHIP_SB,
.gen = 7,
.busdma_addr_mask_sz = 40,
.res_spec = agp_g4x_res_spec,
.check_active = agp_sb_check_active,
.set_desc = agp_i810_set_desc,
.dump_regs = agp_sb_dump_regs,
.get_stolen_size = agp_sb_get_stolen_size,
.get_gtt_mappable_entries = agp_i915_get_gtt_mappable_entries,
.get_gtt_total_entries = agp_sb_get_gtt_total_entries,
.install_gatt = agp_g4x_install_gatt,
.deinstall_gatt = agp_i830_deinstall_gatt,
.write_gtt = agp_sb_write_gtt,
.install_gtt_pte = agp_sb_install_gtt_pte,
.read_gtt_pte = agp_g4x_read_gtt_pte,
.read_gtt_pte_paddr = agp_sb_read_gtt_pte_paddr,
.set_aperture = agp_i915_set_aperture,
.chipset_flush_setup = agp_i810_chipset_flush_setup,
.chipset_flush_teardown = agp_i810_chipset_flush_teardown,
.chipset_flush = agp_i810_chipset_flush,
};
/* For adding new devices, devid is the id of the graphics controller
* (pci:0:2:0, for example). The placeholder (usually at pci:0:2:1) for the
* second head should never be added. The bridge_offset is the offset to
@ -763,39 +790,199 @@ static const struct agp_i810_match {
},
{
.devid = 0x04028086,
.name = "Haswell desktop GT1",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x04128086,
.name = "Haswell desktop GT2",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x040a8086,
.name = "Haswell server GT1",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x041a8086,
.name = "Haswell server GT2",
.name = "Haswell GT1 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x04068086,
.name = "Haswell mobile GT1",
.name = "Haswell GT1 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x040A8086,
.name = "Haswell GT1 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x04128086,
.name = "Haswell GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x04168086,
.name = "Haswell mobile GT2",
.name = "Haswell GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0c168086,
.name = "Haswell SDV",
.devid = 0x041A8086,
.name = "Haswell GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x04228086,
.name = "Haswell GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x04268086,
.name = "Haswell GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x042A8086,
.name = "Haswell GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A028086,
.name = "Haswell ULT GT1 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A068086,
.name = "Haswell ULT GT1 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A0A8086,
.name = "Haswell ULT GT1 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A128086,
.name = "Haswell ULT GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A168086,
.name = "Haswell ULT GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A1A8086,
.name = "Haswell ULT GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A228086,
.name = "Haswell ULT GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A268086,
.name = "Haswell ULT GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0A2A8086,
.name = "Haswell ULT GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C028086,
.name = "Haswell SDV GT1 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C068086,
.name = "Haswell SDV GT1 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C0A8086,
.name = "Haswell SDV GT1 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C128086,
.name = "Haswell SDV GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C168086,
.name = "Haswell SDV GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C1A8086,
.name = "Haswell SDV GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C228086,
.name = "Haswell SDV GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C268086,
.name = "Haswell SDV GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0C2A8086,
.name = "Haswell SDV GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D028086,
.name = "Haswell CRW GT1 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D068086,
.name = "Haswell CRW GT1 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D0A8086,
.name = "Haswell CRW GT1 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D128086,
.name = "Haswell CRW GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D168086,
.name = "Haswell CRW GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D1A8086,
.name = "Haswell CRW GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D228086,
.name = "Haswell CRW GT2 desktop",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D268086,
.name = "Haswell CRW GT2 mobile",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x0D2A8086,
.name = "Haswell CRW GT2 server",
.driver = &agp_i810_hsw_driver
},
{
.devid = 0x01558086,
.name = "Valleyview (desktop)",
.driver = &agp_i810_valleyview_driver
},
{
.devid = 0x01578086,
.name = "Valleyview (mobile)",
.driver = &agp_i810_valleyview_driver
},
{
.devid = 0x0F308086,
.name = "Valleyview (mobile)",
.driver = &agp_i810_valleyview_driver
},
{
.devid = 0,
}
@ -2285,6 +2472,10 @@ agp_intel_gtt_get(device_t dev)
res.gtt_mappable_entries = sc->gtt_mappable_entries;
res.do_idle_maps = 0;
res.scratch_page_dma = VM_PAGE_TO_PHYS(bogus_page);
if (sc->agp.as_aperture != NULL)
res.gma_bus_addr = rman_get_start(sc->agp.as_aperture);
else
res.gma_bus_addr = 0;
return (res);
}
@ -2588,11 +2779,12 @@ intel_gtt_insert_pages(u_int first_entry, u_int num_entries, vm_page_t *pages,
pages, flags);
}
struct intel_gtt
struct intel_gtt *
intel_gtt_get(void)
{
return (agp_intel_gtt_get(intel_agp));
intel_private.base = agp_intel_gtt_get(intel_agp);
return (&intel_private.base);
}
int

View File

@ -33,6 +33,7 @@
#define AGP_AGP_I810_H
#include <sys/param.h>
#include <sys/rman.h>
#include <sys/sglist.h>
#include <vm/vm.h>
@ -51,24 +52,23 @@
struct intel_gtt {
/* Size of memory reserved for graphics by the BIOS */
u_int stolen_size;
unsigned int stolen_size;
/* Total number of gtt entries. */
u_int gtt_total_entries;
/*
* Part of the gtt that is mappable by the cpu, for those
* chips where this is not the full gtt.
*/
u_int gtt_mappable_entries;
/*
* Always false.
*/
u_int do_idle_maps;
/*
* Share the scratch page dma with ppgtts.
*/
unsigned int gtt_total_entries;
/* Part of the gtt that is mappable by the cpu, for those chips where
* this is not the full gtt. */
unsigned int gtt_mappable_entries;
/* Whether i915 needs to use the dmar apis or not. */
unsigned int needs_dmar : 1;
/* Whether we idle the gpu before mapping/unmapping */
unsigned int do_idle_maps : 1;
/* Share the scratch page dma with ppgtts. */
vm_paddr_t scratch_page_dma;
vm_page_t scratch_page;
/* for ppgtt PDE access */
uint32_t *gtt;
/* needed for ioremap in drm/i915 */
bus_addr_t gma_bus_addr;
};
struct intel_gtt agp_intel_gtt_get(device_t dev);
@ -83,7 +83,7 @@ void agp_intel_gtt_insert_sg_entries(device_t dev, struct sglist *sg_list,
void agp_intel_gtt_insert_pages(device_t dev, u_int first_entry,
u_int num_entries, vm_page_t *pages, u_int flags);
struct intel_gtt intel_gtt_get(void);
struct intel_gtt *intel_gtt_get(void);
int intel_gtt_chipset_flush(void);
void intel_gtt_unmap_memory(struct sglist *sg_list);
void intel_gtt_clear_range(u_int first_entry, u_int num_entries);

View File

@ -238,7 +238,6 @@ struct drm_device;
__func__ , ##__VA_ARGS__); \
} while (0)
/*@}*/
/***********************************************************************/
@ -700,6 +699,8 @@ struct drm_driver {
void (*postclose) (struct drm_device *, struct drm_file *);
void (*lastclose) (struct drm_device *);
int (*unload) (struct drm_device *);
int (*suspend) (struct drm_device *, pm_message_t state);
int (*resume) (struct drm_device *);
int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv);
int (*dma_quiescent) (struct drm_device *);
int (*context_dtor) (struct drm_device *dev, int context);
@ -1118,7 +1119,7 @@ struct drm_device {
char busid_str[128];
int modesetting;
drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */
const drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */
};
#define DRM_SWITCH_POWER_ON 0
@ -1581,6 +1582,8 @@ static __inline__ void drm_core_dropmap(struct drm_local_map *map)
{
}
#include <dev/drm2/drm_mem_util.h>
extern int drm_fill_in_dev(struct drm_device *dev,
struct drm_driver *driver);
extern void drm_cancel_fill_in_dev(struct drm_device *dev);
@ -1758,9 +1761,11 @@ struct dmi_system_id {
bool dmi_check_system(const struct dmi_system_id *);
/* Device setup support (drm_drv.c) */
int drm_probe_helper(device_t kdev, drm_pci_id_list_t *idlist);
int drm_attach_helper(device_t kdev, drm_pci_id_list_t *idlist,
int drm_probe_helper(device_t kdev, const drm_pci_id_list_t *idlist);
int drm_attach_helper(device_t kdev, const drm_pci_id_list_t *idlist,
struct drm_driver *driver);
int drm_generic_suspend(device_t kdev);
int drm_generic_resume(device_t kdev);
int drm_generic_detach(device_t kdev);
void drm_event_wakeup(struct drm_pending_event *e);

View File

@ -39,8 +39,8 @@ typedef uint64_t atomic64_t;
#define NB_BITS_PER_LONG (sizeof(long) * NBBY)
#define BITS_TO_LONGS(x) howmany(x, NB_BITS_PER_LONG)
#define atomic_read(p) (*(volatile u_int *)(p))
#define atomic_set(p, v) do { *(u_int *)(p) = (v); } while (0)
#define atomic_read(p) atomic_load_acq_int(p)
#define atomic_set(p, v) atomic_store_rel_int(p, v)
#define atomic64_read(p) atomic_load_acq_64(p)
#define atomic64_set(p, v) atomic_store_rel_64(p, v)
@ -78,6 +78,9 @@ typedef uint64_t atomic64_t;
#define cmpxchg(ptr, old, new) \
(atomic_cmpset_int((volatile u_int *)(ptr),(old),(new)) ? (old) : (0))
#define atomic_inc_not_zero(p) atomic_inc(p)
#define atomic_clear_mask(b, p) atomic_clear_int((p), (b))
static __inline u_long
find_first_zero_bit(const u_long *p, u_long max)
{

View File

@ -149,7 +149,7 @@ iic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num)
buf = msgs[m].buf;
reading = (msgs[m].flags & IIC_M_RD) != 0;
ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading);
if (ret != 0)
if (ret < 0)
break;
if (reading) {
for (b = 0; b < len; b++) {
@ -160,7 +160,7 @@ iic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num)
} else {
for (b = 0; b < len; b++) {
ret = iic_dp_aux_put_byte(idev, buf[b]);
if (ret != 0)
if (ret < 0)
break;
}
}

View File

@ -470,6 +470,14 @@ int drm_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int flags,
err_i1:
atomic_dec(&dev->ioctl_count);
if (retcode == -ERESTARTSYS) {
/*
* FIXME: Find where in i915 ERESTARTSYS should be
* converted to EINTR.
*/
DRM_DEBUG("ret = %d -> %d\n", retcode, -EINTR);
retcode = -EINTR;
}
if (retcode)
DRM_DEBUG("ret = %d\n", retcode);
if (retcode != 0 &&

View File

@ -40,7 +40,6 @@ struct list_head {
};
#define list_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
static __inline__ void
INIT_LIST_HEAD(struct list_head *head) {
@ -179,4 +178,123 @@ list_splice(const struct list_head *list, struct list_head *head)
void drm_list_sort(void *priv, struct list_head *head, int (*cmp)(void *priv,
struct list_head *a, struct list_head *b));
/* hlist, copied from sys/dev/ofed/linux/list.h */
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#define HLIST_HEAD_INIT { }
#define HLIST_HEAD(name) struct hlist_head name = HLIST_HEAD_INIT
#define INIT_HLIST_HEAD(head) (head)->first = NULL
#define INIT_HLIST_NODE(node) \
do { \
(node)->next = NULL; \
(node)->pprev = NULL; \
} while (0)
static inline int
hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int
hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline void
hlist_del(struct hlist_node *n)
{
if (n->next)
n->next->pprev = n->pprev;
*n->pprev = n->next;
}
static inline void
hlist_del_init(struct hlist_node *n)
{
if (hlist_unhashed(n))
return;
hlist_del(n);
INIT_HLIST_NODE(n);
}
static inline void
hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
n->next = h->first;
if (h->first)
h->first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
static inline void
hlist_add_before(struct hlist_node *n, struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static inline void
hlist_add_after(struct hlist_node *n, struct hlist_node *next)
{
next->next = n->next;
n->next = next;
next->pprev = &n->next;
if (next->next)
next->next->pprev = &next->next;
}
static inline void
hlist_move_list(struct hlist_head *old, struct hlist_head *new)
{
new->first = old->first;
if (new->first)
new->first->pprev = &new->first;
old->first = NULL;
}
#define hlist_entry(ptr, type, field) container_of(ptr, type, field)
#define hlist_for_each(p, head) \
for (p = (head)->first; p; p = p->next)
#define hlist_for_each_safe(p, n, head) \
for (p = (head)->first; p && ({ n = p->next; 1; }); p = n)
#define hlist_for_each_entry(tp, p, head, field) \
for (p = (head)->first; \
p ? (tp = hlist_entry(p, typeof(*tp), field)): NULL; p = p->next)
#define hlist_for_each_entry_continue(tp, p, field) \
for (p = (p)->next; \
p ? (tp = hlist_entry(p, typeof(*tp), field)): NULL; p = p->next)
#define hlist_for_each_entry_from(tp, p, field) \
for (; p ? (tp = hlist_entry(p, typeof(*tp), field)): NULL; p = p->next)
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
(pos) != 0 && ({ n = (pos)->next; \
tpos = hlist_entry((pos), typeof(*(tpos)), member); 1;}); \
pos = (n))
#endif /* _DRM_LINUX_LIST_H_ */

View File

@ -0,0 +1,59 @@
/*
* Copyright © 2008 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Jesse Barnes <jbarnes@virtuousgeek.org>
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifndef _DRM_MEM_UTIL_H_
#define _DRM_MEM_UTIL_H_
#include <sys/types.h>
#include <sys/malloc.h>
static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
{
if (size != 0 && nmemb > SIZE_MAX / size)
return NULL;
return malloc(nmemb * size, DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
}
/* Modeled after cairo's malloc_ab, it's like calloc but without the zeroing. */
static __inline__ void *drm_malloc_ab(size_t nmemb, size_t size)
{
if (size != 0 && nmemb > SIZE_MAX / size)
return NULL;
return malloc(nmemb * size, DRM_MEM_DRIVER, M_NOWAIT);
}
static __inline void drm_free_large(void *ptr)
{
free(ptr, DRM_MEM_DRIVER);
}
#endif

View File

@ -67,8 +67,27 @@ ns_to_timeval(const int64_t nsec)
return (tv);
}
static drm_pci_id_list_t *
drm_find_description(int vendor, int device, drm_pci_id_list_t *idlist)
/* Copied from OFED. */
unsigned long drm_linux_timer_hz_mask;
static void
drm_linux_timer_init(void *arg)
{
/*
* Compute an internal HZ value which can divide 2**32 to
* avoid timer rounding problems when the tick value wraps
* around 2**32:
*/
drm_linux_timer_hz_mask = 1;
while (drm_linux_timer_hz_mask < (unsigned long)hz)
drm_linux_timer_hz_mask *= 2;
drm_linux_timer_hz_mask--;
}
SYSINIT(drm_linux_timer, SI_SUB_DRIVERS, SI_ORDER_FIRST, drm_linux_timer_init, NULL);
static const drm_pci_id_list_t *
drm_find_description(int vendor, int device, const drm_pci_id_list_t *idlist)
{
int i = 0;
@ -87,9 +106,9 @@ drm_find_description(int vendor, int device, drm_pci_id_list_t *idlist)
* method.
*/
int
drm_probe_helper(device_t kdev, drm_pci_id_list_t *idlist)
drm_probe_helper(device_t kdev, const drm_pci_id_list_t *idlist)
{
drm_pci_id_list_t *id_entry;
const drm_pci_id_list_t *id_entry;
int vendor, device;
vendor = pci_get_vendor(kdev);
@ -118,7 +137,7 @@ drm_probe_helper(device_t kdev, drm_pci_id_list_t *idlist)
* method.
*/
int
drm_attach_helper(device_t kdev, drm_pci_id_list_t *idlist,
drm_attach_helper(device_t kdev, const drm_pci_id_list_t *idlist,
struct drm_driver *driver)
{
struct drm_device *dev;
@ -136,6 +155,55 @@ drm_attach_helper(device_t kdev, drm_pci_id_list_t *idlist,
return (ret);
}
int
drm_generic_suspend(device_t kdev)
{
struct drm_device *dev;
int error;
DRM_DEBUG_KMS("Starting suspend\n");
dev = device_get_softc(kdev);
if (dev->driver->suspend) {
pm_message_t state;
state.event = PM_EVENT_SUSPEND;
error = -dev->driver->suspend(dev, state);
if (error)
goto out;
}
error = bus_generic_suspend(kdev);
out:
DRM_DEBUG_KMS("Finished suspend: %d\n", error);
return error;
}
int
drm_generic_resume(device_t kdev)
{
struct drm_device *dev;
int error;
DRM_DEBUG_KMS("Starting resume\n");
dev = device_get_softc(kdev);
if (dev->driver->resume) {
error = -dev->driver->resume(dev);
if (error)
goto out;
}
error = bus_generic_resume(kdev);
out:
DRM_DEBUG_KMS("Finished resume: %d\n", error);
return error;
}
int
drm_generic_detach(device_t kdev)
{
@ -331,6 +399,42 @@ drm_clflush_virt_range(char *addr, unsigned long length)
#endif
}
void
hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
char *linebuf, size_t linebuflen, bool ascii __unused)
{
int i, j, c;
i = j = 0;
while (i < len && j <= linebuflen) {
c = ((const char *)buf)[i];
if (i != 0) {
if (i % rowsize == 0) {
/* Newline required. */
sprintf(linebuf + j, "\n");
++j;
} else if (i % groupsize == 0) {
/* Space required. */
sprintf(linebuf + j, " ");
++j;
}
}
if (j > linebuflen - 1)
break;
sprintf(linebuf + j, "%02X", c);
j += 2;
++i;
}
if (j <= linebuflen)
sprintf(linebuf + j, "\n");
}
#if DRM_LINUX
#include <sys/sysproto.h>

View File

@ -10,6 +10,7 @@ __FBSDID("$FreeBSD$");
#define _DRM_OS_FREEBSD_H_
#include <sys/fbio.h>
#include <sys/smp.h>
#if _BYTE_ORDER == _BIG_ENDIAN
#define __BIG_ENDIAN 4321
@ -24,10 +25,22 @@ __FBSDID("$FreeBSD$");
#endif
#ifndef __user
#define __user
#define __user
#endif
#ifndef __iomem
#define __iomem
#define __iomem
#endif
#ifndef __always_unused
#define __always_unused
#endif
#ifndef __must_check
#define __must_check
#endif
#ifndef __force
#define __force
#endif
#ifndef uninitialized_var
#define uninitialized_var(x) x
#endif
#define cpu_to_le16(x) htole16(x)
@ -69,9 +82,23 @@ typedef void irqreturn_t;
#define __exit
#define __read_mostly
#define WARN_ON(cond) KASSERT(!(cond), ("WARN ON: " #cond))
#define BUILD_BUG_ON(x) CTASSERT(!(x))
#define BUILD_BUG_ON_NOT_POWER_OF_2(x)
#ifndef WARN
#define WARN(condition, format, ...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
DRM_ERROR(format, ##__VA_ARGS__); \
unlikely(__ret_warn_on); \
})
#endif
#define WARN_ONCE(condition, format, ...) \
WARN(condition, format, ##__VA_ARGS__)
#define WARN_ON(cond) WARN(cond, "WARN ON: " #cond)
#define WARN_ON_SMP(cond) WARN_ON(cond)
#define BUG_ON(cond) KASSERT(!(cond), ("BUG ON: " #cond))
#define BUG() panic("BUG")
#define BUG_ON(cond) KASSERT(!(cond), ("BUG ON: " #cond " -> 0x%jx", (uintmax_t)(cond)))
#define unlikely(x) __builtin_expect(!!(x), 0)
#define likely(x) __builtin_expect(!!(x), 1)
#define container_of(ptr, type, member) ({ \
@ -93,6 +120,15 @@ typedef void irqreturn_t;
#define DRM_UDELAY(udelay) DELAY(udelay)
#define drm_msleep(x, msg) pause((msg), ((int64_t)(x)) * hz / 1000)
#define DRM_MSLEEP(msecs) drm_msleep((msecs), "drm_msleep")
#define get_seconds() time_second
#define ioread8(addr) *(volatile uint8_t *)((char *)addr)
#define ioread16(addr) *(volatile uint16_t *)((char *)addr)
#define ioread32(addr) *(volatile uint32_t *)((char *)addr)
#define iowrite8(data, addr) *(volatile uint8_t *)((char *)addr) = data;
#define iowrite16(data, addr) *(volatile uint16_t *)((char *)addr) = data;
#define iowrite32(data, addr) *(volatile uint32_t *)((char *)addr) = data;
#define DRM_READ8(map, offset) \
*(volatile u_int8_t *)(((vm_offset_t)(map)->handle) + \
@ -127,12 +163,18 @@ typedef void irqreturn_t;
#define DRM_WRITEMEMORYBARRIER() wmb()
#define DRM_MEMORYBARRIER() mb()
#define smp_rmb() rmb()
#define smp_wmb() wmb()
#define smp_mb__before_atomic_inc() mb()
#define smp_mb__after_atomic_inc() mb()
#define barrier() __compiler_membar()
#define do_div(a, b) ((a) /= (b))
#define div64_u64(a, b) ((a) / (b))
#define lower_32_bits(n) ((u32)(n))
#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
#define __set_bit(n, s) set_bit((n), (s))
#define __clear_bit(n, s) clear_bit((n), (s))
#define min_t(type, x, y) ({ \
type __min1 = (x); \
@ -148,6 +190,10 @@ typedef void irqreturn_t;
#define memcpy_fromio(a, b, c) memcpy((a), (b), (c))
#define memcpy_toio(a, b, c) memcpy((a), (b), (c))
#define VERIFY_READ VM_PROT_READ
#define VERIFY_WRITE VM_PROT_WRITE
#define access_ok(prot, p, l) useracc((p), (l), (prot))
/* XXXKIB what is the right code for the FreeBSD ? */
/* kib@ used ENXIO here -- dumbbell@ */
#define EREMOTEIO EIO
@ -170,8 +216,10 @@ typedef void irqreturn_t;
#define PCI_VENDOR_ID_SONY 0x104d
#define PCI_VENDOR_ID_VIA 0x1106
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define hweight32(i) bitcount32(i)
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define DIV_ROUND_CLOSEST(n,d) (((n) + (d) / 2) / (d))
#define div_u64(n, d) ((n) / (d))
#define hweight32(i) bitcount32(i)
static inline unsigned long
roundup_pow_of_two(unsigned long x)
@ -195,6 +243,8 @@ ror32(uint32_t word, unsigned int shift)
}
#define IS_ALIGNED(x, y) (((x) & ((y) - 1)) == 0)
#define round_down(x, y) rounddown2((x), (y))
#define round_up(x, y) roundup2((x), (y))
#define get_unaligned(ptr) \
({ __typeof__(*(ptr)) __tmp; \
memcpy(&__tmp, (ptr), sizeof(*(ptr))); __tmp; })
@ -251,7 +301,9 @@ abs64(int64_t x)
int64_t timeval_to_ns(const struct timeval *tv);
struct timeval ns_to_timeval(const int64_t nsec);
#define PAGE_ALIGN(addr) round_page(addr)
#define PAGE_ALIGN(addr) round_page(addr)
#define page_to_phys(x) VM_PAGE_TO_PHYS(x)
#define offset_in_page(x) ((x) & PAGE_MASK)
#define drm_get_device_from_kdev(_kdev) (((struct drm_minor *)(_kdev)->si_drv1)->dev)
@ -295,20 +347,193 @@ __get_user(size_t size, const void *ptr, void *x)
}
#define get_user(x, ptr) __get_user(sizeof(*ptr), (ptr), &(x))
static inline int
__copy_to_user_inatomic(void __user *to, const void *from, unsigned n)
{
return (copyout_nofault(from, to, n) != 0 ? n : 0);
}
#define __copy_to_user_inatomic_nocache(to, from, n) \
__copy_to_user_inatomic((to), (from), (n))
static inline unsigned long
__copy_from_user_inatomic(void *to, const void __user *from,
unsigned long n)
{
/*
* XXXKIB. Equivalent Linux function is implemented using
* MOVNTI for aligned moves. For unaligned head and tail,
* normal move is performed. As such, it is not incorrect, if
* only somewhat slower, to use normal copyin. All uses
* except shmem_pwrite_fast() have the destination mapped WC.
*/
return ((copyin_nofault(__DECONST(void *, from), to, n) != 0 ? n : 0));
}
#define __copy_from_user_inatomic_nocache(to, from, n) \
__copy_from_user_inatomic((to), (from), (n))
static inline int
fault_in_multipages_readable(const char __user *uaddr, int size)
{
char c;
int ret = 0;
const char __user *end = uaddr + size - 1;
if (unlikely(size == 0))
return ret;
while (uaddr <= end) {
ret = -copyin(uaddr, &c, 1);
if (ret != 0)
return -EFAULT;
uaddr += PAGE_SIZE;
}
/* Check whether the range spilled into the next page. */
if (((unsigned long)uaddr & ~PAGE_MASK) ==
((unsigned long)end & ~PAGE_MASK)) {
ret = -copyin(end, &c, 1);
}
return ret;
}
static inline int
fault_in_multipages_writeable(char __user *uaddr, int size)
{
int ret = 0;
char __user *end = uaddr + size - 1;
if (unlikely(size == 0))
return ret;
/*
* Writing zeroes into userspace here is OK, because we know that if
* the zero gets there, we'll be overwriting it.
*/
while (uaddr <= end) {
ret = subyte(uaddr, 0);
if (ret != 0)
return -EFAULT;
uaddr += PAGE_SIZE;
}
/* Check whether the range spilled into the next page. */
if (((unsigned long)uaddr & ~PAGE_MASK) ==
((unsigned long)end & ~PAGE_MASK))
ret = subyte(end, 0);
return ret;
}
enum __drm_capabilities {
CAP_SYS_ADMIN
};
static inline bool
capable(enum __drm_capabilities cap)
{
switch (cap) {
case CAP_SYS_ADMIN:
return DRM_SUSER(curthread);
}
}
#define to_user_ptr(x) ((void *)(uintptr_t)(x))
#define sigemptyset(set) SIGEMPTYSET(set)
#define sigaddset(set, sig) SIGADDSET(set, sig)
#define DRM_LOCK(dev) sx_xlock(&(dev)->dev_struct_lock)
#define DRM_UNLOCK(dev) sx_xunlock(&(dev)->dev_struct_lock)
extern unsigned long drm_linux_timer_hz_mask;
#define jiffies ticks
#define jiffies_to_msecs(x) (((int64_t)(x)) * 1000 / hz)
#define msecs_to_jiffies(x) (((int64_t)(x)) * hz / 1000)
#define timespec_to_jiffies(x) (((x)->tv_sec * 1000000 + (x)->tv_nsec) * hz / 1000000)
#define time_after(a,b) ((long)(b) - (long)(a) < 0)
#define time_after_eq(a,b) ((long)(b) - (long)(a) <= 0)
#define round_jiffies(j) ((unsigned long)(((j) + drm_linux_timer_hz_mask) & ~drm_linux_timer_hz_mask))
#define round_jiffies_up(j) round_jiffies(j) /* TODO */
#define round_jiffies_up_relative(j) round_jiffies_up(j) /* TODO */
#define wake_up(queue) wakeup((void *)queue)
#define wake_up_interruptible(queue) wakeup((void *)queue)
#define getrawmonotonic(ts) getnanouptime(ts)
#define wake_up(queue) wakeup_one((void *)queue)
#define wake_up_interruptible(queue) wakeup_one((void *)queue)
#define wake_up_all(queue) wakeup((void *)queue)
#define wake_up_interruptible_all(queue) wakeup((void *)queue)
struct completion {
unsigned int done;
struct mtx lock;
};
#define INIT_COMPLETION(c) ((c).done = 0);
static inline void
init_completion(struct completion *c)
{
mtx_init(&c->lock, "drmcompl", NULL, MTX_DEF);
c->done = 0;
}
static inline void
free_completion(struct completion *c)
{
mtx_destroy(&c->lock);
}
static inline void
complete_all(struct completion *c)
{
mtx_lock(&c->lock);
c->done++;
mtx_unlock(&c->lock);
wakeup(c);
}
static inline long
wait_for_completion_interruptible_timeout(struct completion *c,
unsigned long timeout)
{
unsigned long start_jiffies, elapsed_jiffies;
bool timeout_expired = false, awakened = false;
long ret = timeout;
start_jiffies = ticks;
mtx_lock(&c->lock);
while (c->done == 0 && !timeout_expired) {
ret = -msleep(c, &c->lock, PCATCH, "drmwco", timeout);
switch(ret) {
case -EWOULDBLOCK:
timeout_expired = true;
ret = 0;
break;
case -EINTR:
case -ERESTART:
ret = -ERESTARTSYS;
break;
case 0:
awakened = true;
break;
}
}
mtx_unlock(&c->lock);
if (awakened) {
elapsed_jiffies = ticks - start_jiffies;
ret = timeout > elapsed_jiffies ? timeout - elapsed_jiffies : 1;
}
return (ret);
}
MALLOC_DECLARE(DRM_MEM_DMA);
MALLOC_DECLARE(DRM_MEM_SAREA);
@ -356,6 +581,12 @@ typedef struct drm_pci_id_list
#if defined(__i386__) || defined(__amd64__)
#define CONFIG_ACPI
#define CONFIG_DRM_I915_KMS
#undef CONFIG_INTEL_IOMMU
#endif
#ifdef COMPAT_FREEBSD32
#define CONFIG_COMPAT
#endif
#define CONFIG_AGP 1
@ -364,19 +595,102 @@ typedef struct drm_pci_id_list
#define CONFIG_FB 1
extern const char *fb_mode_option;
#undef CONFIG_DEBUG_FS
#undef CONFIG_VGA_CONSOLE
#define EXPORT_SYMBOL(x)
#define EXPORT_SYMBOL_GPL(x)
#define MODULE_AUTHOR(author)
#define MODULE_DESCRIPTION(desc)
#define MODULE_LICENSE(license)
#define MODULE_PARM_DESC(name, desc)
#define MODULE_DEVICE_TABLE(name, list)
#define module_param_named(name, var, type, perm)
#define printk printf
#define pr_err DRM_ERROR
#define pr_warn DRM_WARNING
#define pr_warn_once DRM_WARNING
#define KERN_DEBUG ""
/* I2C compatibility. */
#define I2C_M_RD IIC_M_RD
#define I2C_M_WR IIC_M_WR
#define I2C_M_NOSTART IIC_M_NOSTART
struct fb_info * framebuffer_alloc(void);
void framebuffer_release(struct fb_info *info);
#define console_lock()
#define console_unlock()
#define console_trylock() true
#define PM_EVENT_SUSPEND 0x0002
#define PM_EVENT_QUIESCE 0x0008
#define PM_EVENT_PRETHAW PM_EVENT_QUIESCE
typedef struct pm_message {
int event;
} pm_message_t;
static inline int
pci_read_config_byte(device_t kdev, int where, u8 *val)
{
*val = (u8)pci_read_config(kdev, where, 1);
return (0);
}
static inline int
pci_write_config_byte(device_t kdev, int where, u8 val)
{
pci_write_config(kdev, where, val, 1);
return (0);
}
static inline int
pci_read_config_word(device_t kdev, int where, uint16_t *val)
{
*val = (uint16_t)pci_read_config(kdev, where, 2);
return (0);
}
static inline int
pci_write_config_word(device_t kdev, int where, uint16_t val)
{
pci_write_config(kdev, where, val, 2);
return (0);
}
static inline int
pci_read_config_dword(device_t kdev, int where, uint32_t *val)
{
*val = (uint32_t)pci_read_config(kdev, where, 4);
return (0);
}
static inline int
pci_write_config_dword(device_t kdev, int where, uint32_t val)
{
pci_write_config(kdev, where, val, 4);
return (0);
}
static inline void
on_each_cpu(void callback(void *data), void *data, int wait)
{
smp_rendezvous(NULL, callback, NULL, data);
}
void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
int groupsize, char *linebuf, size_t linebuflen, bool ascii);
#define KIB_NOTYET() \
do { \
if (drm_debug && drm_notyet) \

View File

@ -33,57 +33,89 @@
{0, 0, 0, NULL}
#define i915_PCI_IDS \
{0x8086, 0x0042, CHIP_I9XX|CHIP_I915, "Intel IronLake"}, \
{0x8086, 0x0046, CHIP_I9XX|CHIP_I915, "Intel IronLake"}, \
{0x8086, 0x0102, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \
{0x8086, 0x0106, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \
{0x8086, 0x010A, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \
{0x8086, 0x0112, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \
{0x8086, 0x0116, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \
{0x8086, 0x0122, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \
{0x8086, 0x0126, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \
{0x8086, 0x0152, CHIP_I9XX|CHIP_I915, "Intel IvyBridge"}, \
{0x8086, 0x0156, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (M)"}, \
{0x8086, 0x015A, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (S)"}, \
{0x8086, 0x0162, CHIP_I9XX|CHIP_I915, "Intel IvyBridge"}, \
{0x8086, 0x0166, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (M)"}, \
{0x8086, 0x016A, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (S)"}, \
{0x8086, 0x0402, CHIP_I9XX|CHIP_I915, "Intel Haswell"}, \
{0x8086, 0x0406, CHIP_I9XX|CHIP_I915, "Intel Haswell (M)"}, \
{0x8086, 0x040A, CHIP_I9XX|CHIP_I915, "Intel Haswell (S)"}, \
{0x8086, 0x0412, CHIP_I9XX|CHIP_I915, "Intel Haswell"}, \
{0x8086, 0x0416, CHIP_I9XX|CHIP_I915, "Intel Haswell (M)"}, \
{0x8086, 0x041A, CHIP_I9XX|CHIP_I915, "Intel Haswell (S)"}, \
{0x8086, 0x0C16, CHIP_I9XX|CHIP_I915, "Intel Haswell (SDV)"}, \
{0x8086, 0x2562, CHIP_I8XX, "Intel i845G GMCH"}, \
{0x8086, 0x2572, CHIP_I8XX, "Intel i865G GMCH"}, \
{0x8086, 0x2582, CHIP_I9XX|CHIP_I915, "Intel i915G"}, \
{0x8086, 0x258A, CHIP_I9XX|CHIP_I915, "Intel E7221 (i915)"}, \
{0x8086, 0x2592, CHIP_I9XX|CHIP_I915, "Intel i915GM"}, \
{0x8086, 0x2772, CHIP_I9XX|CHIP_I915, "Intel i945G"}, \
{0x8086, 0x27A2, CHIP_I9XX|CHIP_I915, "Intel i945GM"}, \
{0x8086, 0x27AE, CHIP_I9XX|CHIP_I915, "Intel i945GME"}, \
{0x8086, 0x2972, CHIP_I9XX|CHIP_I965, "Intel i946GZ"}, \
{0x8086, 0x2982, CHIP_I9XX|CHIP_I965, "Intel i965G"}, \
{0x8086, 0x2992, CHIP_I9XX|CHIP_I965, "Intel i965Q"}, \
{0x8086, 0x29A2, CHIP_I9XX|CHIP_I965, "Intel i965G"}, \
{0x8086, 0x29B2, CHIP_I9XX|CHIP_I915, "Intel Q35"}, \
{0x8086, 0x29C2, CHIP_I9XX|CHIP_I915, "Intel G33"}, \
{0x8086, 0x29D2, CHIP_I9XX|CHIP_I915, "Intel Q33"}, \
{0x8086, 0x2A02, CHIP_I9XX|CHIP_I965, "Intel i965GM"}, \
{0x8086, 0x2A12, CHIP_I9XX|CHIP_I965, "Intel i965GME/GLE"}, \
{0x8086, 0x2A42, CHIP_I9XX|CHIP_I965, "Mobile Intel® GM45 Express Chipset"}, \
{0x8086, 0x2E02, CHIP_I9XX|CHIP_I965, "Intel Eaglelake"}, \
{0x8086, 0x2E12, CHIP_I9XX|CHIP_I965, "Intel Q45/Q43"}, \
{0x8086, 0x2E22, CHIP_I9XX|CHIP_I965, "Intel G45/G43"}, \
{0x8086, 0x2E32, CHIP_I9XX|CHIP_I965, "Intel G41"}, \
{0x8086, 0x2E42, CHIP_I9XX|CHIP_I915, "Intel G43 ?"}, \
{0x8086, 0x2E92, CHIP_I9XX|CHIP_I915, "Intel G43 ?"}, \
{0x8086, 0x3577, CHIP_I8XX, "Intel i830M GMCH"}, \
{0x8086, 0x3582, CHIP_I8XX, "Intel i852GM/i855GM GMCH"}, \
{0x8086, 0x358E, CHIP_I8XX, "Intel i852GM/i855GM GMCH"}, \
{0x8086, 0xA001, CHIP_I9XX|CHIP_I965, "Intel Pineview"}, \
{0x8086, 0xA011, CHIP_I9XX|CHIP_I965, "Intel Pineview (M)"}, \
{0x8086, 0x0042, 0, "Intel IronLake"}, \
{0x8086, 0x0046, 0, "Intel IronLake"}, \
{0x8086, 0x0102, 0, "Intel SandyBridge"}, \
{0x8086, 0x0106, 0, "Intel SandyBridge (M)"}, \
{0x8086, 0x010A, 0, "Intel SandyBridge (M)"}, \
{0x8086, 0x0112, 0, "Intel SandyBridge"}, \
{0x8086, 0x0116, 0, "Intel SandyBridge (M)"}, \
{0x8086, 0x0122, 0, "Intel SandyBridge"}, \
{0x8086, 0x0126, 0, "Intel SandyBridge (M)"}, \
{0x8086, 0x0152, 0, "Intel IvyBridge"}, \
{0x8086, 0x0156, 0, "Intel IvyBridge (M)"}, \
{0x8086, 0x015A, 0, "Intel IvyBridge (S)"}, \
{0x8086, 0x0162, 0, "Intel IvyBridge"}, \
{0x8086, 0x0166, 0, "Intel IvyBridge (M)"}, \
{0x8086, 0x016A, 0, "Intel IvyBridge (S)"}, \
{0x8086, 0x0402, 0, "Intel Haswell (GT1 desktop)"}, \
{0x8086, 0x0406, 0, "Intel Haswell (GT1 mobile)"}, \
{0x8086, 0x040A, 0, "Intel Haswell (GT1 server)"}, \
{0x8086, 0x0412, 0, "Intel Haswell (GT2 desktop)"}, \
{0x8086, 0x0416, 0, "Intel Haswell (GT2 mobile)"}, \
{0x8086, 0x041A, 0, "Intel Haswell (GT2 server)"}, \
{0x8086, 0x0422, 0, "Intel Haswell (GT2 desktop)"}, \
{0x8086, 0x0426, 0, "Intel Haswell (GT2 mobile)"}, \
{0x8086, 0x042A, 0, "Intel Haswell (GT2 server)"}, \
{0x8086, 0x0A02, 0, "Intel Haswell (ULT GT1 desktop)"}, \
{0x8086, 0x0A06, 0, "Intel Haswell (ULT GT1 mobile)"}, \
{0x8086, 0x0A0A, 0, "Intel Haswell (ULT GT1 server)"}, \
{0x8086, 0x0A12, 0, "Intel Haswell (ULT GT2 desktop)"}, \
{0x8086, 0x0A16, 0, "Intel Haswell (ULT GT2 mobile)"}, \
{0x8086, 0x0A1A, 0, "Intel Haswell (ULT GT2 server)"}, \
{0x8086, 0x0A22, 0, "Intel Haswell (ULT GT2 desktop)"}, \
{0x8086, 0x0A26, 0, "Intel Haswell (ULT GT2 mobile)"}, \
{0x8086, 0x0A2A, 0, "Intel Haswell (ULT GT2 server)"}, \
{0x8086, 0x0C02, 0, "Intel Haswell (SDV GT1 desktop)"}, \
{0x8086, 0x0C06, 0, "Intel Haswell (SDV GT1 mobile)"}, \
{0x8086, 0x0C0A, 0, "Intel Haswell (SDV GT1 server)"}, \
{0x8086, 0x0C12, 0, "Intel Haswell (SDV GT2 desktop)"}, \
{0x8086, 0x0C16, 0, "Intel Haswell (SDV GT2 mobile)"}, \
{0x8086, 0x0C1A, 0, "Intel Haswell (SDV GT2 server)"}, \
{0x8086, 0x0C22, 0, "Intel Haswell (SDV GT2 desktop)"}, \
{0x8086, 0x0C26, 0, "Intel Haswell (SDV GT2 mobile)"}, \
{0x8086, 0x0C2A, 0, "Intel Haswell (SDV GT2 server)"}, \
{0x8086, 0x0D02, 0, "Intel Haswell (CRW GT1 desktop)"}, \
{0x8086, 0x0D06, 0, "Intel Haswell (CRW GT1 mobile)"}, \
{0x8086, 0x0D0A, 0, "Intel Haswell (CRW GT1 server)"}, \
{0x8086, 0x0D12, 0, "Intel Haswell (CRW GT2 desktop)"}, \
{0x8086, 0x0D16, 0, "Intel Haswell (CRW GT2 mobile)"}, \
{0x8086, 0x0D1A, 0, "Intel Haswell (CRW GT2 server)"}, \
{0x8086, 0x0D22, 0, "Intel Haswell (CRW GT2 desktop)"}, \
{0x8086, 0x0D26, 0, "Intel Haswell (CRW GT2 mobile)"}, \
{0x8086, 0x0D2A, 0, "Intel Haswell (CRW GT2 server)"}, \
{0x8086, 0x0155, 0, "Intel Valleyview (desktop)"}, \
{0x8086, 0x0157, 0, "Intel Valleyview (mobile)"}, \
{0x8086, 0x0F30, 0, "Intel Valleyview (mobile)"}, \
{0x8086, 0x2562, 0, "Intel i845G GMCH"}, \
{0x8086, 0x2572, 0, "Intel i865G GMCH"}, \
{0x8086, 0x2582, 0, "Intel i915G"}, \
{0x8086, 0x258A, 0, "Intel E7221 (i915)"}, \
{0x8086, 0x2592, 0, "Intel i915GM"}, \
{0x8086, 0x2772, 0, "Intel i945G"}, \
{0x8086, 0x27A2, 0, "Intel i945GM"}, \
{0x8086, 0x27AE, 0, "Intel i945GME"}, \
{0x8086, 0x2972, 0, "Intel i946GZ"}, \
{0x8086, 0x2982, 0, "Intel i965G"}, \
{0x8086, 0x2992, 0, "Intel i965Q"}, \
{0x8086, 0x29A2, 0, "Intel i965G"}, \
{0x8086, 0x29B2, 0, "Intel Q35"}, \
{0x8086, 0x29C2, 0, "Intel G33"}, \
{0x8086, 0x29D2, 0, "Intel Q33"}, \
{0x8086, 0x2A02, 0, "Intel i965GM"}, \
{0x8086, 0x2A12, 0, "Intel i965GME/GLE"}, \
{0x8086, 0x2A42, 0, "Mobile Intel® GM45 Express Chipset"}, \
{0x8086, 0x2E02, 0, "Intel Eaglelake"}, \
{0x8086, 0x2E12, 0, "Intel Q45/Q43"}, \
{0x8086, 0x2E22, 0, "Intel G45/G43"}, \
{0x8086, 0x2E32, 0, "Intel G41"}, \
{0x8086, 0x2E42, 0, "Intel G43 ?"}, \
{0x8086, 0x2E92, 0, "Intel G43 ?"}, \
{0x8086, 0x3577, 0, "Intel i830M GMCH"}, \
{0x8086, 0x3582, 0, "Intel i852GM/i855GM GMCH"}, \
{0x8086, 0x358E, 0, "Intel i852GM/i855GM GMCH"}, \
{0x8086, 0xA001, 0, "Intel Pineview"}, \
{0x8086, 0xA011, 0, "Intel Pineview (M)"}, \
{0, 0, 0, NULL}
#define imagine_PCI_IDS \

155
sys/dev/drm2/i915/dvo.h Normal file
View File

@ -0,0 +1,155 @@
/*
* Copyright © 2006 Eric Anholt
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef _INTEL_DVO_H
#define _INTEL_DVO_H
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/bus.h>
#include <dev/iicbus/iic.h>
#include <dev/iicbus/iiconf.h>
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm_crtc.h>
#include "intel_drv.h"
struct intel_dvo_device {
const char *name;
int type;
/* DVOA/B/C output register */
u32 dvo_reg;
/* GPIO register used for i2c bus to control this device */
u32 gpio;
int slave_addr;
const struct intel_dvo_dev_ops *dev_ops;
void *dev_priv;
device_t i2c_bus;
};
struct intel_dvo_dev_ops {
/*
* Initialize the device at startup time.
* Returns NULL if the device does not exist.
*/
bool (*init)(struct intel_dvo_device *dvo,
device_t i2cbus);
/*
* Called to allow the output a chance to create properties after the
* RandR objects have been created.
*/
void (*create_resources)(struct intel_dvo_device *dvo);
/*
* Turn on/off output.
*
* Because none of our dvo drivers support an intermediate power levels,
* we don't expose this in the interfac.
*/
void (*dpms)(struct intel_dvo_device *dvo, bool enable);
/*
* Callback for testing a video mode for a given output.
*
* This function should only check for cases where a mode can't
* be supported on the output specifically, and not represent
* generic CRTC limitations.
*
* \return MODE_OK if the mode is valid, or another MODE_* otherwise.
*/
int (*mode_valid)(struct intel_dvo_device *dvo,
struct drm_display_mode *mode);
/*
* Callback to adjust the mode to be set in the CRTC.
*
* This allows an output to adjust the clock or even the entire set of
* timings, which is used for panels with fixed timings or for
* buses with clock limitations.
*/
bool (*mode_fixup)(struct intel_dvo_device *dvo,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/*
* Callback for preparing mode changes on an output
*/
void (*prepare)(struct intel_dvo_device *dvo);
/*
* Callback for committing mode changes on an output
*/
void (*commit)(struct intel_dvo_device *dvo);
/*
* Callback for setting up a video mode after fixups have been made.
*
* This is only called while the output is disabled. The dpms callback
* must be all that's necessary for the output, to turn the output on
* after this function is called.
*/
void (*mode_set)(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/*
* Probe for a connected output, and return detect_status.
*/
enum drm_connector_status (*detect)(struct intel_dvo_device *dvo);
/*
* Probe the current hw status, returning true if the connected output
* is active.
*/
bool (*get_hw_state)(struct intel_dvo_device *dev);
/**
* Query the device for the modes it provides.
*
* This function may also update MonInfo, mm_width, and mm_height.
*
* \return singly-linked list of modes or NULL if no modes found.
*/
struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo);
/**
* Clean up driver-specific bits of the output
*/
void (*destroy) (struct intel_dvo_device *dvo);
/**
* Debugging hook to dump device registers to log file
*/
void (*dump_regs)(struct intel_dvo_device *dvo);
};
extern struct intel_dvo_dev_ops sil164_ops;
extern struct intel_dvo_dev_ops ch7xxx_ops;
extern struct intel_dvo_dev_ops ivch_ops;
extern struct intel_dvo_dev_ops tfp410_ops;
extern struct intel_dvo_dev_ops ch7017_ops;
extern struct intel_dvo_dev_ops ns2501_ops;
#endif /* _INTEL_DVO_H */

View File

@ -0,0 +1,418 @@
/*
* Copyright © 2006 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "dvo.h"
#define CH7017_TV_DISPLAY_MODE 0x00
#define CH7017_FLICKER_FILTER 0x01
#define CH7017_VIDEO_BANDWIDTH 0x02
#define CH7017_TEXT_ENHANCEMENT 0x03
#define CH7017_START_ACTIVE_VIDEO 0x04
#define CH7017_HORIZONTAL_POSITION 0x05
#define CH7017_VERTICAL_POSITION 0x06
#define CH7017_BLACK_LEVEL 0x07
#define CH7017_CONTRAST_ENHANCEMENT 0x08
#define CH7017_TV_PLL 0x09
#define CH7017_TV_PLL_M 0x0a
#define CH7017_TV_PLL_N 0x0b
#define CH7017_SUB_CARRIER_0 0x0c
#define CH7017_CIV_CONTROL 0x10
#define CH7017_CIV_0 0x11
#define CH7017_CHROMA_BOOST 0x14
#define CH7017_CLOCK_MODE 0x1c
#define CH7017_INPUT_CLOCK 0x1d
#define CH7017_GPIO_CONTROL 0x1e
#define CH7017_INPUT_DATA_FORMAT 0x1f
#define CH7017_CONNECTION_DETECT 0x20
#define CH7017_DAC_CONTROL 0x21
#define CH7017_BUFFERED_CLOCK_OUTPUT 0x22
#define CH7017_DEFEAT_VSYNC 0x47
#define CH7017_TEST_PATTERN 0x48
#define CH7017_POWER_MANAGEMENT 0x49
/** Enables the TV output path. */
#define CH7017_TV_EN (1 << 0)
#define CH7017_DAC0_POWER_DOWN (1 << 1)
#define CH7017_DAC1_POWER_DOWN (1 << 2)
#define CH7017_DAC2_POWER_DOWN (1 << 3)
#define CH7017_DAC3_POWER_DOWN (1 << 4)
/** Powers down the TV out block, and DAC0-3 */
#define CH7017_TV_POWER_DOWN_EN (1 << 5)
#define CH7017_VERSION_ID 0x4a
#define CH7017_DEVICE_ID 0x4b
#define CH7017_DEVICE_ID_VALUE 0x1b
#define CH7018_DEVICE_ID_VALUE 0x1a
#define CH7019_DEVICE_ID_VALUE 0x19
#define CH7017_XCLK_D2_ADJUST 0x53
#define CH7017_UP_SCALER_COEFF_0 0x55
#define CH7017_UP_SCALER_COEFF_1 0x56
#define CH7017_UP_SCALER_COEFF_2 0x57
#define CH7017_UP_SCALER_COEFF_3 0x58
#define CH7017_UP_SCALER_COEFF_4 0x59
#define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a
#define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b
#define CH7017_GPIO_INVERT 0x5c
#define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d
#define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e
#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f
/**< Low bits of horizontal active pixel input */
#define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x60
/** High bits of horizontal active pixel input */
#define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0)
/** High bits of vertical active line output */
#define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3)
#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x61
/**< Low bits of vertical active line output */
#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x62
/**< Low bits of horizontal active pixel output */
#define CH7017_LVDS_POWER_DOWN 0x63
/** High bits of horizontal active pixel output */
#define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0)
/** Enables the LVDS power down state transition */
#define CH7017_LVDS_POWER_DOWN_EN (1 << 6)
/** Enables the LVDS upscaler */
#define CH7017_LVDS_UPSCALER_EN (1 << 7)
#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08
#define CH7017_LVDS_ENCODING 0x64
#define CH7017_LVDS_DITHER_2D (1 << 2)
#define CH7017_LVDS_DITHER_DIS (1 << 3)
#define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4)
#define CH7017_LVDS_24_BIT (1 << 5)
#define CH7017_LVDS_ENCODING_2 0x65
#define CH7017_LVDS_PLL_CONTROL 0x66
/** Enables the LVDS panel output path */
#define CH7017_LVDS_PANEN (1 << 0)
/** Enables the LVDS panel backlight */
#define CH7017_LVDS_BKLEN (1 << 3)
#define CH7017_POWER_SEQUENCING_T1 0x67
#define CH7017_POWER_SEQUENCING_T2 0x68
#define CH7017_POWER_SEQUENCING_T3 0x69
#define CH7017_POWER_SEQUENCING_T4 0x6a
#define CH7017_POWER_SEQUENCING_T5 0x6b
#define CH7017_GPIO_DRIVER_TYPE 0x6c
#define CH7017_GPIO_DATA 0x6d
#define CH7017_GPIO_DIRECTION_CONTROL 0x6e
#define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71
# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4
# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0
# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80
#define CH7017_LVDS_PLL_VCO_CONTROL 0x72
# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80
# define CH7017_LVDS_PLL_VCO_SHIFT 4
# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0
#define CH7017_OUTPUTS_ENABLE 0x73
# define CH7017_CHARGE_PUMP_LOW 0x0
# define CH7017_CHARGE_PUMP_HIGH 0x3
# define CH7017_LVDS_CHANNEL_A (1 << 3)
# define CH7017_LVDS_CHANNEL_B (1 << 4)
# define CH7017_TV_DAC_A (1 << 5)
# define CH7017_TV_DAC_B (1 << 6)
# define CH7017_DDC_SELECT_DC2 (1 << 7)
#define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74
#define CH7017_LVDS_PLL_EMI_REDUCTION 0x75
#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76
#define CH7017_LVDS_CONTROL_2 0x78
# define CH7017_LOOP_FILTER_SHIFT 5
# define CH7017_PHASE_DETECTOR_SHIFT 0
#define CH7017_BANG_LIMIT_CONTROL 0x7f
struct ch7017_priv {
uint8_t dummy;
};
static void ch7017_dump_regs(struct intel_dvo_device *dvo);
static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable);
static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val)
{
struct iic_msg msgs[] = {
{
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 1,
.buf = &addr,
},
{
.slave = dvo->slave_addr << 1,
.flags = I2C_M_RD,
.len = 1,
.buf = val,
}
};
return -iicbus_transfer(dvo->i2c_bus, msgs, 2) == 0;
}
static bool ch7017_write(struct intel_dvo_device *dvo, u8 addr, u8 val)
{
uint8_t buf[2] = { addr, val };
struct iic_msg msg = {
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 2,
.buf = buf,
};
return -iicbus_transfer(dvo->i2c_bus, &msg, 1) == 0;
}
/** Probes for a CH7017 on the given bus and slave address. */
static bool ch7017_init(struct intel_dvo_device *dvo,
device_t adapter)
{
struct ch7017_priv *priv;
const char *str;
u8 val;
priv = malloc(sizeof(struct ch7017_priv), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
if (priv == NULL)
return false;
dvo->i2c_bus = adapter;
dvo->dev_priv = priv;
if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
goto fail;
switch (val) {
case CH7017_DEVICE_ID_VALUE:
str = "ch7017";
break;
case CH7018_DEVICE_ID_VALUE:
str = "ch7018";
break;
case CH7019_DEVICE_ID_VALUE:
str = "ch7019";
break;
default:
DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
"slave %d.\n",
val, device_get_nameunit(adapter),
dvo->slave_addr);
goto fail;
}
DRM_DEBUG_KMS("%s detected on %s, addr %d\n",
str, device_get_nameunit(adapter), dvo->slave_addr);
return true;
fail:
free(priv, DRM_MEM_KMS);
return false;
}
static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo)
{
return connector_status_connected;
}
static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,
struct drm_display_mode *mode)
{
if (mode->clock > 160000)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
static void ch7017_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;
uint8_t outputs_enable, lvds_control_2, lvds_power_down;
uint8_t horizontal_active_pixel_input;
uint8_t horizontal_active_pixel_output, vertical_active_line_output;
uint8_t active_input_line_output;
DRM_DEBUG_KMS("Registers before mode setting\n");
ch7017_dump_regs(dvo);
/* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
if (mode->clock < 100000) {
outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW;
lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
(13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
(2 << CH7017_LVDS_PLL_VCO_SHIFT) |
(3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) |
(0 << CH7017_PHASE_DETECTOR_SHIFT);
} else {
outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;
lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
(3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
lvds_pll_feedback_div = 35;
lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |
(0 << CH7017_PHASE_DETECTOR_SHIFT);
if (1) { /* XXX: dual channel panel detection. Assume yes for now. */
outputs_enable |= CH7017_LVDS_CHANNEL_B;
lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
(2 << CH7017_LVDS_PLL_VCO_SHIFT) |
(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
} else {
lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
(1 << CH7017_LVDS_PLL_VCO_SHIFT) |
(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
}
}
horizontal_active_pixel_input = mode->hdisplay & 0x00ff;
vertical_active_line_output = mode->vdisplay & 0x00ff;
horizontal_active_pixel_output = mode->hdisplay & 0x00ff;
active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) |
(((mode->vdisplay & 0x0700) >> 8) << 3);
lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
(mode->hdisplay & 0x0700) >> 8;
ch7017_dpms(dvo, false);
ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
horizontal_active_pixel_input);
ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
horizontal_active_pixel_output);
ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT,
vertical_active_line_output);
ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT,
active_input_line_output);
ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control);
ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div);
ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2);
ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable);
/* Turn the LVDS back on with new settings. */
ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
DRM_DEBUG_KMS("Registers after mode setting\n");
ch7017_dump_regs(dvo);
}
/* set the CH7017 power state */
static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable)
{
uint8_t val;
ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
/* Turn off TV/VGA, and never turn it on since we don't support it. */
ch7017_write(dvo, CH7017_POWER_MANAGEMENT,
CH7017_DAC0_POWER_DOWN |
CH7017_DAC1_POWER_DOWN |
CH7017_DAC2_POWER_DOWN |
CH7017_DAC3_POWER_DOWN |
CH7017_TV_POWER_DOWN_EN);
if (enable) {
/* Turn on the LVDS */
ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
val & ~CH7017_LVDS_POWER_DOWN_EN);
} else {
/* Turn off the LVDS */
ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
val | CH7017_LVDS_POWER_DOWN_EN);
}
/* XXX: Should actually wait for update power status somehow */
drm_msleep(20, "ch7017");
}
static bool ch7017_get_hw_state(struct intel_dvo_device *dvo)
{
uint8_t val;
ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
if (val & CH7017_LVDS_POWER_DOWN_EN)
return false;
else
return true;
}
static void ch7017_dump_regs(struct intel_dvo_device *dvo)
{
uint8_t val;
#define DUMP(reg) \
do { \
ch7017_read(dvo, reg, &val); \
DRM_DEBUG_KMS(#reg ": %02x\n", val); \
} while (0)
DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT);
DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT);
DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT);
DUMP(CH7017_LVDS_PLL_VCO_CONTROL);
DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV);
DUMP(CH7017_LVDS_CONTROL_2);
DUMP(CH7017_OUTPUTS_ENABLE);
DUMP(CH7017_LVDS_POWER_DOWN);
}
static void ch7017_destroy(struct intel_dvo_device *dvo)
{
struct ch7017_priv *priv = dvo->dev_priv;
if (priv) {
free(priv, DRM_MEM_KMS);
dvo->dev_priv = NULL;
}
}
struct intel_dvo_dev_ops ch7017_ops = {
.init = ch7017_init,
.detect = ch7017_detect,
.mode_valid = ch7017_mode_valid,
.mode_set = ch7017_mode_set,
.dpms = ch7017_dpms,
.get_hw_state = ch7017_get_hw_state,
.dump_regs = ch7017_dump_regs,
.destroy = ch7017_destroy,
};

View File

@ -0,0 +1,347 @@
/**************************************************************************
Copyright © 2006 Dave Airlie
All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sub license, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice (including the
next paragraph) shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**************************************************************************/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "dvo.h"
#define CH7xxx_REG_VID 0x4a
#define CH7xxx_REG_DID 0x4b
#define CH7011_VID 0x83 /* 7010 as well */
#define CH7009A_VID 0x84
#define CH7009B_VID 0x85
#define CH7301_VID 0x95
#define CH7xxx_VID 0x84
#define CH7xxx_DID 0x17
#define CH7xxx_NUM_REGS 0x4c
#define CH7xxx_CM 0x1c
#define CH7xxx_CM_XCM (1<<0)
#define CH7xxx_CM_MCP (1<<2)
#define CH7xxx_INPUT_CLOCK 0x1d
#define CH7xxx_GPIO 0x1e
#define CH7xxx_GPIO_HPIR (1<<3)
#define CH7xxx_IDF 0x1f
#define CH7xxx_IDF_HSP (1<<3)
#define CH7xxx_IDF_VSP (1<<4)
#define CH7xxx_CONNECTION_DETECT 0x20
#define CH7xxx_CDET_DVI (1<<5)
#define CH7301_DAC_CNTL 0x21
#define CH7301_HOTPLUG 0x23
#define CH7xxx_TCTL 0x31
#define CH7xxx_TVCO 0x32
#define CH7xxx_TPCP 0x33
#define CH7xxx_TPD 0x34
#define CH7xxx_TPVT 0x35
#define CH7xxx_TLPF 0x36
#define CH7xxx_TCT 0x37
#define CH7301_TEST_PATTERN 0x48
#define CH7xxx_PM 0x49
#define CH7xxx_PM_FPD (1<<0)
#define CH7301_PM_DACPD0 (1<<1)
#define CH7301_PM_DACPD1 (1<<2)
#define CH7301_PM_DACPD2 (1<<3)
#define CH7xxx_PM_DVIL (1<<6)
#define CH7xxx_PM_DVIP (1<<7)
#define CH7301_SYNC_POLARITY 0x56
#define CH7301_SYNC_RGB_YUV (1<<0)
#define CH7301_SYNC_POL_DVI (1<<5)
/** @file
* driver for the Chrontel 7xxx DVI chip over DVO.
*/
static struct ch7xxx_id_struct {
uint8_t vid;
char *name;
} ch7xxx_ids[] = {
{ CH7011_VID, "CH7011" },
{ CH7009A_VID, "CH7009A" },
{ CH7009B_VID, "CH7009B" },
{ CH7301_VID, "CH7301" },
};
struct ch7xxx_priv {
bool quiet;
};
static char *ch7xxx_get_id(uint8_t vid)
{
int i;
for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
if (ch7xxx_ids[i].vid == vid)
return ch7xxx_ids[i].name;
}
return NULL;
}
/** Reads an 8 bit register */
static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
struct iic_msg msgs[] = {
{
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.slave = dvo->slave_addr << 1,
.flags = I2C_M_RD,
.len = 1,
.buf = in_buf,
}
};
out_buf[0] = addr;
out_buf[1] = 0;
if (-iicbus_transfer(adapter, msgs, 2) == 0) {
*ch = in_buf[0];
return true;
}
if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
/** Writes an 8 bit register */
static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct iic_msg msg = {
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 2,
.buf = out_buf,
};
out_buf[0] = addr;
out_buf[1] = ch;
if (-iicbus_transfer(adapter, &msg, 1) == 0)
return true;
if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
static bool ch7xxx_init(struct intel_dvo_device *dvo,
device_t adapter)
{
/* this will detect the CH7xxx chip on the specified i2c bus */
struct ch7xxx_priv *ch7xxx;
uint8_t vendor, device;
char *name;
ch7xxx = malloc(sizeof(struct ch7xxx_priv), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
if (ch7xxx == NULL)
return false;
dvo->i2c_bus = adapter;
dvo->dev_priv = ch7xxx;
ch7xxx->quiet = true;
if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
goto out;
name = ch7xxx_get_id(vendor);
if (!name) {
DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
"slave %d.\n",
vendor, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
goto out;
if (device != CH7xxx_DID) {
DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
"slave %d.\n",
vendor, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
ch7xxx->quiet = false;
DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
name, vendor, device);
return true;
out:
free(ch7xxx, DRM_MEM_KMS);
return false;
}
static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
{
uint8_t cdet, orig_pm, pm;
ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
pm = orig_pm;
pm &= ~CH7xxx_PM_FPD;
pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
ch7xxx_writeb(dvo, CH7xxx_PM, pm);
ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
if (cdet & CH7xxx_CDET_DVI)
return connector_status_connected;
return connector_status_disconnected;
}
static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
struct drm_display_mode *mode)
{
if (mode->clock > 165000)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
uint8_t tvco, tpcp, tpd, tlpf, idf;
if (mode->clock <= 65000) {
tvco = 0x23;
tpcp = 0x08;
tpd = 0x16;
tlpf = 0x60;
} else {
tvco = 0x2d;
tpcp = 0x06;
tpd = 0x26;
tlpf = 0xa0;
}
ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
idf |= CH7xxx_IDF_HSP;
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
idf |= CH7xxx_IDF_HSP;
ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
}
/* set the CH7xxx power state */
static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable)
{
if (enable)
ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
else
ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
}
static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo)
{
u8 val;
ch7xxx_readb(dvo, CH7xxx_PM, &val);
if (val & (CH7xxx_PM_DVIL | CH7xxx_PM_DVIP))
return true;
else
return false;
}
static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
{
int i;
for (i = 0; i < CH7xxx_NUM_REGS; i++) {
uint8_t val;
if ((i % 8) == 0)
DRM_LOG_KMS("\n %02X: ", i);
ch7xxx_readb(dvo, i, &val);
DRM_LOG_KMS("%02X ", val);
}
}
static void ch7xxx_destroy(struct intel_dvo_device *dvo)
{
struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
if (ch7xxx) {
free(ch7xxx, DRM_MEM_KMS);
dvo->dev_priv = NULL;
}
}
struct intel_dvo_dev_ops ch7xxx_ops = {
.init = ch7xxx_init,
.detect = ch7xxx_detect,
.mode_valid = ch7xxx_mode_valid,
.mode_set = ch7xxx_mode_set,
.dpms = ch7xxx_dpms,
.get_hw_state = ch7xxx_get_hw_state,
.dump_regs = ch7xxx_dump_regs,
.destroy = ch7xxx_destroy,
};

View File

@ -0,0 +1,439 @@
/*
* Copyright © 2006 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "dvo.h"
/*
* register definitions for the i82807aa.
*
* Documentation on this chipset can be found in datasheet #29069001 at
* intel.com.
*/
/*
* VCH Revision & GMBus Base Addr
*/
#define VR00 0x00
# define VR00_BASE_ADDRESS_MASK 0x007f
/*
* Functionality Enable
*/
#define VR01 0x01
/*
* Enable the panel fitter
*/
# define VR01_PANEL_FIT_ENABLE (1 << 3)
/*
* Enables the LCD display.
*
* This must not be set while VR01_DVO_BYPASS_ENABLE is set.
*/
# define VR01_LCD_ENABLE (1 << 2)
/** Enables the DVO repeater. */
# define VR01_DVO_BYPASS_ENABLE (1 << 1)
/** Enables the DVO clock */
# define VR01_DVO_ENABLE (1 << 0)
/*
* LCD Interface Format
*/
#define VR10 0x10
/** Enables LVDS output instead of CMOS */
# define VR10_LVDS_ENABLE (1 << 4)
/** Enables 18-bit LVDS output. */
# define VR10_INTERFACE_1X18 (0 << 2)
/** Enables 24-bit LVDS or CMOS output */
# define VR10_INTERFACE_1X24 (1 << 2)
/** Enables 2x18-bit LVDS or CMOS output. */
# define VR10_INTERFACE_2X18 (2 << 2)
/** Enables 2x24-bit LVDS output */
# define VR10_INTERFACE_2X24 (3 << 2)
/*
* VR20 LCD Horizontal Display Size
*/
#define VR20 0x20
/*
* LCD Vertical Display Size
*/
#define VR21 0x20
/*
* Panel power down status
*/
#define VR30 0x30
/** Read only bit indicating that the panel is not in a safe poweroff state. */
# define VR30_PANEL_ON (1 << 15)
#define VR40 0x40
# define VR40_STALL_ENABLE (1 << 13)
# define VR40_VERTICAL_INTERP_ENABLE (1 << 12)
# define VR40_ENHANCED_PANEL_FITTING (1 << 11)
# define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10)
# define VR40_AUTO_RATIO_ENABLE (1 << 9)
# define VR40_CLOCK_GATING_ENABLE (1 << 8)
/*
* Panel Fitting Vertical Ratio
* (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
*/
#define VR41 0x41
/*
* Panel Fitting Horizontal Ratio
* (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
*/
#define VR42 0x42
/*
* Horizontal Image Size
*/
#define VR43 0x43
/* VR80 GPIO 0
*/
#define VR80 0x80
#define VR81 0x81
#define VR82 0x82
#define VR83 0x83
#define VR84 0x84
#define VR85 0x85
#define VR86 0x86
#define VR87 0x87
/* VR88 GPIO 8
*/
#define VR88 0x88
/* Graphics BIOS scratch 0
*/
#define VR8E 0x8E
# define VR8E_PANEL_TYPE_MASK (0xf << 0)
# define VR8E_PANEL_INTERFACE_CMOS (0 << 4)
# define VR8E_PANEL_INTERFACE_LVDS (1 << 4)
# define VR8E_FORCE_DEFAULT_PANEL (1 << 5)
/* Graphics BIOS scratch 1
*/
#define VR8F 0x8F
# define VR8F_VCH_PRESENT (1 << 0)
# define VR8F_DISPLAY_CONN (1 << 1)
# define VR8F_POWER_MASK (0x3c)
# define VR8F_POWER_POS (2)
struct ivch_priv {
bool quiet;
uint16_t width, height;
};
static void ivch_dump_regs(struct intel_dvo_device *dvo);
/**
* Reads a register on the ivch.
*
* Each of the 256 registers are 16 bits long.
*/
static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
{
struct ivch_priv *priv = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
u8 out_buf[1];
u8 in_buf[2];
struct iic_msg msgs[] = {
{
.slave = dvo->slave_addr << 1,
.flags = I2C_M_RD,
.len = 0,
},
{
.slave = 0 << 1,
.flags = I2C_M_NOSTART,
.len = 1,
.buf = out_buf,
},
{
.slave = dvo->slave_addr << 1,
.flags = I2C_M_RD | I2C_M_NOSTART,
.len = 2,
.buf = in_buf,
}
};
out_buf[0] = addr;
if (-iicbus_transfer(adapter, msgs, 3) == 0) {
*data = (in_buf[1] << 8) | in_buf[0];
return true;
}
if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from "
"%s:%02x.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
/** Writes a 16-bit register on the ivch */
static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
{
struct ivch_priv *priv = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
u8 out_buf[3];
struct iic_msg msg = {
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 3,
.buf = out_buf,
};
out_buf[0] = addr;
out_buf[1] = data & 0xff;
out_buf[2] = data >> 8;
if (-iicbus_transfer(adapter, &msg, 1) == 0)
return true;
if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
/** Probes the given bus and slave address for an ivch */
static bool ivch_init(struct intel_dvo_device *dvo,
device_t adapter)
{
struct ivch_priv *priv;
uint16_t temp;
priv = malloc(sizeof(struct ivch_priv), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
if (priv == NULL)
return false;
dvo->i2c_bus = adapter;
dvo->dev_priv = priv;
priv->quiet = true;
if (!ivch_read(dvo, VR00, &temp))
goto out;
priv->quiet = false;
/* Since the identification bits are probably zeroes, which doesn't seem
* very unique, check that the value in the base address field matches
* the address it's responding on.
*/
if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
"(%d vs %d)\n",
(temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
goto out;
}
ivch_read(dvo, VR20, &priv->width);
ivch_read(dvo, VR21, &priv->height);
return true;
out:
free(priv, DRM_MEM_KMS);
return false;
}
static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
{
return connector_status_connected;
}
static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
struct drm_display_mode *mode)
{
if (mode->clock > 112000)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
/** Sets the power state of the panel connected to the ivch */
static void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
{
int i;
uint16_t vr01, vr30, backlight;
/* Set the new power state of the panel. */
if (!ivch_read(dvo, VR01, &vr01))
return;
if (enable)
backlight = 1;
else
backlight = 0;
ivch_write(dvo, VR80, backlight);
if (enable)
vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
else
vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
ivch_write(dvo, VR01, vr01);
/* Wait for the panel to make its state transition */
for (i = 0; i < 100; i++) {
if (!ivch_read(dvo, VR30, &vr30))
break;
if (((vr30 & VR30_PANEL_ON) != 0) == enable)
break;
udelay(1000);
}
/* wait some more; vch may fail to resync sometimes without this */
udelay(16 * 1000);
}
static bool ivch_get_hw_state(struct intel_dvo_device *dvo)
{
uint16_t vr01;
/* Set the new power state of the panel. */
if (!ivch_read(dvo, VR01, &vr01))
return false;
if (vr01 & VR01_LCD_ENABLE)
return true;
else
return false;
}
static void ivch_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
uint16_t vr40 = 0;
uint16_t vr01;
vr01 = 0;
vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
VR40_HORIZONTAL_INTERP_ENABLE);
if (mode->hdisplay != adjusted_mode->hdisplay ||
mode->vdisplay != adjusted_mode->vdisplay) {
uint16_t x_ratio, y_ratio;
vr01 |= VR01_PANEL_FIT_ENABLE;
vr40 |= VR40_CLOCK_GATING_ENABLE;
x_ratio = (((mode->hdisplay - 1) << 16) /
(adjusted_mode->hdisplay - 1)) >> 2;
y_ratio = (((mode->vdisplay - 1) << 16) /
(adjusted_mode->vdisplay - 1)) >> 2;
ivch_write(dvo, VR42, x_ratio);
ivch_write(dvo, VR41, y_ratio);
} else {
vr01 &= ~VR01_PANEL_FIT_ENABLE;
vr40 &= ~VR40_CLOCK_GATING_ENABLE;
}
vr40 &= ~VR40_AUTO_RATIO_ENABLE;
ivch_write(dvo, VR01, vr01);
ivch_write(dvo, VR40, vr40);
ivch_dump_regs(dvo);
}
static void ivch_dump_regs(struct intel_dvo_device *dvo)
{
uint16_t val;
ivch_read(dvo, VR00, &val);
DRM_LOG_KMS("VR00: 0x%04x\n", val);
ivch_read(dvo, VR01, &val);
DRM_LOG_KMS("VR01: 0x%04x\n", val);
ivch_read(dvo, VR30, &val);
DRM_LOG_KMS("VR30: 0x%04x\n", val);
ivch_read(dvo, VR40, &val);
DRM_LOG_KMS("VR40: 0x%04x\n", val);
/* GPIO registers */
ivch_read(dvo, VR80, &val);
DRM_LOG_KMS("VR80: 0x%04x\n", val);
ivch_read(dvo, VR81, &val);
DRM_LOG_KMS("VR81: 0x%04x\n", val);
ivch_read(dvo, VR82, &val);
DRM_LOG_KMS("VR82: 0x%04x\n", val);
ivch_read(dvo, VR83, &val);
DRM_LOG_KMS("VR83: 0x%04x\n", val);
ivch_read(dvo, VR84, &val);
DRM_LOG_KMS("VR84: 0x%04x\n", val);
ivch_read(dvo, VR85, &val);
DRM_LOG_KMS("VR85: 0x%04x\n", val);
ivch_read(dvo, VR86, &val);
DRM_LOG_KMS("VR86: 0x%04x\n", val);
ivch_read(dvo, VR87, &val);
DRM_LOG_KMS("VR87: 0x%04x\n", val);
ivch_read(dvo, VR88, &val);
DRM_LOG_KMS("VR88: 0x%04x\n", val);
/* Scratch register 0 - AIM Panel type */
ivch_read(dvo, VR8E, &val);
DRM_LOG_KMS("VR8E: 0x%04x\n", val);
/* Scratch register 1 - Status register */
ivch_read(dvo, VR8F, &val);
DRM_LOG_KMS("VR8F: 0x%04x\n", val);
}
static void ivch_destroy(struct intel_dvo_device *dvo)
{
struct ivch_priv *priv = dvo->dev_priv;
if (priv) {
free(priv, DRM_MEM_KMS);
dvo->dev_priv = NULL;
}
}
struct intel_dvo_dev_ops ivch_ops = {
.init = ivch_init,
.dpms = ivch_dpms,
.get_hw_state = ivch_get_hw_state,
.mode_valid = ivch_mode_valid,
.mode_set = ivch_mode_set,
.detect = ivch_detect,
.dump_regs = ivch_dump_regs,
.destroy = ivch_destroy,
};

View File

@ -0,0 +1,601 @@
/*
*
* Copyright (c) 2012 Gilles Dartiguelongue, Thomas Richter
*
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "dvo.h"
#include "i915_reg.h"
#include "i915_drv.h"
#define NS2501_VID 0x1305
#define NS2501_DID 0x6726
#define NS2501_VID_LO 0x00
#define NS2501_VID_HI 0x01
#define NS2501_DID_LO 0x02
#define NS2501_DID_HI 0x03
#define NS2501_REV 0x04
#define NS2501_RSVD 0x05
#define NS2501_FREQ_LO 0x06
#define NS2501_FREQ_HI 0x07
#define NS2501_REG8 0x08
#define NS2501_8_VEN (1<<5)
#define NS2501_8_HEN (1<<4)
#define NS2501_8_DSEL (1<<3)
#define NS2501_8_BPAS (1<<2)
#define NS2501_8_RSVD (1<<1)
#define NS2501_8_PD (1<<0)
#define NS2501_REG9 0x09
#define NS2501_9_VLOW (1<<7)
#define NS2501_9_MSEL_MASK (0x7<<4)
#define NS2501_9_TSEL (1<<3)
#define NS2501_9_RSEN (1<<2)
#define NS2501_9_RSVD (1<<1)
#define NS2501_9_MDI (1<<0)
#define NS2501_REGC 0x0c
struct ns2501_priv {
//I2CDevRec d;
bool quiet;
int reg_8_shadow;
int reg_8_set;
// Shadow registers for i915
int dvoc;
int pll_a;
int srcdim;
int fw_blc;
};
#define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr))
/*
* For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens
* laptops does not react on the i2c bus unless
* both the PLL is running and the display is configured in its native
* resolution.
* This function forces the DVO on, and stores the registers it touches.
* Afterwards, registers are restored to regular values.
*
* This is pretty much a hack, though it works.
* Without that, ns2501_readb and ns2501_writeb fail
* when switching the resolution.
*/
static void enable_dvo(struct intel_dvo_device *dvo)
{
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
device_t adapter = dvo->i2c_bus;
/*
* FIXME Linux<->FreeBSD: device_get_softc() returns a struct
* intel_iic_softc in reality, where struct intel_gmbus is
* the first member. struct intel_iic_softc is defined in
* intel_iic.c.
*/
struct intel_gmbus *bus =
(struct intel_gmbus *)device_get_softc(adapter);
struct drm_i915_private *dev_priv = bus->dev_priv;
DRM_DEBUG_KMS("%s: Trying to re-enable the DVO\n", __FUNCTION__);
ns->dvoc = I915_READ(DVO_C);
ns->pll_a = I915_READ(_DPLL_A);
ns->srcdim = I915_READ(DVOC_SRCDIM);
ns->fw_blc = I915_READ(FW_BLC);
I915_WRITE(DVOC, 0x10004084);
I915_WRITE(_DPLL_A, 0xd0820000);
I915_WRITE(DVOC_SRCDIM, 0x400300); // 1024x768
I915_WRITE(FW_BLC, 0x1080304);
I915_WRITE(DVOC, 0x90004084);
}
/*
* Restore the I915 registers modified by the above
* trigger function.
*/
static void restore_dvo(struct intel_dvo_device *dvo)
{
device_t adapter = dvo->i2c_bus;
/*
* FIXME Linux<->FreeBSD: device_get_softc() returns a struct
* intel_iic_softc in reality, where struct intel_gmbus is
* the first member. struct intel_iic_softc is defined in
* intel_iic.c.
*/
struct intel_gmbus *bus =
(struct intel_gmbus *)device_get_softc(adapter);
struct drm_i915_private *dev_priv = bus->dev_priv;
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
I915_WRITE(DVOC, ns->dvoc);
I915_WRITE(_DPLL_A, ns->pll_a);
I915_WRITE(DVOC_SRCDIM, ns->srcdim);
I915_WRITE(FW_BLC, ns->fw_blc);
}
/*
** Read a register from the ns2501.
** Returns true if successful, false otherwise.
** If it returns false, it might be wise to enable the
** DVO with the above function.
*/
static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
{
struct ns2501_priv *ns = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
struct iic_msg msgs[] = {
{
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.slave = dvo->slave_addr << 1,
.flags = I2C_M_RD,
.len = 1,
.buf = in_buf,
}
};
out_buf[0] = addr;
out_buf[1] = 0;
if (-iicbus_transfer(adapter, msgs, 2) == 0) {
*ch = in_buf[0];
return true;
}
if (!ns->quiet) {
DRM_DEBUG_KMS
("Unable to read register 0x%02x from %s:0x%02x.\n", addr,
device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
/*
** Write a register to the ns2501.
** Returns true if successful, false otherwise.
** If it returns false, it might be wise to enable the
** DVO with the above function.
*/
static bool ns2501_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct ns2501_priv *ns = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct iic_msg msg = {
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 2,
.buf = out_buf,
};
out_buf[0] = addr;
out_buf[1] = ch;
if (-iicbus_transfer(adapter, &msg, 1) == 0) {
return true;
}
if (!ns->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
/* National Semiconductor 2501 driver for chip on i2c bus
* scan for the chip on the bus.
* Hope the VBIOS initialized the PLL correctly so we can
* talk to it. If not, it will not be seen and not detected.
* Bummer!
*/
static bool ns2501_init(struct intel_dvo_device *dvo,
device_t adapter)
{
/* this will detect the NS2501 chip on the specified i2c bus */
struct ns2501_priv *ns;
unsigned char ch;
ns = malloc(sizeof(struct ns2501_priv), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
if (ns == NULL)
return false;
dvo->i2c_bus = adapter;
dvo->dev_priv = ns;
ns->quiet = true;
if (!ns2501_readb(dvo, NS2501_VID_LO, &ch))
goto out;
if (ch != (NS2501_VID & 0xff)) {
DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
ch, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
if (!ns2501_readb(dvo, NS2501_DID_LO, &ch))
goto out;
if (ch != (NS2501_DID & 0xff)) {
DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
ch, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
ns->quiet = false;
ns->reg_8_set = 0;
ns->reg_8_shadow =
NS2501_8_PD | NS2501_8_BPAS | NS2501_8_VEN | NS2501_8_HEN;
DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n");
return true;
out:
free(ns, DRM_MEM_KMS);
return false;
}
static enum drm_connector_status ns2501_detect(struct intel_dvo_device *dvo)
{
/*
* This is a Laptop display, it doesn't have hotplugging.
* Even if not, the detection bit of the 2501 is unreliable as
* it only works for some display types.
* It is even more unreliable as the PLL must be active for
* allowing reading from the chiop.
*/
return connector_status_connected;
}
static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
struct drm_display_mode *mode)
{
DRM_DEBUG_KMS
("%s: is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
__FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
mode->vtotal);
/*
* Currently, these are all the modes I have data from.
* More might exist. Unclear how to find the native resolution
* of the panel in here so we could always accept it
* by disabling the scaler.
*/
if ((mode->hdisplay == 800 && mode->vdisplay == 600) ||
(mode->hdisplay == 640 && mode->vdisplay == 480) ||
(mode->hdisplay == 1024 && mode->vdisplay == 768)) {
return MODE_OK;
} else {
return MODE_ONE_SIZE; /* Is this a reasonable error? */
}
}
static void ns2501_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
bool ok;
bool restore = false;
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
DRM_DEBUG_KMS
("%s: set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
__FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
mode->vtotal);
/*
* Where do I find the native resolution for which scaling is not required???
*
* First trigger the DVO on as otherwise the chip does not appear on the i2c
* bus.
*/
do {
ok = true;
if (mode->hdisplay == 800 && mode->vdisplay == 600) {
/* mode 277 */
ns->reg_8_shadow &= ~NS2501_8_BPAS;
DRM_DEBUG_KMS("%s: switching to 800x600\n",
__FUNCTION__);
/*
* No, I do not know where this data comes from.
* It is just what the video bios left in the DVO, so
* I'm just copying it here over.
* This also means that I cannot support any other modes
* except the ones supported by the bios.
*/
ok &= ns2501_writeb(dvo, 0x11, 0xc8); // 0xc7 also works.
ok &= ns2501_writeb(dvo, 0x1b, 0x19);
ok &= ns2501_writeb(dvo, 0x1c, 0x62); // VBIOS left 0x64 here, but 0x62 works nicer
ok &= ns2501_writeb(dvo, 0x1d, 0x02);
ok &= ns2501_writeb(dvo, 0x34, 0x03);
ok &= ns2501_writeb(dvo, 0x35, 0xff);
ok &= ns2501_writeb(dvo, 0x80, 0x27);
ok &= ns2501_writeb(dvo, 0x81, 0x03);
ok &= ns2501_writeb(dvo, 0x82, 0x41);
ok &= ns2501_writeb(dvo, 0x83, 0x05);
ok &= ns2501_writeb(dvo, 0x8d, 0x02);
ok &= ns2501_writeb(dvo, 0x8e, 0x04);
ok &= ns2501_writeb(dvo, 0x8f, 0x00);
ok &= ns2501_writeb(dvo, 0x90, 0xfe); /* vertical. VBIOS left 0xff here, but 0xfe works better */
ok &= ns2501_writeb(dvo, 0x91, 0x07);
ok &= ns2501_writeb(dvo, 0x94, 0x00);
ok &= ns2501_writeb(dvo, 0x95, 0x00);
ok &= ns2501_writeb(dvo, 0x96, 0x00);
ok &= ns2501_writeb(dvo, 0x99, 0x00);
ok &= ns2501_writeb(dvo, 0x9a, 0x88);
ok &= ns2501_writeb(dvo, 0x9c, 0x23); /* Looks like first and last line of the image. */
ok &= ns2501_writeb(dvo, 0x9d, 0x00);
ok &= ns2501_writeb(dvo, 0x9e, 0x25);
ok &= ns2501_writeb(dvo, 0x9f, 0x03);
ok &= ns2501_writeb(dvo, 0xa4, 0x80);
ok &= ns2501_writeb(dvo, 0xb6, 0x00);
ok &= ns2501_writeb(dvo, 0xb9, 0xc8); /* horizontal? */
ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */
ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */
ok &= ns2501_writeb(dvo, 0xc1, 0xd7);
ok &= ns2501_writeb(dvo, 0xc2, 0x00);
ok &= ns2501_writeb(dvo, 0xc3, 0xf8);
ok &= ns2501_writeb(dvo, 0xc4, 0x03);
ok &= ns2501_writeb(dvo, 0xc5, 0x1a);
ok &= ns2501_writeb(dvo, 0xc6, 0x00);
ok &= ns2501_writeb(dvo, 0xc7, 0x73);
ok &= ns2501_writeb(dvo, 0xc8, 0x02);
} else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
/* mode 274 */
DRM_DEBUG_KMS("%s: switching to 640x480\n",
__FUNCTION__);
/*
* No, I do not know where this data comes from.
* It is just what the video bios left in the DVO, so
* I'm just copying it here over.
* This also means that I cannot support any other modes
* except the ones supported by the bios.
*/
ns->reg_8_shadow &= ~NS2501_8_BPAS;
ok &= ns2501_writeb(dvo, 0x11, 0xa0);
ok &= ns2501_writeb(dvo, 0x1b, 0x11);
ok &= ns2501_writeb(dvo, 0x1c, 0x54);
ok &= ns2501_writeb(dvo, 0x1d, 0x03);
ok &= ns2501_writeb(dvo, 0x34, 0x03);
ok &= ns2501_writeb(dvo, 0x35, 0xff);
ok &= ns2501_writeb(dvo, 0x80, 0xff);
ok &= ns2501_writeb(dvo, 0x81, 0x07);
ok &= ns2501_writeb(dvo, 0x82, 0x3d);
ok &= ns2501_writeb(dvo, 0x83, 0x05);
ok &= ns2501_writeb(dvo, 0x8d, 0x02);
ok &= ns2501_writeb(dvo, 0x8e, 0x10);
ok &= ns2501_writeb(dvo, 0x8f, 0x00);
ok &= ns2501_writeb(dvo, 0x90, 0xff); /* vertical */
ok &= ns2501_writeb(dvo, 0x91, 0x07);
ok &= ns2501_writeb(dvo, 0x94, 0x00);
ok &= ns2501_writeb(dvo, 0x95, 0x00);
ok &= ns2501_writeb(dvo, 0x96, 0x05);
ok &= ns2501_writeb(dvo, 0x99, 0x00);
ok &= ns2501_writeb(dvo, 0x9a, 0x88);
ok &= ns2501_writeb(dvo, 0x9c, 0x24);
ok &= ns2501_writeb(dvo, 0x9d, 0x00);
ok &= ns2501_writeb(dvo, 0x9e, 0x25);
ok &= ns2501_writeb(dvo, 0x9f, 0x03);
ok &= ns2501_writeb(dvo, 0xa4, 0x84);
ok &= ns2501_writeb(dvo, 0xb6, 0x09);
ok &= ns2501_writeb(dvo, 0xb9, 0xa0); /* horizontal? */
ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */
ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */
ok &= ns2501_writeb(dvo, 0xc1, 0x90);
ok &= ns2501_writeb(dvo, 0xc2, 0x00);
ok &= ns2501_writeb(dvo, 0xc3, 0x0f);
ok &= ns2501_writeb(dvo, 0xc4, 0x03);
ok &= ns2501_writeb(dvo, 0xc5, 0x16);
ok &= ns2501_writeb(dvo, 0xc6, 0x00);
ok &= ns2501_writeb(dvo, 0xc7, 0x02);
ok &= ns2501_writeb(dvo, 0xc8, 0x02);
} else if (mode->hdisplay == 1024 && mode->vdisplay == 768) {
/* mode 280 */
DRM_DEBUG_KMS("%s: switching to 1024x768\n",
__FUNCTION__);
/*
* This might or might not work, actually. I'm silently
* assuming here that the native panel resolution is
* 1024x768. If not, then this leaves the scaler disabled
* generating a picture that is likely not the expected.
*
* Problem is that I do not know where to take the panel
* dimensions from.
*
* Enable the bypass, scaling not required.
*
* The scaler registers are irrelevant here....
*
*/
ns->reg_8_shadow |= NS2501_8_BPAS;
ok &= ns2501_writeb(dvo, 0x37, 0x44);
} else {
/*
* Data not known. Bummer!
* Hopefully, the code should not go here
* as mode_OK delivered no other modes.
*/
ns->reg_8_shadow |= NS2501_8_BPAS;
}
ok &= ns2501_writeb(dvo, NS2501_REG8, ns->reg_8_shadow);
if (!ok) {
if (restore)
restore_dvo(dvo);
enable_dvo(dvo);
restore = true;
}
} while (!ok);
/*
* Restore the old i915 registers before
* forcing the ns2501 on.
*/
if (restore)
restore_dvo(dvo);
}
/* set the NS2501 power state */
static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
{
unsigned char ch;
if (!ns2501_readb(dvo, NS2501_REG8, &ch))
return false;
if (ch & NS2501_8_PD)
return true;
else
return false;
}
/* set the NS2501 power state */
static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
{
bool ok;
bool restore = false;
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
unsigned char ch;
DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %i\n",
__FUNCTION__, enable);
ch = ns->reg_8_shadow;
if (enable)
ch |= NS2501_8_PD;
else
ch &= ~NS2501_8_PD;
if (ns->reg_8_set == 0 || ns->reg_8_shadow != ch) {
ns->reg_8_set = 1;
ns->reg_8_shadow = ch;
do {
ok = true;
ok &= ns2501_writeb(dvo, NS2501_REG8, ch);
ok &=
ns2501_writeb(dvo, 0x34,
enable ? 0x03 : 0x00);
ok &=
ns2501_writeb(dvo, 0x35,
enable ? 0xff : 0x00);
if (!ok) {
if (restore)
restore_dvo(dvo);
enable_dvo(dvo);
restore = true;
}
} while (!ok);
if (restore)
restore_dvo(dvo);
}
}
static void ns2501_dump_regs(struct intel_dvo_device *dvo)
{
uint8_t val;
ns2501_readb(dvo, NS2501_FREQ_LO, &val);
DRM_LOG_KMS("NS2501_FREQ_LO: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_FREQ_HI, &val);
DRM_LOG_KMS("NS2501_FREQ_HI: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_REG8, &val);
DRM_LOG_KMS("NS2501_REG8: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_REG9, &val);
DRM_LOG_KMS("NS2501_REG9: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_REGC, &val);
DRM_LOG_KMS("NS2501_REGC: 0x%02x\n", val);
}
static void ns2501_destroy(struct intel_dvo_device *dvo)
{
struct ns2501_priv *ns = dvo->dev_priv;
if (ns) {
free(ns, DRM_MEM_KMS);
dvo->dev_priv = NULL;
}
}
struct intel_dvo_dev_ops ns2501_ops = {
.init = ns2501_init,
.detect = ns2501_detect,
.mode_valid = ns2501_mode_valid,
.mode_set = ns2501_mode_set,
.dpms = ns2501_dpms,
.get_hw_state = ns2501_get_hw_state,
.dump_regs = ns2501_dump_regs,
.destroy = ns2501_destroy,
};

View File

@ -0,0 +1,282 @@
/**************************************************************************
Copyright © 2006 Dave Airlie
All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sub license, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice (including the
next paragraph) shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**************************************************************************/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "dvo.h"
#define SIL164_VID 0x0001
#define SIL164_DID 0x0006
#define SIL164_VID_LO 0x00
#define SIL164_VID_HI 0x01
#define SIL164_DID_LO 0x02
#define SIL164_DID_HI 0x03
#define SIL164_REV 0x04
#define SIL164_RSVD 0x05
#define SIL164_FREQ_LO 0x06
#define SIL164_FREQ_HI 0x07
#define SIL164_REG8 0x08
#define SIL164_8_VEN (1<<5)
#define SIL164_8_HEN (1<<4)
#define SIL164_8_DSEL (1<<3)
#define SIL164_8_BSEL (1<<2)
#define SIL164_8_EDGE (1<<1)
#define SIL164_8_PD (1<<0)
#define SIL164_REG9 0x09
#define SIL164_9_VLOW (1<<7)
#define SIL164_9_MSEL_MASK (0x7<<4)
#define SIL164_9_TSEL (1<<3)
#define SIL164_9_RSEN (1<<2)
#define SIL164_9_HTPLG (1<<1)
#define SIL164_9_MDI (1<<0)
#define SIL164_REGC 0x0c
struct sil164_priv {
//I2CDevRec d;
bool quiet;
};
#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr))
static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct sil164_priv *sil = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
struct iic_msg msgs[] = {
{
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.slave = dvo->slave_addr << 1,
.flags = I2C_M_RD,
.len = 1,
.buf = in_buf,
}
};
out_buf[0] = addr;
out_buf[1] = 0;
if (-iicbus_transfer(adapter, msgs, 2) == 0) {
*ch = in_buf[0];
return true;
}
if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct sil164_priv *sil = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct iic_msg msg = {
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 2,
.buf = out_buf,
};
out_buf[0] = addr;
out_buf[1] = ch;
if (-iicbus_transfer(adapter, &msg, 1) == 0)
return true;
if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
/* Silicon Image 164 driver for chip on i2c bus */
static bool sil164_init(struct intel_dvo_device *dvo,
device_t adapter)
{
/* this will detect the SIL164 chip on the specified i2c bus */
struct sil164_priv *sil;
unsigned char ch;
sil = malloc(sizeof(struct sil164_priv), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
if (sil == NULL)
return false;
dvo->i2c_bus = adapter;
dvo->dev_priv = sil;
sil->quiet = true;
if (!sil164_readb(dvo, SIL164_VID_LO, &ch))
goto out;
if (ch != (SIL164_VID & 0xff)) {
DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
ch, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
if (!sil164_readb(dvo, SIL164_DID_LO, &ch))
goto out;
if (ch != (SIL164_DID & 0xff)) {
DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
ch, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
sil->quiet = false;
DRM_DEBUG_KMS("init sil164 dvo controller successfully!\n");
return true;
out:
free(sil, DRM_MEM_KMS);
return false;
}
static enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo)
{
uint8_t reg9;
sil164_readb(dvo, SIL164_REG9, &reg9);
if (reg9 & SIL164_9_HTPLG)
return connector_status_connected;
else
return connector_status_disconnected;
}
static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static void sil164_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
/* As long as the basics are set up, since we don't have clock
* dependencies in the mode setup, we can just leave the
* registers alone and everything will work fine.
*/
/* recommended programming sequence from doc */
/*sil164_writeb(sil, 0x08, 0x30);
sil164_writeb(sil, 0x09, 0x00);
sil164_writeb(sil, 0x0a, 0x90);
sil164_writeb(sil, 0x0c, 0x89);
sil164_writeb(sil, 0x08, 0x31);*/
/* don't do much */
return;
}
/* set the SIL164 power state */
static void sil164_dpms(struct intel_dvo_device *dvo, bool enable)
{
int ret;
unsigned char ch;
ret = sil164_readb(dvo, SIL164_REG8, &ch);
if (ret == false)
return;
if (enable)
ch |= SIL164_8_PD;
else
ch &= ~SIL164_8_PD;
sil164_writeb(dvo, SIL164_REG8, ch);
return;
}
static bool sil164_get_hw_state(struct intel_dvo_device *dvo)
{
int ret;
unsigned char ch;
ret = sil164_readb(dvo, SIL164_REG8, &ch);
if (ret == false)
return false;
if (ch & SIL164_8_PD)
return true;
else
return false;
}
static void sil164_dump_regs(struct intel_dvo_device *dvo)
{
uint8_t val;
sil164_readb(dvo, SIL164_FREQ_LO, &val);
DRM_LOG_KMS("SIL164_FREQ_LO: 0x%02x\n", val);
sil164_readb(dvo, SIL164_FREQ_HI, &val);
DRM_LOG_KMS("SIL164_FREQ_HI: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REG8, &val);
DRM_LOG_KMS("SIL164_REG8: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REG9, &val);
DRM_LOG_KMS("SIL164_REG9: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REGC, &val);
DRM_LOG_KMS("SIL164_REGC: 0x%02x\n", val);
}
static void sil164_destroy(struct intel_dvo_device *dvo)
{
struct sil164_priv *sil = dvo->dev_priv;
if (sil) {
free(sil, DRM_MEM_KMS);
dvo->dev_priv = NULL;
}
}
struct intel_dvo_dev_ops sil164_ops = {
.init = sil164_init,
.detect = sil164_detect,
.mode_valid = sil164_mode_valid,
.mode_set = sil164_mode_set,
.dpms = sil164_dpms,
.get_hw_state = sil164_get_hw_state,
.dump_regs = sil164_dump_regs,
.destroy = sil164_destroy,
};

View File

@ -0,0 +1,321 @@
/*
* Copyright © 2007 Dave Mueller
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Dave Mueller <dave.mueller@gmx.ch>
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "dvo.h"
/* register definitions according to the TFP410 data sheet */
#define TFP410_VID 0x014C
#define TFP410_DID 0x0410
#define TFP410_VID_LO 0x00
#define TFP410_VID_HI 0x01
#define TFP410_DID_LO 0x02
#define TFP410_DID_HI 0x03
#define TFP410_REV 0x04
#define TFP410_CTL_1 0x08
#define TFP410_CTL_1_TDIS (1<<6)
#define TFP410_CTL_1_VEN (1<<5)
#define TFP410_CTL_1_HEN (1<<4)
#define TFP410_CTL_1_DSEL (1<<3)
#define TFP410_CTL_1_BSEL (1<<2)
#define TFP410_CTL_1_EDGE (1<<1)
#define TFP410_CTL_1_PD (1<<0)
#define TFP410_CTL_2 0x09
#define TFP410_CTL_2_VLOW (1<<7)
#define TFP410_CTL_2_MSEL_MASK (0x7<<4)
#define TFP410_CTL_2_MSEL (1<<4)
#define TFP410_CTL_2_TSEL (1<<3)
#define TFP410_CTL_2_RSEN (1<<2)
#define TFP410_CTL_2_HTPLG (1<<1)
#define TFP410_CTL_2_MDI (1<<0)
#define TFP410_CTL_3 0x0A
#define TFP410_CTL_3_DK_MASK (0x7<<5)
#define TFP410_CTL_3_DK (1<<5)
#define TFP410_CTL_3_DKEN (1<<4)
#define TFP410_CTL_3_CTL_MASK (0x7<<1)
#define TFP410_CTL_3_CTL (1<<1)
#define TFP410_USERCFG 0x0B
#define TFP410_DE_DLY 0x32
#define TFP410_DE_CTL 0x33
#define TFP410_DE_CTL_DEGEN (1<<6)
#define TFP410_DE_CTL_VSPOL (1<<5)
#define TFP410_DE_CTL_HSPOL (1<<4)
#define TFP410_DE_CTL_DEDLY8 (1<<0)
#define TFP410_DE_TOP 0x34
#define TFP410_DE_CNT_LO 0x36
#define TFP410_DE_CNT_HI 0x37
#define TFP410_DE_LIN_LO 0x38
#define TFP410_DE_LIN_HI 0x39
#define TFP410_H_RES_LO 0x3A
#define TFP410_H_RES_HI 0x3B
#define TFP410_V_RES_LO 0x3C
#define TFP410_V_RES_HI 0x3D
struct tfp410_priv {
bool quiet;
};
static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct tfp410_priv *tfp = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
struct iic_msg msgs[] = {
{
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.slave = dvo->slave_addr << 1,
.flags = I2C_M_RD,
.len = 1,
.buf = in_buf,
}
};
out_buf[0] = addr;
out_buf[1] = 0;
if (-iicbus_transfer(adapter, msgs, 2) == 0) {
*ch = in_buf[0];
return true;
}
if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct tfp410_priv *tfp = dvo->dev_priv;
device_t adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct iic_msg msg = {
.slave = dvo->slave_addr << 1,
.flags = 0,
.len = 2,
.buf = out_buf,
};
out_buf[0] = addr;
out_buf[1] = ch;
if (-iicbus_transfer(adapter, &msg, 1) == 0)
return true;
if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, device_get_nameunit(adapter), dvo->slave_addr);
}
return false;
}
static int tfp410_getid(struct intel_dvo_device *dvo, int addr)
{
uint8_t ch1, ch2;
if (tfp410_readb(dvo, addr+0, &ch1) &&
tfp410_readb(dvo, addr+1, &ch2))
return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF);
return -1;
}
/* Ti TFP410 driver for chip on i2c bus */
static bool tfp410_init(struct intel_dvo_device *dvo,
device_t adapter)
{
/* this will detect the tfp410 chip on the specified i2c bus */
struct tfp410_priv *tfp;
int id;
tfp = malloc(sizeof(struct tfp410_priv), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
if (tfp == NULL)
return false;
dvo->i2c_bus = adapter;
dvo->dev_priv = tfp;
tfp->quiet = true;
if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
DRM_DEBUG_KMS("tfp410 not detected got VID %X: from %s "
"Slave %d.\n",
id, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
DRM_DEBUG_KMS("tfp410 not detected got DID %X: from %s "
"Slave %d.\n",
id, device_get_nameunit(adapter), dvo->slave_addr);
goto out;
}
tfp->quiet = false;
return true;
out:
free(tfp, DRM_MEM_KMS);
return false;
}
static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo)
{
enum drm_connector_status ret = connector_status_disconnected;
uint8_t ctl2;
if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) {
if (ctl2 & TFP410_CTL_2_RSEN)
ret = connector_status_connected;
else
ret = connector_status_disconnected;
}
return ret;
}
static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static void tfp410_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
/* As long as the basics are set up, since we don't have clock dependencies
* in the mode setup, we can just leave the registers alone and everything
* will work fine.
*/
/* don't do much */
return;
}
/* set the tfp410 power state */
static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable)
{
uint8_t ctl1;
if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
return;
if (enable)
ctl1 |= TFP410_CTL_1_PD;
else
ctl1 &= ~TFP410_CTL_1_PD;
tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
}
static bool tfp410_get_hw_state(struct intel_dvo_device *dvo)
{
uint8_t ctl1;
if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
return false;
if (ctl1 & TFP410_CTL_1_PD)
return true;
else
return false;
}
static void tfp410_dump_regs(struct intel_dvo_device *dvo)
{
uint8_t val, val2;
tfp410_readb(dvo, TFP410_REV, &val);
DRM_LOG_KMS("TFP410_REV: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_1, &val);
DRM_LOG_KMS("TFP410_CTL1: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_2, &val);
DRM_LOG_KMS("TFP410_CTL2: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_3, &val);
DRM_LOG_KMS("TFP410_CTL3: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_USERCFG, &val);
DRM_LOG_KMS("TFP410_USERCFG: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_DLY, &val);
DRM_LOG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_CTL, &val);
DRM_LOG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_TOP, &val);
DRM_LOG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
DRM_LOG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
DRM_LOG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
tfp410_readb(dvo, TFP410_H_RES_LO, &val);
tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
DRM_LOG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val);
tfp410_readb(dvo, TFP410_V_RES_LO, &val);
tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
DRM_LOG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
}
static void tfp410_destroy(struct intel_dvo_device *dvo)
{
struct tfp410_priv *tfp = dvo->dev_priv;
if (tfp) {
free(tfp, DRM_MEM_KMS);
dvo->dev_priv = NULL;
}
}
struct intel_dvo_dev_ops tfp410_ops = {
.init = tfp410_init,
.detect = tfp410_detect,
.mode_valid = tfp410_mode_valid,
.mode_set = tfp410_mode_set,
.dpms = tfp410_dpms,
.get_hw_state = tfp410_get_hw_state,
.dump_regs = tfp410_dump_regs,
.destroy = tfp410_destroy,
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/*-
/*
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
@ -24,17 +24,18 @@
*
*/
#ifndef _UAPI_I915_DRM_H_
#define _UAPI_I915_DRM_H_
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifndef _I915_DRM_H_
#define _I915_DRM_H_
#include <dev/drm2/drm.h>
/* Please note that modifications to all structs defined here are
* subject to backwards-compatibility constraints.
*/
#include <dev/drm2/drm.h>
/* Each region is a minimum of 16k, and there are at most 255 of them.
*/
@ -46,12 +47,7 @@ typedef struct _drm_i915_init {
enum {
I915_INIT_DMA = 0x01,
I915_CLEANUP_DMA = 0x02,
I915_RESUME_DMA = 0x03,
/* Since this struct isn't versioned, just used a new
* 'func' code to indicate the presence of dri2 sarea
* info. */
I915_INIT_DMA2 = 0x04
I915_RESUME_DMA = 0x03
} func;
unsigned int mmio_offset;
int sarea_priv_offset;
@ -69,7 +65,6 @@ typedef struct _drm_i915_init {
unsigned int depth_pitch;
unsigned int cpp;
unsigned int chipset;
unsigned int sarea_handle;
} drm_i915_init_t;
typedef struct _drm_i915_sarea {
@ -123,20 +118,18 @@ typedef struct _drm_i915_sarea {
int pipeB_w;
int pipeB_h;
/* Triple buffering */
drm_handle_t third_handle;
int third_offset;
int third_size;
unsigned int third_tiled;
/* fill out some space for old userspace triple buffer */
drm_handle_t unused_handle;
__u32 unused1, unused2, unused3;
/* buffer object handles for the static buffers. May change
* over the lifetime of the client, though it doesn't in our current
* implementation.
/* buffer object handles for static buffers. May change
* over the lifetime of the client.
*/
__u32 front_bo_handle;
__u32 back_bo_handle;
__u32 third_bo_handle;
__u32 unused_bo_handle;
__u32 depth_bo_handle;
} drm_i915_sarea_t;
/* due to userspace building against these headers we need some compat here */
@ -149,16 +142,6 @@ typedef struct _drm_i915_sarea {
#define planeB_w pipeB_w
#define planeB_h pipeB_h
/* Driver specific fence types and classes.
*/
/* The only fence class we support */
#define DRM_I915_FENCE_CLASS_ACCEL 0
/* Fence type that guarantees read-write flush */
#define DRM_I915_FENCE_TYPE_RW 2
/* MI_FLUSH programmed just before the fence */
#define DRM_I915_FENCE_FLAG_FLUSHED 0x01000000
/* Flags for perf_boxes
*/
#define I915_BOX_RING_EMPTY 0x1
@ -186,9 +169,7 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_SET_VBLANK_PIPE 0x0d
#define DRM_I915_GET_VBLANK_PIPE 0x0e
#define DRM_I915_VBLANK_SWAP 0x0f
#define DRM_I915_MMIO 0x10
#define DRM_I915_HWS_ADDR 0x11
#define DRM_I915_EXECBUFFER 0x12
#define DRM_I915_GEM_INIT 0x13
#define DRM_I915_GEM_EXECBUFFER 0x14
#define DRM_I915_GEM_PIN 0x15
@ -212,14 +193,18 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_OVERLAY_PUT_IMAGE 0x27
#define DRM_I915_OVERLAY_ATTRS 0x28
#define DRM_I915_GEM_EXECBUFFER2 0x29
#define DRM_I915_GET_SPRITE_COLORKEY 0x2a
#define DRM_I915_SET_SPRITE_COLORKEY 0x2b
#define DRM_I915_GET_SPRITE_COLORKEY 0x2a
#define DRM_I915_SET_SPRITE_COLORKEY 0x2b
#define DRM_I915_GEM_WAIT 0x2c
#define DRM_I915_GEM_CONTEXT_CREATE 0x2d
#define DRM_I915_GEM_CONTEXT_DESTROY 0x2e
#define DRM_I915_GEM_SET_CACHING 0x2f
#define DRM_I915_GEM_GET_CACHING 0x30
#define DRM_I915_REG_READ 0x31
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
#define DRM_IOCTL_I915_FLIP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FLIP, drm_i915_flip_t)
#define DRM_IOCTL_I915_FLIP DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP)
#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t)
#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t)
#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t)
@ -233,13 +218,15 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t)
#define DRM_IOCTL_I915_MMIO DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_MMIO, drm_i915_mmio)
#define DRM_IOCTL_I915_HWS_ADDR DRM_IOW(DRM_COMMAND_BASE + DRM_I915_HWS_ADDR, struct drm_i915_gem_init)
#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init)
#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer)
#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)
#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin)
#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin)
#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
#define DRM_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_SET_CACHING, struct drm_i915_gem_caching)
#define DRM_IOCTL_I915_GEM_GET_CACHING DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GET_CACHING, struct drm_i915_gem_caching)
#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE)
#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT)
#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT)
@ -255,24 +242,14 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id)
#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image)
#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image)
#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
#define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey)
#define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey)
#define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait)
#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create)
#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy)
/* Asynchronous page flipping:
*/
typedef struct drm_i915_flip {
/*
* This is really talking about planes, and we could rename it
* except for the fact that some of the duplicated i915_drm.h files
* out there check for HAVE_I915_FLIP and so might pick up this
* version.
*/
int pipes;
} drm_i915_flip_t;
#define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read)
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
@ -310,15 +287,15 @@ typedef struct drm_i915_irq_wait {
/* Ioctl to query kernel params:
*/
#define I915_PARAM_IRQ_ACTIVE 1
#define I915_PARAM_ALLOW_BATCHBUFFER 2
#define I915_PARAM_LAST_DISPATCH 3
#define I915_PARAM_CHIPSET_ID 4
#define I915_PARAM_HAS_GEM 5
#define I915_PARAM_NUM_FENCES_AVAIL 6
#define I915_PARAM_HAS_OVERLAY 7
#define I915_PARAM_IRQ_ACTIVE 1
#define I915_PARAM_ALLOW_BATCHBUFFER 2
#define I915_PARAM_LAST_DISPATCH 3
#define I915_PARAM_CHIPSET_ID 4
#define I915_PARAM_HAS_GEM 5
#define I915_PARAM_NUM_FENCES_AVAIL 6
#define I915_PARAM_HAS_OVERLAY 7
#define I915_PARAM_HAS_PAGEFLIPPING 8
#define I915_PARAM_HAS_EXECBUF2 9
#define I915_PARAM_HAS_EXECBUF2 9
#define I915_PARAM_HAS_BSD 10
#define I915_PARAM_HAS_BLT 11
#define I915_PARAM_HAS_RELAXED_FENCING 12
@ -326,8 +303,14 @@ typedef struct drm_i915_irq_wait {
#define I915_PARAM_HAS_EXEC_CONSTANTS 14
#define I915_PARAM_HAS_RELAXED_DELTA 15
#define I915_PARAM_HAS_GEN7_SOL_RESET 16
#define I915_PARAM_HAS_LLC 17
#define I915_PARAM_HAS_LLC 17
#define I915_PARAM_HAS_ALIASING_PPGTT 18
#define I915_PARAM_HAS_WAIT_TIMEOUT 19
#define I915_PARAM_HAS_SEMAPHORES 20
#define I915_PARAM_HAS_PRIME_VMAP_FLUSH 21
#define I915_PARAM_RSVD_FOR_FUTURE_USE 22
#define I915_PARAM_HAS_SECURE_BATCHES 23
#define I915_PARAM_HAS_PINNED_BATCHES 24
typedef struct drm_i915_getparam {
int param;
@ -392,70 +375,10 @@ typedef struct drm_i915_vblank_swap {
unsigned int sequence;
} drm_i915_vblank_swap_t;
#define I915_MMIO_READ 0
#define I915_MMIO_WRITE 1
#define I915_MMIO_MAY_READ 0x1
#define I915_MMIO_MAY_WRITE 0x2
#define MMIO_REGS_IA_PRIMATIVES_COUNT 0
#define MMIO_REGS_IA_VERTICES_COUNT 1
#define MMIO_REGS_VS_INVOCATION_COUNT 2
#define MMIO_REGS_GS_PRIMITIVES_COUNT 3
#define MMIO_REGS_GS_INVOCATION_COUNT 4
#define MMIO_REGS_CL_PRIMITIVES_COUNT 5
#define MMIO_REGS_CL_INVOCATION_COUNT 6
#define MMIO_REGS_PS_INVOCATION_COUNT 7
#define MMIO_REGS_PS_DEPTH_COUNT 8
typedef struct drm_i915_mmio_entry {
unsigned int flag;
unsigned int offset;
unsigned int size;
} drm_i915_mmio_entry_t;
typedef struct drm_i915_mmio {
unsigned int read_write:1;
unsigned int reg:31;
void __user *data;
} drm_i915_mmio_t;
typedef struct drm_i915_hws_addr {
__u64 addr;
} drm_i915_hws_addr_t;
/*
* Relocation header is 4 uint32_ts
* 0 - 32 bit reloc count
* 1 - 32-bit relocation type
* 2-3 - 64-bit user buffer handle ptr for another list of relocs.
*/
#define I915_RELOC_HEADER 4
/*
* type 0 relocation has 4-uint32_t stride
* 0 - offset into buffer
* 1 - delta to add in
* 2 - buffer handle
* 3 - reserved (for optimisations later).
*/
/*
* type 1 relocation has 4-uint32_t stride.
* Hangs off the first item in the op list.
* Performed after all valiations are done.
* Try to group relocs into the same relocatee together for
* performance reasons.
* 0 - offset into buffer
* 1 - delta to add in
* 2 - buffer index in op list.
* 3 - relocatee index in op list.
*/
#define I915_RELOC_TYPE_0 0
#define I915_RELOC0_STRIDE 4
#define I915_RELOC_TYPE_1 1
#define I915_RELOC1_STRIDE 4
struct drm_i915_gem_init {
/**
* Beginning offset in the GTT to be managed by the DRM memory
@ -493,8 +416,12 @@ struct drm_i915_gem_pread {
__u64 offset;
/** Length of data to read */
__u64 size;
/** Pointer to write the data into. */
__u64 data_ptr; /* void *, but pointers are not 32/64 compatible */
/**
* Pointer to write the data into.
*
* This is a fixed-size type for 32/64 compatibility.
*/
__u64 data_ptr;
};
struct drm_i915_gem_pwrite {
@ -505,8 +432,12 @@ struct drm_i915_gem_pwrite {
__u64 offset;
/** Length of data to write */
__u64 size;
/** Pointer to read the data from. */
__u64 data_ptr; /* void *, but pointers are not 32/64 compatible */
/**
* Pointer to read the data from.
*
* This is a fixed-size type for 32/64 compatibility.
*/
__u64 data_ptr;
};
struct drm_i915_gem_mmap {
@ -521,8 +452,12 @@ struct drm_i915_gem_mmap {
* The value will be page-aligned.
*/
__u64 size;
/** Returned pointer the data was mapped at */
__u64 addr_ptr; /* void *, but pointers are not 32/64 compatible */
/**
* Returned pointer the data was mapped at.
*
* This is a fixed-size type for 32/64 compatibility.
*/
__u64 addr_ptr;
};
struct drm_i915_gem_mmap_gtt {
@ -667,7 +602,8 @@ struct drm_i915_gem_execbuffer {
__u32 DR1;
__u32 DR4;
__u32 num_cliprects;
__u64 cliprects_ptr; /* struct drm_clip_rect *cliprects */
/** This is a struct drm_clip_rect *cliprects */
__u64 cliprects_ptr;
};
struct drm_i915_gem_exec_object2 {
@ -696,7 +632,7 @@ struct drm_i915_gem_exec_object2 {
#define EXEC_OBJECT_NEEDS_FENCE (1<<0)
__u64 flags;
__u64 rsvd1; /* now used for context info */
__u64 rsvd1;
__u64 rsvd2;
};
@ -733,13 +669,27 @@ struct drm_i915_gem_execbuffer2 {
#define I915_EXEC_CONSTANTS_ABSOLUTE (1<<6)
#define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */
__u64 flags;
__u64 rsvd1;
__u64 rsvd1; /* now used for context info */
__u64 rsvd2;
};
/** Resets the SO write offset registers for transform feedback on gen7. */
#define I915_EXEC_GEN7_SOL_RESET (1<<8)
/** Request a privileged ("secure") batch buffer. Note only available for
* DRM_ROOT_ONLY | DRM_MASTER processes.
*/
#define I915_EXEC_SECURE (1<<9)
/** Inform the kernel that the batch is and will always be pinned. This
* negates the requirement for a workaround to be performed to avoid
* an incoherent CS (such as can be found on 830/845). If this flag is
* not passed, the kernel will endeavour to make sure the batch is
* coherent with the CS before execution. If this flag is passed,
* userspace assumes the responsibility for ensuring the same.
*/
#define I915_EXEC_IS_PINNED (1<<10)
#define I915_EXEC_CONTEXT_ID_MASK (0xffffffff)
#define i915_execbuffer2_set_context_id(eb2, context) \
(eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK
@ -768,10 +718,31 @@ struct drm_i915_gem_busy {
/** Handle of the buffer to check for busy */
__u32 handle;
/** Return busy status (1 if busy, 0 if idle) */
/** Return busy status (1 if busy, 0 if idle).
* The high word is used to indicate on which rings the object
* currently resides:
* 16:31 - busy (r or r/w) rings (16 render, 17 bsd, 18 blt, etc)
*/
__u32 busy;
};
#define I915_CACHING_NONE 0
#define I915_CACHING_CACHED 1
struct drm_i915_gem_caching {
/**
* Handle of the buffer to set/get the caching level of. */
__u32 handle;
/**
* Cacheing level to apply or return value
*
* bits0-15 are for generic caching control (i.e. the above defined
* values). bits16-31 are reserved for platform-specific variations
* (e.g. l3$ caching on gen7). */
__u32 caching;
};
#define I915_TILING_NONE 0
#define I915_TILING_X 1
#define I915_TILING_Y 2
@ -856,7 +827,7 @@ struct drm_i915_get_pipe_from_crtc_id {
#define I915_MADV_WILLNEED 0
#define I915_MADV_DONTNEED 1
#define I915_MADV_PURGED_INTERNAL 2 /* internal state */
#define __I915_MADV_PURGED 2 /* internal state */
struct drm_i915_gem_madvise {
/** Handle of the buffer to change the backing store advice */
@ -871,6 +842,7 @@ struct drm_i915_gem_madvise {
__u32 retained;
};
/* flags */
#define I915_OVERLAY_TYPE_MASK 0xff
#define I915_OVERLAY_YUV_PLANAR 0x01
#define I915_OVERLAY_YUV_PACKED 0x02
@ -968,6 +940,14 @@ struct drm_intel_sprite_colorkey {
__u32 flags;
};
struct drm_i915_gem_wait {
/** Handle of BO we shall wait on */
__u32 bo_handle;
__u32 flags;
/** Number of nanoseconds to wait, Returns time remaining. */
__s64 timeout_ns;
};
struct drm_i915_gem_context_create {
/* output: id of new context*/
__u32 ctx_id;
@ -979,4 +959,15 @@ struct drm_i915_gem_context_destroy {
__u32 pad;
};
#endif /* _I915_DRM_H_ */
struct drm_i915_reg_read {
__u64 offset;
__u64 val; /* Return value */
};
/* For use by IPS driver */
extern unsigned long i915_read_mch_val(void);
extern bool i915_gpu_raise(void);
extern bool i915_gpu_lower(void);
extern bool i915_gpu_busy(void);
extern bool i915_gpu_turbo_disable(void);
#endif /* _UAPI_I915_DRM_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -115,11 +115,9 @@ static int get_context_size(struct drm_device *dev)
break;
case 7:
reg = I915_READ(GEN7_CXT_SIZE);
#ifdef FREEBSD_WIP
if (IS_HASWELL(dev))
ret = HSW_CXT_TOTAL_SIZE(reg) * 64;
else
#endif
ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
break;
default:
@ -140,7 +138,7 @@ static void do_destroy(struct i915_hw_context *ctx)
if (ctx->file_priv)
drm_gem_names_remove(&ctx->file_priv->context_idr, ctx->id);
else
KASSERT(ctx == dev_priv->rings[RCS].default_context,
KASSERT(ctx == dev_priv->ring[RCS].default_context,
("i915_gem_context: ctx != default_context"));
drm_gem_object_unreference(&ctx->obj->base);
@ -178,7 +176,7 @@ create_hw_context(struct drm_device *dev,
* object tracking code. We give an initial ring value simple to pass an
* assertion in the context switch code.
*/
ctx->ring = &dev_priv->rings[RCS];
ctx->ring = &dev_priv->ring[RCS];
/* Default context will never have a file_priv */
if (file_priv == NULL) {
@ -234,8 +232,8 @@ static int create_default_context(struct drm_i915_private *dev_priv)
* may not be available. To avoid this we always pin the
* default context.
*/
dev_priv->rings[RCS].default_context = ctx;
ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
dev_priv->ring[RCS].default_context = ctx;
ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false);
if (ret)
goto err_destroy;
@ -265,12 +263,12 @@ void i915_gem_context_init(struct drm_device *dev)
/* If called from reset, or thaw... we've been here already */
if (dev_priv->hw_contexts_disabled ||
dev_priv->rings[RCS].default_context)
dev_priv->ring[RCS].default_context)
return;
ctx_size = get_context_size(dev);
dev_priv->hw_context_size = get_context_size(dev);
dev_priv->hw_context_size = roundup(dev_priv->hw_context_size, 4096);
dev_priv->hw_context_size = round_up(dev_priv->hw_context_size, 4096);
if (ctx_size <= 0 || ctx_size > (1<<20)) {
dev_priv->hw_contexts_disabled = true;
@ -297,9 +295,9 @@ void i915_gem_context_fini(struct drm_device *dev)
* other code, leading to spurious errors. */
intel_gpu_reset(dev);
i915_gem_object_unpin(dev_priv->rings[RCS].default_context->obj);
i915_gem_object_unpin(dev_priv->ring[RCS].default_context->obj);
do_destroy(dev_priv->rings[RCS].default_context);
do_destroy(dev_priv->ring[RCS].default_context);
}
static int context_idr_cleanup(uint32_t id, void *p, void *data)
@ -389,7 +387,7 @@ static int do_switch(struct i915_hw_context *to)
if (from_obj == to->obj)
return 0;
ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false);
ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false);
if (ret)
return ret;
@ -426,8 +424,7 @@ static int do_switch(struct i915_hw_context *to)
*/
if (from_obj != NULL) {
from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
i915_gem_object_move_to_active(from_obj, ring,
i915_gem_next_request_seqno(ring));
i915_gem_object_move_to_active(from_obj, ring);
/* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
* whole damn pipeline, we don't need to explicitly mark the
* object dirty. The only exception is that the context must be
@ -472,7 +469,7 @@ int i915_switch_context(struct intel_ring_buffer *ring,
if (dev_priv->hw_contexts_disabled)
return 0;
if (ring != &dev_priv->rings[RCS])
if (ring != &dev_priv->ring[RCS])
return 0;
if (to_id == DEFAULT_CONTEXT_ID) {

View File

@ -30,9 +30,8 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/i915_drm.h>
static bool
mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind)
@ -46,7 +45,8 @@ mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind)
int
i915_gem_evict_something(struct drm_device *dev, int min_size,
unsigned alignment, bool mappable)
unsigned alignment, unsigned cache_level,
bool mappable, bool nonblocking)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct list_head eviction_list, unwind_list;
@ -81,11 +81,12 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
INIT_LIST_HEAD(&unwind_list);
if (mappable)
drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space, min_size,
alignment, 0, 0,
dev_priv->mm.gtt_mappable_end);
drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space,
min_size, alignment, cache_level,
0, dev_priv->mm.gtt_mappable_end);
else
drm_mm_init_scan(&dev_priv->mm.gtt_space, min_size, alignment, 0);
drm_mm_init_scan(&dev_priv->mm.gtt_space,
min_size, alignment, cache_level);
/* First see if there is a large enough contiguous idle region... */
list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list) {
@ -93,29 +94,16 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
goto found;
}
if (nonblocking)
goto none;
/* Now merge in the soon-to-be-expired objects... */
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
/* Does the object require an outstanding flush? */
if (obj->base.write_domain)
continue;
if (mark_free(obj, &unwind_list))
goto found;
}
/* Finally add anything with a pending flush (in order of retirement) */
list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) {
if (mark_free(obj, &unwind_list))
goto found;
}
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
if (!obj->base.write_domain)
continue;
if (mark_free(obj, &unwind_list))
goto found;
}
none:
/* Nothing found, clean up and bail out! */
while (!list_empty(&unwind_list)) {
obj = list_first_entry(&unwind_list,
@ -123,7 +111,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
exec_list);
ret = drm_mm_scan_remove_block(obj->gtt_space);
KASSERT(ret == 0, ("drm_mm_scan_remove_block failed %d", ret));
BUG_ON(ret);
list_del_init(&obj->exec_list);
}
@ -166,7 +154,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
}
int
i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
i915_gem_evict_everything(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj, *next;
@ -174,12 +162,11 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
int ret;
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->mm.active_list));
if (lists_empty)
return -ENOSPC;
CTR2(KTR_DRM, "evict_everything %p %d", dev, purgeable_only);
CTR1(KTR_DRM, "evict_everything %p", dev);
/* The gpu_idle will flush everything in the write domain to the
* active list. Then we must move everything off the active list
@ -191,17 +178,11 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
i915_gem_retire_requests(dev);
KASSERT(list_empty(&dev_priv->mm.flushing_list),
("flush list not empty"));
/* Having flushed everything, unbind() should never raise an error */
list_for_each_entry_safe(obj, next,
&dev_priv->mm.inactive_list, mm_list) {
if (!purgeable_only || obj->madv != I915_MADV_WILLNEED) {
if (obj->pin_count == 0)
i915_gem_object_unbind(obj);
}
}
&dev_priv->mm.inactive_list, mm_list)
if (obj->pin_count == 0)
WARN_ON(i915_gem_object_unbind(obj));
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -26,28 +26,75 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
#include <sys/sched.h>
#include <sys/sf_buf.h>
#include <vm/vm_pageout.h>
typedef uint32_t gtt_pte_t;
/* PPGTT stuff */
#define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0))
#define GEN6_PDE_VALID (1 << 0)
/* gen6+ has bit 11-4 for physical addr bit 39-32 */
#define GEN6_PDE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
#define GEN6_PTE_VALID (1 << 0)
#define GEN6_PTE_UNCACHED (1 << 1)
#define HSW_PTE_UNCACHED (0)
#define GEN6_PTE_CACHE_LLC (2 << 1)
#define GEN6_PTE_CACHE_LLC_MLC (3 << 1)
#define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
static inline gtt_pte_t pte_encode(struct drm_device *dev,
dma_addr_t addr,
enum i915_cache_level level)
{
gtt_pte_t pte = GEN6_PTE_VALID;
pte |= GEN6_PTE_ADDR_ENCODE(addr);
switch (level) {
case I915_CACHE_LLC_MLC:
/* Haswell doesn't set L3 this way */
if (IS_HASWELL(dev))
pte |= GEN6_PTE_CACHE_LLC;
else
pte |= GEN6_PTE_CACHE_LLC_MLC;
break;
case I915_CACHE_LLC:
pte |= GEN6_PTE_CACHE_LLC;
break;
case I915_CACHE_NONE:
if (IS_HASWELL(dev))
pte |= HSW_PTE_UNCACHED;
else
pte |= GEN6_PTE_UNCACHED;
break;
default:
BUG();
}
return pte;
}
/* PPGTT support for Sandybdrige/Gen6 and later */
static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
unsigned first_entry,
unsigned num_entries)
{
uint32_t *pt_vaddr;
uint32_t scratch_pte;
gtt_pte_t *pt_vaddr;
gtt_pte_t scratch_pte;
unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
unsigned last_pte, i;
struct sf_buf *sf;
unsigned act_pd, first_pte, last_pte, i;
act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
scratch_pte = GEN6_PTE_ADDR_ENCODE(ppgtt->scratch_page_dma_addr);
scratch_pte |= GEN6_PTE_VALID | GEN6_PTE_CACHE_LLC;
scratch_pte = pte_encode(ppgtt->dev, ppgtt->scratch_page_dma_addr,
I915_CACHE_LLC);
while (num_entries) {
last_pte = first_pte + num_entries;
@ -68,7 +115,6 @@ static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
first_pte = 0;
act_pd++;
}
}
int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
@ -77,47 +123,125 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
struct i915_hw_ppgtt *ppgtt;
unsigned first_pd_entry_in_global_pt;
int i;
int ret = -ENOMEM;
/* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024
* entries. For aliasing ppgtt support we just steal them at the end for
* now. */
first_pd_entry_in_global_pt = 512 * 1024 - I915_PPGTT_PD_ENTRIES;
* now. */
first_pd_entry_in_global_pt = dev_priv->mm.gtt->gtt_total_entries - I915_PPGTT_PD_ENTRIES;
ppgtt = malloc(sizeof(*ppgtt), DRM_I915_GEM, M_WAITOK | M_ZERO);
if (!ppgtt)
return ret;
ppgtt->dev = dev;
ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES;
ppgtt->pt_pages = malloc(sizeof(vm_page_t) * ppgtt->num_pd_entries,
DRM_I915_GEM, M_WAITOK | M_ZERO);
ppgtt->pt_pages = malloc(sizeof(struct page *)*ppgtt->num_pd_entries,
DRM_I915_GEM, M_WAITOK | M_ZERO);
if (!ppgtt->pt_pages)
goto err_ppgtt;
for (i = 0; i < ppgtt->num_pd_entries; i++) {
ppgtt->pt_pages[i] = vm_page_alloc(NULL, 0,
VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED |
VM_ALLOC_ZERO);
if (ppgtt->pt_pages[i] == NULL) {
dev_priv->mm.aliasing_ppgtt = ppgtt;
i915_gem_cleanup_aliasing_ppgtt(dev);
return (-ENOMEM);
}
if (!ppgtt->pt_pages[i])
goto err_pt_alloc;
}
ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt.scratch_page_dma;
if (dev_priv->mm.gtt->needs_dmar) {
ppgtt->pt_dma_addr = malloc(sizeof(dma_addr_t)
*ppgtt->num_pd_entries,
DRM_I915_GEM, M_WAITOK | M_ZERO);
if (!ppgtt->pt_dma_addr)
goto err_pt_alloc;
#ifdef CONFIG_INTEL_IOMMU /* <- Added as a marker on FreeBSD. */
for (i = 0; i < ppgtt->num_pd_entries; i++) {
dma_addr_t pt_addr;
pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i],
0, 4096,
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(dev->pdev,
pt_addr)) {
ret = -EIO;
goto err_pd_pin;
}
ppgtt->pt_dma_addr[i] = pt_addr;
}
#endif
}
ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt->scratch_page_dma;
i915_ppgtt_clear_range(ppgtt, 0,
ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES);
ppgtt->pd_offset = (first_pd_entry_in_global_pt)*sizeof(gtt_pte_t);
i915_ppgtt_clear_range(ppgtt, 0, ppgtt->num_pd_entries *
I915_PPGTT_PT_ENTRIES);
ppgtt->pd_offset = (first_pd_entry_in_global_pt) * sizeof(uint32_t);
dev_priv->mm.aliasing_ppgtt = ppgtt;
return 0;
#ifdef CONFIG_INTEL_IOMMU /* <- Added as a marker on FreeBSD. */
err_pd_pin:
if (ppgtt->pt_dma_addr) {
for (i--; i >= 0; i--)
pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i],
4096, PCI_DMA_BIDIRECTIONAL);
}
#endif
err_pt_alloc:
free(ppgtt->pt_dma_addr, DRM_I915_GEM);
for (i = 0; i < ppgtt->num_pd_entries; i++) {
if (ppgtt->pt_pages[i]) {
vm_page_unwire(ppgtt->pt_pages[i], PQ_INACTIVE);
vm_page_free(ppgtt->pt_pages[i]);
}
}
free(ppgtt->pt_pages, DRM_I915_GEM);
err_ppgtt:
free(ppgtt, DRM_I915_GEM);
return ret;
}
void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
int i;
if (!ppgtt)
return;
#ifdef CONFIG_INTEL_IOMMU /* <- Added as a marker on FreeBSD. */
if (ppgtt->pt_dma_addr) {
for (i = 0; i < ppgtt->num_pd_entries; i++)
pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i],
4096, PCI_DMA_BIDIRECTIONAL);
}
#endif
free(ppgtt->pt_dma_addr, DRM_I915_GEM);
for (i = 0; i < ppgtt->num_pd_entries; i++) {
vm_page_unwire(ppgtt->pt_pages[i], PQ_INACTIVE);
vm_page_free(ppgtt->pt_pages[i]);
}
free(ppgtt->pt_pages, DRM_I915_GEM);
free(ppgtt, DRM_I915_GEM);
}
static void i915_ppgtt_insert_pages(struct i915_hw_ppgtt *ppgtt,
vm_page_t *pages,
unsigned first_entry,
unsigned num_entries,
vm_page_t *pages,
uint32_t pte_flags)
enum i915_cache_level cache_level)
{
uint32_t *pt_vaddr, pte;
uint32_t *pt_vaddr;
unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
unsigned j, last_pte;
@ -135,8 +259,8 @@ static void i915_ppgtt_insert_pages(struct i915_hw_ppgtt *ppgtt,
for (j = first_pte; j < last_pte; j++) {
page_addr = VM_PAGE_TO_PHYS(*pages);
pte = GEN6_PTE_ADDR_ENCODE(page_addr);
pt_vaddr[j] = pte | pte_flags;
pt_vaddr[j] = pte_encode(ppgtt->dev, page_addr,
cache_level);
pages++;
}
@ -154,30 +278,11 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
struct drm_i915_gem_object *obj,
enum i915_cache_level cache_level)
{
struct drm_device *dev;
struct drm_i915_private *dev_priv;
uint32_t pte_flags;
dev = obj->base.dev;
dev_priv = dev->dev_private;
pte_flags = GEN6_PTE_VALID;
switch (cache_level) {
case I915_CACHE_LLC_MLC:
pte_flags |= GEN6_PTE_CACHE_LLC_MLC;
break;
case I915_CACHE_LLC:
pte_flags |= GEN6_PTE_CACHE_LLC;
break;
case I915_CACHE_NONE:
pte_flags |= GEN6_PTE_UNCACHED;
break;
default:
panic("cache mode");
}
i915_ppgtt_insert_pages(ppgtt, obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT, obj->pages, pte_flags);
i915_ppgtt_insert_pages(ppgtt,
obj->pages,
obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT,
cache_level);
}
void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
@ -194,7 +299,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev)
uint32_t pd_offset;
struct intel_ring_buffer *ring;
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
u_int first_pd_entry_in_global_pt;
uint32_t __iomem *pd_addr;
uint32_t pd_entry;
int i;
@ -202,17 +307,22 @@ void i915_gem_init_ppgtt(struct drm_device *dev)
return;
first_pd_entry_in_global_pt = 512 * 1024 - I915_PPGTT_PD_ENTRIES;
pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(uint32_t);
for (i = 0; i < ppgtt->num_pd_entries; i++) {
vm_paddr_t pt_addr;
pt_addr = VM_PAGE_TO_PHYS(ppgtt->pt_pages[i]);
if (dev_priv->mm.gtt->needs_dmar)
pt_addr = ppgtt->pt_dma_addr[i];
else
pt_addr = VM_PAGE_TO_PHYS(ppgtt->pt_pages[i]);
pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr);
pd_entry |= GEN6_PDE_VALID;
intel_gtt_write(first_pd_entry_in_global_pt + i, pd_entry);
/* NOTE Linux<->FreeBSD: Arguments of writel() are reversed. */
writel(pd_addr + i, pd_entry);
}
intel_gtt_read_pte(first_pd_entry_in_global_pt);
readl(pd_addr);
pd_offset = ppgtt->pd_offset;
pd_offset /= 64; /* in cachelines, */
@ -250,12 +360,12 @@ static bool do_idling(struct drm_i915_private *dev_priv)
{
bool ret = dev_priv->mm.interruptible;
if (dev_priv->mm.gtt.do_idle_maps) {
if (unlikely(dev_priv->mm.gtt->do_idle_maps)) {
dev_priv->mm.interruptible = false;
if (i915_gpu_idle(dev_priv->dev)) {
DRM_ERROR("Couldn't idle GPU\n");
/* Wait a bit, in hopes it avoids the hang */
DELAY(10);
udelay(10);
}
}
@ -264,57 +374,35 @@ static bool do_idling(struct drm_i915_private *dev_priv)
static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
{
if (dev_priv->mm.gtt.do_idle_maps)
if (unlikely(dev_priv->mm.gtt->do_idle_maps))
dev_priv->mm.interruptible = interruptible;
}
void
i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
static void i915_ggtt_clear_range(struct drm_device *dev,
unsigned first_entry,
unsigned num_entries)
{
struct drm_i915_private *dev_priv;
struct i915_hw_ppgtt *ppgtt;
vm_page_t m;
struct drm_i915_private *dev_priv = dev->dev_private;
gtt_pte_t scratch_pte;
gtt_pte_t __iomem *gtt_base = dev_priv->mm.gtt->gtt + first_entry;
const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry;
int i;
dev_priv = dev->dev_private;
ppgtt = dev_priv->mm.aliasing_ppgtt;
if (ppgtt == NULL)
if (INTEL_INFO(dev)->gen < 6) {
intel_gtt_clear_range(first_entry, num_entries);
return;
dev_priv->mm.aliasing_ppgtt = NULL;
for (i = 0; i < ppgtt->num_pd_entries; i++) {
m = ppgtt->pt_pages[i];
if (m != NULL) {
vm_page_unwire(m, PQ_INACTIVE);
vm_page_free(m);
}
}
free(ppgtt->pt_pages, DRM_I915_GEM);
free(ppgtt, DRM_I915_GEM);
}
if (WARN(num_entries > max_entries,
"First entry = %d; Num entries = %d (max=%d)\n",
first_entry, num_entries, max_entries))
num_entries = max_entries;
static unsigned int
cache_level_to_agp_type(struct drm_device *dev, enum i915_cache_level
cache_level)
{
switch (cache_level) {
case I915_CACHE_LLC_MLC:
if (INTEL_INFO(dev)->gen >= 6)
return (AGP_USER_CACHED_MEMORY_LLC_MLC);
/*
* Older chipsets do not have this extra level of CPU
* cacheing, so fallthrough and request the PTE simply
* as cached.
*/
case I915_CACHE_LLC:
return (AGP_USER_CACHED_MEMORY);
default:
case I915_CACHE_NONE:
return (AGP_USER_MEMORY);
}
scratch_pte = pte_encode(dev, dev_priv->mm.gtt->scratch_page_dma, I915_CACHE_LLC);
for (i = 0; i < num_entries; i++)
iowrite32(scratch_pte, &gtt_base[i]);
readl(gtt_base);
}
void i915_gem_restore_gtt_mappings(struct drm_device *dev)
@ -323,45 +411,99 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
struct drm_i915_gem_object *obj;
/* First fill our portion of the GTT with scratch pages */
intel_gtt_clear_range(dev_priv->mm.gtt_start / PAGE_SIZE,
i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE,
(dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE);
list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
i915_gem_clflush_object(obj);
i915_gem_gtt_bind_object(obj, obj->cache_level);
}
intel_gtt_chipset_flush();
i915_gem_chipset_flush(dev);
}
int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
{
if (obj->has_dma_mapping)
return 0;
#ifdef FREEBSD_WIP
if (!dma_map_sg(&obj->base.dev->pdev->dev,
obj->pages->sgl, obj->pages->nents,
PCI_DMA_BIDIRECTIONAL))
return -ENOSPC;
#endif /* FREEBSD_WIP */
return 0;
}
/*
* Binds an object into the global gtt with the specified cache level. The object
* will be accessible to the GPU via commands whose operands reference offsets
* within the global GTT as well as accessible by the GPU through the GMADR
* mapped BAR (dev_priv->mm.gtt->gtt).
*/
static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj,
enum i915_cache_level level)
{
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
const int first_entry = obj->gtt_space->start >> PAGE_SHIFT;
#if defined(INVARIANTS)
const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry;
#endif
gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gtt->gtt + first_entry;
int i = 0;
vm_paddr_t addr;
for (i = 0; i < obj->base.size >> PAGE_SHIFT; ++i) {
addr = VM_PAGE_TO_PHYS(obj->pages[i]);
iowrite32(pte_encode(dev, addr, level), &gtt_entries[i]);
}
BUG_ON(i > max_entries);
BUG_ON(i != obj->base.size / PAGE_SIZE);
/* XXX: This serves as a posting read to make sure that the PTE has
* actually been updated. There is some concern that even though
* registers and PTEs are within the same BAR that they are potentially
* of NUMA access patterns. Therefore, even with the way we assume
* hardware should work, we must keep this posting read for paranoia.
*/
if (i != 0)
WARN_ON(readl(&gtt_entries[i-1]) != pte_encode(dev, addr, level));
/* This next bit makes the above posting read even more important. We
* want to flush the TLBs only after we're certain all the PTE updates
* have finished.
*/
I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN);
POSTING_READ(GFX_FLSH_CNTL_GEN6);
}
void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
enum i915_cache_level cache_level)
{
struct drm_device *dev;
struct drm_i915_private *dev_priv;
unsigned int agp_type;
dev = obj->base.dev;
dev_priv = dev->dev_private;
agp_type = cache_level_to_agp_type(dev, cache_level);
intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT, obj->pages, agp_type);
struct drm_device *dev = obj->base.dev;
if (INTEL_INFO(dev)->gen < 6) {
unsigned int flags = (cache_level == I915_CACHE_NONE) ?
AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY;
intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT,
obj->pages,
flags);
} else {
gen6_ggtt_bind_object(obj, cache_level);
}
obj->has_global_gtt_mapping = 1;
}
void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
{
intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT);
i915_ggtt_clear_range(obj->base.dev,
obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT);
obj->has_global_gtt_mapping = 0;
}
@ -374,35 +516,244 @@ void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj)
interruptible = do_idling(dev_priv);
#ifdef FREEBSD_WIP
if (!obj->has_dma_mapping)
dma_unmap_sg(&dev->pdev->dev,
obj->pages->sgl, obj->pages->nents,
PCI_DMA_BIDIRECTIONAL);
#endif /* FREEBSD_WIP */
undo_idling(dev_priv, interruptible);
}
int i915_gem_init_global_gtt(struct drm_device *dev,
static void i915_gtt_color_adjust(struct drm_mm_node *node,
unsigned long color,
unsigned long *start,
unsigned long *end)
{
if (node->color != color)
*start += 4096;
if (!list_empty(&node->node_list)) {
node = list_entry(node->node_list.next,
struct drm_mm_node,
node_list);
if (node->allocated && node->color != color)
*end -= 4096;
}
}
void i915_gem_init_global_gtt(struct drm_device *dev,
unsigned long start,
unsigned long mappable_end,
unsigned long end)
{
drm_i915_private_t *dev_priv = dev->dev_private;
unsigned long mappable;
int error;
mappable = min(end, mappable_end) - start;
/* Substract the guard page ... */
drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE);
if (!HAS_LLC(dev))
dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust;
dev_priv->mm.gtt_start = start;
dev_priv->mm.gtt_mappable_end = mappable_end;
dev_priv->mm.gtt_end = end;
dev_priv->mm.gtt_total = end - start;
dev_priv->mm.mappable_gtt_total = mappable;
dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start;
/* ... but ensure that we clear the entire range. */
intel_gtt_clear_range(start / PAGE_SIZE, (end-start) / PAGE_SIZE);
i915_ggtt_clear_range(dev, start / PAGE_SIZE, (end-start) / PAGE_SIZE);
device_printf(dev->dev,
"taking over the fictitious range 0x%lx-0x%lx\n",
dev->agp->base + start, dev->agp->base + start + mappable);
error = -vm_phys_fictitious_reg_range(dev->agp->base + start,
dev->agp->base + start + mappable, VM_MEMATTR_WRITE_COMBINING);
return (error);
dev_priv->mm.gtt_base_addr + start,
dev_priv->mm.gtt_base_addr + start + dev_priv->mm.mappable_gtt_total);
vm_phys_fictitious_reg_range(dev_priv->mm.gtt_base_addr + start,
dev_priv->mm.gtt_base_addr + start + dev_priv->mm.mappable_gtt_total,
VM_MEMATTR_WRITE_COMBINING);
}
static int setup_scratch_page(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
vm_page_t page;
dma_addr_t dma_addr;
int tries = 0;
int req = VM_ALLOC_ZERO | VM_ALLOC_NOOBJ;
retry:
page = vm_page_alloc_contig(NULL, 0, req, 1, 0, 0xffffffff,
PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);
if (page == NULL) {
if (tries < 1) {
if (!vm_page_reclaim_contig(req, 1, 0, 0xffffffff,
PAGE_SIZE, 0))
VM_WAIT;
tries++;
goto retry;
}
return -ENOMEM;
}
if ((page->flags & PG_ZERO) == 0)
pmap_zero_page(page);
#ifdef CONFIG_INTEL_IOMMU
dma_addr = pci_map_page(dev->pdev, page, 0, PAGE_SIZE,
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(dev->pdev, dma_addr))
return -EINVAL;
#else
dma_addr = VM_PAGE_TO_PHYS(page);
#endif
dev_priv->mm.gtt->scratch_page = page;
dev_priv->mm.gtt->scratch_page_dma = dma_addr;
return 0;
}
static void teardown_scratch_page(struct drm_device *dev)
{
#ifdef CONFIG_INTEL_IOMMU /* <- Added as a marker on FreeBSD. */
struct drm_i915_private *dev_priv = dev->dev_private;
pci_unmap_page(dev->pdev, dev_priv->mm.gtt->scratch_page_dma,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
#endif
}
static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
{
snb_gmch_ctl >>= SNB_GMCH_GGMS_SHIFT;
snb_gmch_ctl &= SNB_GMCH_GGMS_MASK;
return snb_gmch_ctl << 20;
}
static inline unsigned int gen6_get_stolen_size(u16 snb_gmch_ctl)
{
snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT;
snb_gmch_ctl &= SNB_GMCH_GMS_MASK;
return snb_gmch_ctl << 25; /* 32 MB units */
}
static inline unsigned int gen7_get_stolen_size(u16 snb_gmch_ctl)
{
static const int stolen_decoder[] = {
0, 0, 0, 0, 0, 32, 48, 64, 128, 256, 96, 160, 224, 352};
snb_gmch_ctl >>= IVB_GMCH_GMS_SHIFT;
snb_gmch_ctl &= IVB_GMCH_GMS_MASK;
return stolen_decoder[snb_gmch_ctl] << 20;
}
int i915_gem_gtt_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
vm_paddr_t gtt_bus_addr;
u16 snb_gmch_ctl;
int ret;
/* On modern platforms we need not worry ourself with the legacy
* hostbridge query stuff. Skip it entirely
*/
if (INTEL_INFO(dev)->gen < 6) {
#ifdef FREEBSD_WIP
ret = intel_gmch_probe(dev_priv->bridge_dev, dev->pdev, NULL);
if (!ret) {
DRM_ERROR("failed to set up gmch\n");
return -EIO;
}
#endif /* FREEBSD_WIP */
dev_priv->mm.gtt = intel_gtt_get();
if (!dev_priv->mm.gtt) {
DRM_ERROR("Failed to initialize GTT\n");
#ifdef FREEBSD_WIP
intel_gmch_remove();
#endif /* FREEBSD_WIP */
return -ENODEV;
}
return 0;
}
dev_priv->mm.gtt = malloc(sizeof(*dev_priv->mm.gtt), DRM_I915_GEM, M_WAITOK | M_ZERO);
if (!dev_priv->mm.gtt)
return -ENOMEM;
#ifdef FREEBSD_WIP
if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40)))
pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40));
#endif /* FREEBSD_WIP */
#ifdef CONFIG_INTEL_IOMMU
dev_priv->mm.gtt->needs_dmar = 1;
#endif
/* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */
gtt_bus_addr = drm_get_resource_start(dev, 0) + (2<<20);
dev_priv->mm.gtt->gma_bus_addr = drm_get_resource_start(dev, 2);
/* i9xx_setup */
pci_read_config_word(dev->dev, SNB_GMCH_CTRL, &snb_gmch_ctl);
dev_priv->mm.gtt->gtt_total_entries =
gen6_get_total_gtt_size(snb_gmch_ctl) / sizeof(gtt_pte_t);
if (INTEL_INFO(dev)->gen < 7)
dev_priv->mm.gtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl);
else
dev_priv->mm.gtt->stolen_size = gen7_get_stolen_size(snb_gmch_ctl);
dev_priv->mm.gtt->gtt_mappable_entries = drm_get_resource_len(dev, 2) >> PAGE_SHIFT;
/* 64/512MB is the current min/max we actually know of, but this is just a
* coarse sanity check.
*/
if ((dev_priv->mm.gtt->gtt_mappable_entries >> 8) < 64 ||
dev_priv->mm.gtt->gtt_mappable_entries > dev_priv->mm.gtt->gtt_total_entries) {
DRM_ERROR("Unknown GMADR entries (%d)\n",
dev_priv->mm.gtt->gtt_mappable_entries);
ret = -ENXIO;
goto err_out;
}
ret = setup_scratch_page(dev);
if (ret) {
DRM_ERROR("Scratch setup failed\n");
goto err_out;
}
dev_priv->mm.gtt->gtt = pmap_mapdev_attr(gtt_bus_addr,
/* The size is used later by pmap_unmapdev. */
dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t),
VM_MEMATTR_WRITE_COMBINING);
if (!dev_priv->mm.gtt->gtt) {
DRM_ERROR("Failed to map the gtt page table\n");
teardown_scratch_page(dev);
ret = -ENOMEM;
goto err_out;
}
/* GMADR is the PCI aperture used by SW to access tiled GFX surfaces in a linear fashion. */
DRM_INFO("Memory usable by graphics device = %dM\n", dev_priv->mm.gtt->gtt_total_entries >> 8);
DRM_DEBUG_DRIVER("GMADR size = %dM\n", dev_priv->mm.gtt->gtt_mappable_entries >> 8);
DRM_DEBUG_DRIVER("GTT stolen size = %dM\n", dev_priv->mm.gtt->stolen_size >> 20);
return 0;
err_out:
free(dev_priv->mm.gtt, DRM_I915_GEM);
#ifdef FREEBSD_WIP
if (INTEL_INFO(dev)->gen < 6)
intel_gmch_remove();
#endif /* FREEBSD_WIP */
return ret;
}
void i915_gem_gtt_fini(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
pmap_unmapdev((vm_offset_t)dev_priv->mm.gtt->gtt,
dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t));
teardown_scratch_page(dev);
#ifdef FREEBSD_WIP
if (INTEL_INFO(dev)->gen < 6)
intel_gmch_remove();
#endif /* FREEBSD_WIP */
if (INTEL_INFO(dev)->gen >= 6)
free(dev_priv->mm.gtt, DRM_I915_GEM);
}

View File

@ -30,7 +30,6 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
@ -46,56 +45,48 @@ __FBSDID("$FreeBSD$");
* for is a boon.
*/
#define PTE_ADDRESS_MASK 0xfffff000
#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */
#define PTE_MAPPING_TYPE_UNCACHED (0 << 1)
#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */
#define PTE_MAPPING_TYPE_CACHED (3 << 1)
#define PTE_MAPPING_TYPE_MASK (3 << 1)
#define PTE_VALID (1 << 0)
/**
* i915_stolen_to_phys - take an offset into stolen memory and turn it into
* a physical one
* @dev: drm device
* @offset: address to translate
*
* Some chip functions require allocations from stolen space and need the
* physical address of the memory in question.
*/
static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset)
static unsigned long i915_stolen_to_physical(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
device_t pdev = dev_priv->bridge_dev;
u32 base;
#if 0
/* On the machines I have tested the Graphics Base of Stolen Memory
* is unreliable, so compute the base by subtracting the stolen memory
* from the Top of Low Usable DRAM which is where the BIOS places
* the graphics stolen memory.
* is unreliable, so on those compute the base by subtracting the
* stolen memory from the Top of Low Usable DRAM which is where the
* BIOS places the graphics stolen memory.
*
* On gen2, the layout is slightly different with the Graphics Segment
* immediately following Top of Memory (or Top of Usable DRAM). Note
* it appears that TOUD is only reported by 865g, so we just use the
* top of memory as determined by the e820 probe.
*
* XXX gen2 requires an unavailable symbol and 945gm fails with
* its value of TOLUD.
*/
if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) {
/* top 32bits are reserved = 0 */
pci_read_config_dword(pdev, 0xA4, &base);
} else {
/* XXX presume 8xx is the same as i915 */
pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base);
}
#else
if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) {
u16 val;
val = pci_read_config(pdev, 0xb0, 2);
base = val >> 4 << 20;
} else {
base = 0;
if (INTEL_INFO(dev)->gen >= 6) {
/* Read Base Data of Stolen Memory Register (BDSM) directly.
* Note that there is also a MCHBAR miror at 0x1080c0 or
* we could use device 2:0x5c instead.
*/
pci_read_config_dword(dev->dev, 0xB0, &base);
base &= ~4095; /* lower bits used for locking register */
} else if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) {
/* Read Graphics Base of Stolen Memory directly */
pci_read_config_dword(dev->dev, 0xA4, &base);
#if 0
} else if (IS_GEN3(dev)) {
u8 val;
val = pci_read_config(pdev, 0x9c, 1);
/* Stolen is immediately below Top of Low Usable DRAM */
pci_read_config_byte(pdev, 0x9c, &val);
base = val >> 3 << 27;
}
base -= dev_priv->mm.gtt.stolen_size;
base -= dev_priv->mm.gtt->stolen_size;
} else {
/* Stolen is immediately above Top of Memory */
base = max_low_pfn_mapped << PAGE_SHIFT;
#endif
}
return base + offset;
return base;
}
static void i915_warn_stolen(struct drm_device *dev)
@ -107,7 +98,7 @@ static void i915_warn_stolen(struct drm_device *dev)
static void i915_setup_compression(struct drm_device *dev, int size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_mm_node *compressed_fb, *compressed_llb;
struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb);
unsigned long cfb_base;
unsigned long ll_base = 0;
@ -120,7 +111,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
if (!compressed_fb)
goto err;
cfb_base = i915_stolen_to_phys(dev, compressed_fb->start);
cfb_base = dev_priv->mm.stolen_base + compressed_fb->start;
if (!cfb_base)
goto err_fb;
@ -133,7 +124,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
if (!compressed_llb)
goto err_fb;
ll_base = i915_stolen_to_phys(dev, compressed_llb->start);
ll_base = dev_priv->mm.stolen_base + compressed_llb->start;
if (!ll_base)
goto err_llb;
}
@ -152,7 +143,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
}
DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n",
cfb_base, ll_base, size >> 20);
(long)cfb_base, (long)ll_base, size >> 20);
return;
err_llb:
@ -182,7 +173,14 @@ void i915_gem_cleanup_stolen(struct drm_device *dev)
int i915_gem_init_stolen(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long prealloc_size = dev_priv->mm.gtt.stolen_size;
unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size;
dev_priv->mm.stolen_base = i915_stolen_to_physical(dev);
if (dev_priv->mm.stolen_base == 0)
return 0;
DRM_DEBUG_KMS("found %d bytes of stolen memory at %08lx\n",
dev_priv->mm.gtt->stolen_size, dev_priv->mm.stolen_base);
/* Basic memrange allocator for stolen space */
drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size);

View File

@ -29,7 +29,6 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
@ -95,7 +94,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
if (INTEL_INFO(dev)->gen >= 6) {
if (IS_VALLEYVIEW(dev)) {
swizzle_x = I915_BIT_6_SWIZZLE_NONE;
swizzle_y = I915_BIT_6_SWIZZLE_NONE;
} else if (INTEL_INFO(dev)->gen >= 6) {
uint32_t dimm_c0, dimm_c1;
dimm_c0 = I915_READ(MAD_DIMM_C0);
dimm_c1 = I915_READ(MAD_DIMM_C1);
@ -313,12 +315,12 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
if (!i915_tiling_ok(dev,
args->stride, obj->base.size, args->tiling_mode)) {
drm_gem_object_unreference(&obj->base);
drm_gem_object_unreference_unlocked(&obj->base);
return -EINVAL;
}
if (obj->pin_count) {
drm_gem_object_unreference(&obj->base);
drm_gem_object_unreference_unlocked(&obj->base);
return -EBUSY;
}
@ -483,7 +485,8 @@ i915_gem_object_do_bit_17_swizzle_page(struct drm_i915_gem_object *obj,
return;
new_bit_17 = VM_PAGE_TO_PHYS(m) >> 17;
if ((new_bit_17 & 0x1) != (test_bit(m->pindex, obj->bit_17) != 0)) {
if ((new_bit_17 & 0x1) !=
(test_bit(m->pindex, obj->bit_17) != 0)) {
i915_gem_swizzle_page(m);
vm_page_dirty(m);
}
@ -499,11 +502,12 @@ i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
return;
for (i = 0; i < page_count; i++) {
char new_bit_17 = VM_PAGE_TO_PHYS(obj->pages[i]) >> 17;
vm_page_t page = obj->pages[i];
char new_bit_17 = VM_PAGE_TO_PHYS(page) >> 17;
if ((new_bit_17 & 0x1) !=
(test_bit(i, obj->bit_17) != 0)) {
i915_gem_swizzle_page(obj->pages[i]);
vm_page_dirty(obj->pages[i]);
i915_gem_swizzle_page(page);
vm_page_dirty(page);
}
}
}
@ -516,14 +520,20 @@ i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj)
if (obj->bit_17 == NULL) {
obj->bit_17 = malloc(BITS_TO_LONGS(page_count) *
sizeof(long), DRM_I915_GEM, M_WAITOK);
sizeof(long), DRM_I915_GEM, M_WAITOK);
if (obj->bit_17 == NULL) {
DRM_ERROR("Failed to allocate memory for bit 17 "
"record\n");
return;
}
}
/* XXXKIB: review locking, atomics might be not needed there */
for (i = 0; i < page_count; i++) {
if (VM_PAGE_TO_PHYS(obj->pages[i]) & (1 << 17))
set_bit(i, obj->bit_17);
vm_page_t page = obj->pages[i];
if (VM_PAGE_TO_PHYS(page) & (1 << 17))
__set_bit(i, obj->bit_17);
else
clear_bit(i, obj->bit_17);
__clear_bit(i, obj->bit_17);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -117,21 +117,6 @@ __FBSDID("$FreeBSD$");
#define GEN6_GRDOM_MEDIA (1 << 2)
#define GEN6_GRDOM_BLT (1 << 3)
#define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0))
#define GEN6_PDE_VALID (1 << 0)
#define GEN6_PDE_LARGE_PAGE (2 << 0) /* use 32kb pages */
/* gen6+ has bit 11-4 for physical addr bit 39-32 */
#define GEN6_PDE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
#define GEN6_PTE_VALID (1 << 0)
#define GEN6_PTE_UNCACHED (1 << 1)
#define GEN6_PTE_CACHE_LLC (2 << 1)
#define GEN6_PTE_CACHE_LLC_MLC (3 << 1)
#define GEN6_PTE_CACHE_BITS (3 << 1)
#define GEN6_PTE_GFDT (1 << 3)
#define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
#define RING_PP_DIR_BASE(ring) ((ring)->mmio_base+0x228)
#define RING_PP_DIR_BASE_READ(ring) ((ring)->mmio_base+0x518)
#define RING_PP_DIR_DCLV(ring) ((ring)->mmio_base+0x220)
@ -740,10 +725,6 @@ __FBSDID("$FreeBSD$");
#define GEN6_BSD_SLEEP_FLUSH_DISABLE (1 << 2)
#define GEN6_BSD_SLEEP_INDICATOR (1 << 3)
#define GEN6_BSD_GO_INDICATOR (1 << 4)
#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK (1 << 16)
#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE (1 << 0)
#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE 0
#define GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR (1 << 3)
#define GEN6_BSD_HWSTAM 0x12098
#define GEN6_BSD_IMR 0x120a8
@ -1682,21 +1663,19 @@ __FBSDID("$FreeBSD$");
#define PORT_HOTPLUG_STAT 0x61114
/* HDMI/DP bits are gen4+ */
#define HDMIB_HOTPLUG_INT_STATUS (1 << 29)
#define DPB_HOTPLUG_INT_STATUS (1 << 29)
#define HDMIC_HOTPLUG_INT_STATUS (1 << 28)
#define DPC_HOTPLUG_INT_STATUS (1 << 28)
#define HDMID_HOTPLUG_INT_STATUS (1 << 27)
#define DPD_HOTPLUG_INT_STATUS (1 << 27)
#define DPB_HOTPLUG_LIVE_STATUS (1 << 29)
#define DPC_HOTPLUG_LIVE_STATUS (1 << 28)
#define DPD_HOTPLUG_LIVE_STATUS (1 << 27)
#define DPD_HOTPLUG_INT_STATUS (3 << 21)
#define DPC_HOTPLUG_INT_STATUS (3 << 19)
#define DPB_HOTPLUG_INT_STATUS (3 << 17)
/* HDMI bits are shared with the DP bits */
/*
#define HDMIB_HOTPLUG_LIVE_STATUS (1 << 29)
#define HDMIC_HOTPLUG_LIVE_STATUS (1 << 28)
#define HDMID_HOTPLUG_LIVE_STATUS (1 << 27)
#define HDMID_HOTPLUG_INT_STATUS (3 << 21)
#define HDMIC_HOTPLUG_INT_STATUS (3 << 19)
#define HDMIB_HOTPLUG_INT_STATUS (3 << 17)
*/
/* CRT/TV common between gen3+ */
#define CRT_HOTPLUG_INT_STATUS (1 << 11)
#define TV_HOTPLUG_INT_STATUS (1 << 10)
@ -1704,8 +1683,6 @@ __FBSDID("$FreeBSD$");
#define CRT_HOTPLUG_MONITOR_COLOR (3 << 8)
#define CRT_HOTPLUG_MONITOR_MONO (2 << 8)
#define CRT_HOTPLUG_MONITOR_NONE (0 << 8)
#define SDVOC_HOTPLUG_INT_STATUS (1 << 7)
#define SDVOB_HOTPLUG_INT_STATUS (1 << 6)
/* SDVO is different across gen3/4 */
#define SDVOC_HOTPLUG_INT_STATUS_G4X (1 << 3)
#define SDVOB_HOTPLUG_INT_STATUS_G4X (1 << 2)
@ -3307,12 +3284,6 @@ __FBSDID("$FreeBSD$");
#define DISPLAY_PORT_PLL_BIOS_1 0x46010
#define DISPLAY_PORT_PLL_BIOS_2 0x46014
#define PCH_DSPCLK_GATE_D 0x42020
# define DPFCUNIT_CLOCK_GATE_DISABLE (1 << 9)
# define DPFCRUNIT_CLOCK_GATE_DISABLE (1 << 8)
# define DPFDUNIT_CLOCK_GATE_DISABLE (1 << 7)
# define DPARBUNIT_CLOCK_GATE_DISABLE (1 << 5)
#define PCH_3DCGDIS0 0x46020
# define MARIUNIT_CLOCK_GATE_DISABLE (1 << 18)
# define SVSMUNIT_CLOCK_GATE_DISABLE (1 << 1)
@ -3486,15 +3457,6 @@ __FBSDID("$FreeBSD$");
#define ILK_HDCP_DISABLE (1<<25)
#define ILK_eDP_A_DISABLE (1<<24)
#define ILK_DESKTOP (1<<23)
#define ILK_DSPCLK_GATE 0x42020
#define IVB_VRHUNIT_CLK_GATE (1<<28)
#define ILK_DPARB_CLK_GATE (1<<5)
#define ILK_DPFD_CLK_GATE (1<<7)
/* According to spec this bit 7/8/9 of 0x42020 should be set to enable FBC */
#define ILK_CLK_FBC (1<<7)
#define ILK_DPFC_DIS1 (1<<8)
#define ILK_DPFC_DIS2 (1<<9)
#define ILK_DSPCLK_GATE_D 0x42020
#define ILK_VRHUNIT_CLOCK_GATE_DISABLE (1 << 28)
@ -3763,7 +3725,7 @@ __FBSDID("$FreeBSD$");
#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
#define VLV_VIDEO_DIP_CTL_A 0x60220
#define VLV_VIDEO_DIP_CTL_A 0x60200
#define VLV_VIDEO_DIP_DATA_A 0x60208
#define VLV_VIDEO_DIP_GDCP_PAYLOAD_A 0x60210
@ -3879,7 +3841,6 @@ __FBSDID("$FreeBSD$");
#define _TRANSA_CHICKEN2 0xf0064
#define _TRANSB_CHICKEN2 0xf1064
#define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2)
#define TRANS_AUTOTRAIN_GEN_STALL_DIS (1<<31)
#define TRANS_CHICKEN2_TIMING_OVERRIDE (1<<31)
#define TRANS_CHICKEN2_FDI_POLARITY_REVERSED (1<<29)
@ -4030,33 +3991,7 @@ __FBSDID("$FreeBSD$");
#define FDI_PLL_CTL_1 0xfe000
#define FDI_PLL_CTL_2 0xfe004
/* CRT */
#define PCH_ADPA 0xe1100
#define ADPA_TRANS_SELECT_MASK (1<<30)
#define ADPA_TRANS_A_SELECT 0
#define ADPA_TRANS_B_SELECT (1<<30)
#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */
#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24)
#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24)
#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24)
#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24)
#define ADPA_CRT_HOTPLUG_ENABLE (1<<23)
#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22)
#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22)
#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21)
#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21)
#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20)
#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20)
#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18)
#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18)
#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18)
#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18)
#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17)
#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17)
#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
/* or SDVOB */
#define VLV_HDMIB 0x61140
#define HDMIB 0xe1140
#define PORT_ENABLE (1 << 31)
#define TRANSCODER(pipe) ((pipe) << 30)
@ -4100,21 +4035,6 @@ __FBSDID("$FreeBSD$");
#define PIPEB_PP_OFF_DELAYS 0x6130c
#define PIPEB_PP_DIVISOR 0x61310
#define BLC_PWM_CPU_CTL2 0x48250
#define PWM_ENABLE (1 << 31)
#define PWM_PIPE_A (0 << 29)
#define PWM_PIPE_B (1 << 29)
#define BLC_PWM_CPU_CTL 0x48254
#define BLC_PWM_PCH_CTL1 0xc8250
#define PWM_PCH_ENABLE (1 << 31)
#define PWM_POLARITY_ACTIVE_LOW (1 << 29)
#define PWM_POLARITY_ACTIVE_HIGH (0 << 29)
#define PWM_POLARITY_ACTIVE_LOW2 (1 << 28)
#define PWM_POLARITY_ACTIVE_HIGH2 (0 << 28)
#define BLC_PWM_PCH_CTL2 0xc8254
#define PCH_PP_STATUS 0xc7200
#define PCH_PP_CONTROL 0xc7204
#define PANEL_UNLOCK_REGS (0xabcd << 16)
@ -4701,15 +4621,6 @@ __FBSDID("$FreeBSD$");
#define TRANS_CLK_SEL_DISABLED (0x0<<29)
#define TRANS_CLK_SEL_PORT(x) ((x+1)<<29)
/* Pipe clock selection */
#define PIPE_CLK_SEL_A 0x46140
#define PIPE_CLK_SEL_B 0x46144
#define PIPE_CLK_SEL(pipe) _PIPE(pipe, \
PIPE_CLK_SEL_A, \
PIPE_CLK_SEL_B)
/* For each pipe, we need to select the corresponding port clock */
#define PIPE_CLK_SEL_DISABLED (0x0<<29)
#define PIPE_CLK_SEL_PORT(x) ((x+1)<<29)
#define _TRANSA_MSA_MISC 0x60410
#define _TRANSB_MSA_MISC 0x61410
#define TRANS_MSA_MISC(tran) _TRANSCODER(tran, _TRANSA_MSA_MISC, \

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,256 @@
/*
* Intel ACPI functions
*
* _DSM related code stolen from nouveau_acpi.c.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/i915/i915_drv.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <contrib/dev/acpica/include/accommon.h>
#include <dev/acpica/acpivar.h>
#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
#define INTEL_DSM_FN_SUPPORTED_FUNCTIONS 0 /* No args */
#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
static struct intel_dsm_priv {
ACPI_HANDLE dhandle;
} intel_dsm_priv;
static const u8 intel_dsm_guid[] = {
0xd3, 0x73, 0xd8, 0x7e,
0xd0, 0xc2,
0x4f, 0x4e,
0xa8, 0x54,
0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
};
static int intel_dsm(ACPI_HANDLE handle, int func, int arg)
{
ACPI_BUFFER output = { ACPI_ALLOCATE_BUFFER, NULL };
ACPI_OBJECT_LIST input;
ACPI_OBJECT params[4];
ACPI_OBJECT *obj;
u32 result;
int ret = 0;
input.Count = 4;
input.Pointer = params;
params[0].Type = ACPI_TYPE_BUFFER;
params[0].Buffer.Length = sizeof(intel_dsm_guid);
params[0].Buffer.Pointer = __DECONST(char *, intel_dsm_guid);
params[1].Type = ACPI_TYPE_INTEGER;
params[1].Integer.Value = INTEL_DSM_REVISION_ID;
params[2].Type = ACPI_TYPE_INTEGER;
params[2].Integer.Value = func;
params[3].Type = ACPI_TYPE_INTEGER;
params[3].Integer.Value = arg;
ret = AcpiEvaluateObject(handle, "_DSM", &input, &output);
if (ret) {
DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
return ret;
}
obj = (ACPI_OBJECT *)output.Pointer;
result = 0;
switch (obj->Type) {
case ACPI_TYPE_INTEGER:
result = obj->Integer.Value;
break;
case ACPI_TYPE_BUFFER:
if (obj->Buffer.Length == 4) {
result = (obj->Buffer.Pointer[0] |
(obj->Buffer.Pointer[1] << 8) |
(obj->Buffer.Pointer[2] << 16) |
(obj->Buffer.Pointer[3] << 24));
break;
}
default:
ret = -EINVAL;
break;
}
if (result == 0x80000002)
ret = -ENODEV;
AcpiOsFree(output.Pointer);
return ret;
}
static char *intel_dsm_port_name(u8 id)
{
switch (id) {
case 0:
return "Reserved";
case 1:
return "Analog VGA";
case 2:
return "LVDS";
case 3:
return "Reserved";
case 4:
return "HDMI/DVI_B";
case 5:
return "HDMI/DVI_C";
case 6:
return "HDMI/DVI_D";
case 7:
return "DisplayPort_A";
case 8:
return "DisplayPort_B";
case 9:
return "DisplayPort_C";
case 0xa:
return "DisplayPort_D";
case 0xb:
case 0xc:
case 0xd:
return "Reserved";
case 0xe:
return "WiDi";
default:
return "bad type";
}
}
static char *intel_dsm_mux_type(u8 type)
{
switch (type) {
case 0:
return "unknown";
case 1:
return "No MUX, iGPU only";
case 2:
return "No MUX, dGPU only";
case 3:
return "MUXed between iGPU and dGPU";
default:
return "bad type";
}
}
static void intel_dsm_platform_mux_info(void)
{
ACPI_BUFFER output = { ACPI_ALLOCATE_BUFFER, NULL };
ACPI_OBJECT_LIST input;
ACPI_OBJECT params[4];
ACPI_OBJECT *pkg;
int i, ret;
input.Count = 4;
input.Pointer = params;
params[0].Type = ACPI_TYPE_BUFFER;
params[0].Buffer.Length = sizeof(intel_dsm_guid);
params[0].Buffer.Pointer = __DECONST(char *, intel_dsm_guid);
params[1].Type = ACPI_TYPE_INTEGER;
params[1].Integer.Value = INTEL_DSM_REVISION_ID;
params[2].Type = ACPI_TYPE_INTEGER;
params[2].Integer.Value = INTEL_DSM_FN_PLATFORM_MUX_INFO;
params[3].Type = ACPI_TYPE_INTEGER;
params[3].Integer.Value = 0;
ret = AcpiEvaluateObject(intel_dsm_priv.dhandle, "_DSM", &input,
&output);
if (ret) {
DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
goto out;
}
pkg = (ACPI_OBJECT *)output.Pointer;
if (pkg->Type == ACPI_TYPE_PACKAGE) {
ACPI_OBJECT *connector_count = &pkg->Package.Elements[0];
DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
(unsigned long long)connector_count->Integer.Value);
for (i = 1; i < pkg->Package.Count; i++) {
ACPI_OBJECT *obj = &pkg->Package.Elements[i];
ACPI_OBJECT *connector_id =
&obj->Package.Elements[0];
ACPI_OBJECT *info = &obj->Package.Elements[1];
DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
(unsigned long long)connector_id->Integer.Value);
DRM_DEBUG_DRIVER(" port id: %s\n",
intel_dsm_port_name(info->Buffer.Pointer[0]));
DRM_DEBUG_DRIVER(" display mux info: %s\n",
intel_dsm_mux_type(info->Buffer.Pointer[1]));
DRM_DEBUG_DRIVER(" aux/dc mux info: %s\n",
intel_dsm_mux_type(info->Buffer.Pointer[2]));
DRM_DEBUG_DRIVER(" hpd mux info: %s\n",
intel_dsm_mux_type(info->Buffer.Pointer[3]));
}
}
out:
AcpiOsFree(output.Pointer);
}
static bool intel_dsm_pci_probe(device_t dev)
{
ACPI_HANDLE dhandle, intel_handle;
ACPI_STATUS status;
int ret;
dhandle = acpi_get_handle(dev);
if (!dhandle)
return false;
status = AcpiGetHandle(dhandle, "_DSM", &intel_handle);
if (ACPI_FAILURE(status)) {
DRM_DEBUG_KMS("no _DSM method for intel device\n");
return false;
}
ret = intel_dsm(dhandle, INTEL_DSM_FN_SUPPORTED_FUNCTIONS, 0);
if (ret < 0) {
DRM_DEBUG_KMS("failed to get supported _DSM functions\n");
return false;
}
intel_dsm_priv.dhandle = dhandle;
intel_dsm_platform_mux_info();
return true;
}
static bool intel_dsm_detect(void)
{
char acpi_method_name[255] = { 0 };
ACPI_BUFFER buffer = {sizeof(acpi_method_name), acpi_method_name};
device_t dev = NULL;
bool has_dsm = false;
int vga_count = 0;
#ifdef FREEBSD_WIP
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
#endif /* FREEBSD_WIP */
if ((dev = pci_find_class(PCIC_DISPLAY, PCIS_DISPLAY_VGA)) != NULL) {
vga_count++;
has_dsm |= intel_dsm_pci_probe(dev);
}
if (vga_count == 2 && has_dsm) {
AcpiGetName(intel_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n",
acpi_method_name);
return true;
}
return false;
}
void intel_register_dsm_handler(void)
{
if (!intel_dsm_detect())
return;
}
void intel_unregister_dsm_handler(void)
{
}

View File

@ -23,10 +23,12 @@
* Authors:
* Eric Anholt <eric@anholt.net>
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_dp_helper.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
@ -169,8 +171,7 @@ get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data,
int dvo_timing_offset =
lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
const char *entry = (const char *)lvds_lfp_data->data +
lfp_data_size * index;
const char *entry = (const char *)lvds_lfp_data->data + lfp_data_size * index;
return (const struct lvds_dvo_timing *)(entry + dvo_timing_offset);
}
@ -188,7 +189,7 @@ get_lvds_fp_timing(const struct bdb_header *bdb,
u16 data_size = ((const u16 *)data)[-1]; /* stored in header */
size_t ofs;
if (index >= DRM_ARRAY_SIZE(ptrs->ptr))
if (index >= ARRAY_SIZE(ptrs->ptr))
return NULL;
ofs = ptrs->ptr[index].fp_timing_offset;
if (ofs < data_ofs ||
@ -234,8 +235,9 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
lvds_lfp_data_ptrs,
lvds_options->panel_type);
panel_fixed_mode = malloc(sizeof(*panel_fixed_mode), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
panel_fixed_mode = malloc(sizeof(*panel_fixed_mode), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!panel_fixed_mode)
return;
fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing);
@ -263,9 +265,9 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) {
dev_priv->lvds_downclock_avail = 1;
dev_priv->lvds_downclock = downclock * 10;
DRM_DEBUG("LVDS downclock is found in VBT. "
DRM_DEBUG_KMS("LVDS downclock is found in VBT. "
"Normal Clock %dKHz, downclock %dKHz\n",
panel_fixed_mode->clock, 10 * downclock);
panel_fixed_mode->clock, 10*downclock);
}
fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data,
@ -311,8 +313,9 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv,
if (!dvo_timing)
return;
panel_fixed_mode = malloc(sizeof(*panel_fixed_mode), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
panel_fixed_mode = malloc(sizeof(*panel_fixed_mode), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!panel_fixed_mode)
return;
fill_detail_timing_data(panel_fixed_mode, dvo_timing + index);
@ -351,12 +354,14 @@ parse_general_features(struct drm_i915_private *dev_priv,
dev_priv->lvds_ssc_freq =
intel_bios_ssc_frequency(dev, general->ssc_freq);
dev_priv->display_clock_mode = general->display_clock_mode;
DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d\n",
dev_priv->fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted;
DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d fdi_rx_polarity_inverted %d\n",
dev_priv->int_tv_support,
dev_priv->int_crt_support,
dev_priv->lvds_use_ssc,
dev_priv->lvds_ssc_freq,
dev_priv->display_clock_mode);
dev_priv->display_clock_mode,
dev_priv->fdi_rx_polarity_inverted);
}
}
@ -499,12 +504,8 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
edp = find_section(bdb, BDB_EDP);
if (!edp) {
if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp.support) {
DRM_DEBUG_KMS("No eDP BDB found but eDP panel "
"supported, assume %dbpp panel color "
"depth.\n",
dev_priv->edp.bpp);
}
if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp.support)
DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported.\n");
return;
}
@ -613,8 +614,11 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
DRM_DEBUG_KMS("no child dev is parsed from VBT\n");
return;
}
dev_priv->child_dev = malloc(sizeof(*p_child) * count, DRM_MEM_KMS,
M_WAITOK | M_ZERO);
dev_priv->child_dev = malloc(count * sizeof(*p_child), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!dev_priv->child_dev) {
DRM_DEBUG_KMS("No memory space for child device\n");
return;
}
dev_priv->child_dev_num = count;
count = 0;
@ -654,12 +658,9 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
dev_priv->lvds_use_ssc = 1;
dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq);
/* eDP data */
dev_priv->edp.bpp = 18;
}
static int intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
{
DRM_DEBUG_KMS("Falling back to manually reading VBT from "
"VBIOS ROM for %s\n",
@ -688,12 +689,13 @@ static const struct dmi_system_id intel_no_opregion_vbt[] = {
*
* Returns 0 on success, nonzero on failure.
*/
bool
int
intel_parse_bios(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
device_t vga_dev = device_get_parent(dev->dev);;
struct bdb_header *bdb = NULL;
u8 *bios;
u8 __iomem *bios = NULL;
init_vbt_defaults(dev_priv);
@ -707,20 +709,13 @@ intel_parse_bios(struct drm_device *dev)
} else
dev_priv->opregion.vbt = NULL;
}
bios = NULL;
#if 1
if (bdb == NULL) {
KIB_NOTYET();
return (-1);
}
#else
if (bdb == NULL) {
struct vbt_header *vbt = NULL;
size_t size;
int i;
bios = pci_map_rom(pdev, &size);
bios = vga_pci_map_bios(vga_dev, &size);
if (!bios)
return -1;
@ -734,13 +729,12 @@ intel_parse_bios(struct drm_device *dev)
if (!vbt) {
DRM_DEBUG_DRIVER("VBT signature missing\n");
pci_unmap_rom(pdev, bios);
vga_pci_unmap_bios(vga_dev, bios);
return -1;
}
bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
}
#endif
/* Grab useful general definitions */
parse_general_features(dev_priv, bdb);
@ -752,14 +746,31 @@ intel_parse_bios(struct drm_device *dev)
parse_driver_features(dev_priv, bdb);
parse_edp(dev_priv, bdb);
#if 0
if (bios)
pci_unmap_rom(pdev, bios);
#endif
vga_pci_unmap_bios(vga_dev, bios);
return 0;
}
/*
* NOTE Linux<->FreeBSD:
* Apparently, Linux doesn't free those pointers.
* TODO: Report that upstream.
*/
void
intel_free_parsed_bios_data(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
free(dev_priv->lfp_lvds_vbt_mode, DRM_MEM_KMS);
free(dev_priv->sdvo_lvds_vbt_mode, DRM_MEM_KMS);
free(dev_priv->child_dev, DRM_MEM_KMS);
dev_priv->lfp_lvds_vbt_mode = NULL;
dev_priv->sdvo_lvds_vbt_mode = NULL;
dev_priv->child_dev = NULL;
}
/* Ensure that vital registers have been initialised, even if the BIOS
* is absent or just failing to do its job.
*/
@ -768,7 +779,8 @@ void intel_setup_bios(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
/* Set the Panel Power On/Off timings if uninitialized. */
if ((I915_READ(PP_ON_DELAYS) == 0) && (I915_READ(PP_OFF_DELAYS) == 0)) {
if (!HAS_PCH_SPLIT(dev) &&
I915_READ(PP_ON_DELAYS) == 0 && I915_READ(PP_OFF_DELAYS) == 0) {
/* Set T2 to 40ms and T5 to 200ms */
I915_WRITE(PP_ON_DELAYS, 0x019007d0);

View File

@ -128,7 +128,9 @@ struct bdb_general_features {
/* bits 3 */
u8 disable_smooth_vision:1;
u8 single_dvi:1;
u8 rsvd9:6; /* finish byte */
u8 rsvd9:1;
u8 fdi_rx_polarity_inverted:1;
u8 rsvd10:4; /* finish byte */
/* bits 4 */
u8 legacy_monitor_detect;
@ -477,7 +479,8 @@ struct bdb_edp {
} __attribute__ ((packed));
void intel_setup_bios(struct drm_device *dev);
bool intel_parse_bios(struct drm_device *dev);
int intel_parse_bios(struct drm_device *dev);
void intel_free_parsed_bios_data(struct drm_device *dev);
/*
* Driver<->VBIOS interaction occurs through scratch bits in

View File

@ -28,13 +28,12 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_crtc_helper.h>
#include <dev/drm2/drm_edid.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
/* Here's the desired hotplug mode */
#define ADPA_HOTPLUG_BITS (ADPA_CRT_HOTPLUG_PERIOD_128 | \
@ -46,7 +45,11 @@ __FBSDID("$FreeBSD$");
struct intel_crt {
struct intel_encoder base;
/* DPMS state is stored in the connector, which we need in the
* encoder's enable/disable callbacks */
struct intel_connector *connector;
bool force_hotplug_required;
u32 adpa_reg;
};
static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
@ -55,36 +58,42 @@ static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
struct intel_crt, base);
}
static void pch_crt_dpms(struct drm_encoder *encoder, int mode)
static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 temp;
temp = I915_READ(PCH_ADPA);
temp &= ~ADPA_DAC_ENABLE;
switch (mode) {
case DRM_MODE_DPMS_ON:
temp |= ADPA_DAC_ENABLE;
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
/* Just leave port enable cleared */
break;
}
I915_WRITE(PCH_ADPA, temp);
return container_of(encoder, struct intel_crt, base);
}
static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_device *dev = encoder->dev;
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_encoder_to_crt(encoder);
u32 tmp;
tmp = I915_READ(crt->adpa_reg);
if (!(tmp & ADPA_DAC_ENABLE))
return false;
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
return true;
}
/* Note: The caller is required to filter out dpms modes not supported by the
* platform. */
static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_encoder_to_crt(encoder);
u32 temp;
temp = I915_READ(ADPA);
temp = I915_READ(crt->adpa_reg);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE;
@ -103,7 +112,64 @@ static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
break;
}
I915_WRITE(ADPA, temp);
I915_WRITE(crt->adpa_reg, temp);
}
static void intel_disable_crt(struct intel_encoder *encoder)
{
intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF);
}
static void intel_enable_crt(struct intel_encoder *encoder)
{
struct intel_crt *crt = intel_encoder_to_crt(encoder);
intel_crt_set_dpms(encoder, crt->connector->base.dpms);
}
static void intel_crt_dpms(struct drm_connector *connector, int mode)
{
struct drm_device *dev = connector->dev;
struct intel_encoder *encoder = intel_attached_encoder(connector);
struct drm_crtc *crtc;
int old_dpms;
/* PCH platforms and VLV only support on/off. */
if (INTEL_INFO(dev)->gen >= 5 && mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (mode == connector->dpms)
return;
old_dpms = connector->dpms;
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
crtc = encoder->base.crtc;
if (!crtc) {
encoder->connectors_active = false;
return;
}
/* We need the pipe to run for anything but OFF. */
if (mode == DRM_MODE_DPMS_OFF)
encoder->connectors_active = false;
else
encoder->connectors_active = true;
if (mode < old_dpms) {
/* From off to on, enable the pipe first. */
intel_crtc_update_dpms(crtc);
intel_crt_set_dpms(encoder, mode);
} else {
intel_crt_set_dpms(encoder, mode);
intel_crtc_update_dpms(crtc);
}
intel_modeset_check_state(connector->dev);
}
static int intel_crt_mode_valid(struct drm_connector *connector,
@ -125,6 +191,11 @@ static int intel_crt_mode_valid(struct drm_connector *connector,
if (mode->clock > max_clock)
return MODE_CLOCK_HIGH;
/* The FDI receiver on LPT only supports 8bpc and only has 2 lanes. */
if (HAS_PCH_LPT(dev) &&
(ironlake_get_lanes_required(mode->clock, 270000, 24) > 2))
return MODE_CLOCK_HIGH;
return MODE_OK;
}
@ -142,37 +213,26 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev;
struct drm_crtc *crtc = encoder->crtc;
struct intel_crt *crt =
intel_encoder_to_crt(to_intel_encoder(encoder));
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_i915_private *dev_priv = dev->dev_private;
int dpll_md_reg;
u32 adpa, dpll_md;
u32 adpa_reg;
dpll_md_reg = DPLL_MD(intel_crtc->pipe);
u32 adpa;
if (HAS_PCH_SPLIT(dev))
adpa_reg = PCH_ADPA;
adpa = ADPA_HOTPLUG_BITS;
else
adpa_reg = ADPA;
adpa = 0;
/*
* Disable separate mode multiplier used when cloning SDVO to CRT
* XXX this needs to be adjusted when we really are cloning
*/
if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
dpll_md = I915_READ(dpll_md_reg);
I915_WRITE(dpll_md_reg,
dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
}
adpa = ADPA_HOTPLUG_BITS;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
adpa |= ADPA_HSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
adpa |= ADPA_VSYNC_ACTIVE_HIGH;
/* For CPT allow 3 pipe config, for others just use A or B */
if (HAS_PCH_CPT(dev))
if (HAS_PCH_LPT(dev))
; /* Those bits don't exist here */
else if (HAS_PCH_CPT(dev))
adpa |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
else if (intel_crtc->pipe == 0)
adpa |= ADPA_PIPE_A_SELECT;
@ -182,7 +242,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
if (!HAS_PCH_SPLIT(dev))
I915_WRITE(BCLRPAT(intel_crtc->pipe), 0);
I915_WRITE(adpa_reg, adpa);
I915_WRITE(crt->adpa_reg, adpa);
}
static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
@ -209,10 +269,9 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
I915_WRITE(PCH_ADPA, adpa);
if (_intel_wait_for(dev,
(I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
1000, 1, "915crt"))
DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER\n");
if (wait_for((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
1000))
DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER");
if (turn_off_dac) {
I915_WRITE(PCH_ADPA, save_adpa);
@ -231,6 +290,42 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
return ret;
}
static bool valleyview_crt_detect_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 adpa;
bool ret;
u32 save_adpa;
save_adpa = adpa = I915_READ(ADPA);
DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa);
adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER;
I915_WRITE(ADPA, adpa);
if (wait_for((I915_READ(ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
1000)) {
DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER");
I915_WRITE(ADPA, save_adpa);
}
/* Check the status to see if both blue and green are on now */
adpa = I915_READ(ADPA);
if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0)
ret = true;
else
ret = false;
DRM_DEBUG_KMS("valleyview hotplug adpa=0x%x, result %d\n", adpa, ret);
/* FIXME: debug force function and remove */
ret = true;
return ret;
}
/**
* Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
*
@ -250,6 +345,9 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
if (HAS_PCH_SPLIT(dev))
return intel_ironlake_crt_detect_hotplug(connector);
if (IS_VALLEYVIEW(dev))
return valleyview_crt_detect_hotplug(connector);
/*
* On 4 series desktop, CRT detect sequence need to be done twice
* to get a reliable result.
@ -266,9 +364,9 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
/* turn on the FORCE_DETECT */
I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
/* wait for FORCE_DETECT to go off */
if (_intel_wait_for(dev,
(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT) == 0,
1000, 1, "915cr2"))
if (wait_for((I915_READ(PORT_HOTPLUG_EN) &
CRT_HOTPLUG_FORCE_DETECT) == 0,
1000))
DRM_DEBUG_KMS("timed out waiting for FORCE_DETECT to go off");
}
@ -285,42 +383,72 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
return ret;
}
static struct edid *intel_crt_get_edid(struct drm_connector *connector,
device_t i2c)
{
struct edid *edid;
edid = drm_get_edid(connector, i2c);
if (!edid && !intel_gmbus_is_forced_bit(i2c)) {
DRM_DEBUG_KMS("CRT GMBUS EDID read failed, retry using GPIO bit-banging\n");
intel_gmbus_force_bit(i2c, true);
edid = drm_get_edid(connector, i2c);
intel_gmbus_force_bit(i2c, false);
}
return edid;
}
/* local version of intel_ddc_get_modes() to use intel_crt_get_edid() */
static int intel_crt_ddc_get_modes(struct drm_connector *connector,
device_t adapter)
{
struct edid *edid;
int ret;
edid = intel_crt_get_edid(connector, adapter);
if (!edid)
return 0;
ret = intel_connector_update_modes(connector, edid);
free(edid, DRM_MEM_KMS);
return ret;
}
static bool intel_crt_detect_ddc(struct drm_connector *connector)
{
struct intel_crt *crt = intel_attached_crt(connector);
struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private;
struct edid *edid;
device_t i2c;
/* CRT should always be at 0, but check anyway */
if (crt->base.type != INTEL_OUTPUT_ANALOG)
return false;
BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG);
if (intel_ddc_probe(&crt->base, dev_priv->crt_ddc_pin)) {
struct edid *edid;
bool is_digital = false;
device_t iic;
i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
edid = intel_crt_get_edid(connector, i2c);
if (edid) {
bool is_digital = edid->input & DRM_EDID_INPUT_DIGITAL;
iic = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
edid = drm_get_edid(connector, iic);
/*
* This may be a DVI-I connector with a shared DDC
* link between analog and digital outputs, so we
* have to check the EDID input spec of the attached device.
*
* On the other hand, what should we do if it is a broken EDID?
*/
if (edid != NULL) {
is_digital = edid->input & DRM_EDID_INPUT_DIGITAL;
free(edid, DRM_MEM_KMS);
}
if (!is_digital) {
DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
return true;
} else {
DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
}
DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
} else {
DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n");
}
free(edid, DRM_MEM_KMS);
return false;
}
@ -453,30 +581,37 @@ intel_crt_detect(struct drm_connector *connector, bool force)
struct intel_load_detect_pipe tmp;
if (I915_HAS_HOTPLUG(dev)) {
/* We can not rely on the HPD pin always being correctly wired
* up, for example many KVM do not pass it through, and so
* only trust an assertion that the monitor is connected.
*/
if (intel_crt_detect_hotplug(connector)) {
DRM_DEBUG_KMS("CRT detected via hotplug\n");
return connector_status_connected;
} else {
} else
DRM_DEBUG_KMS("CRT not detected via hotplug\n");
return connector_status_disconnected;
}
}
if (intel_crt_detect_ddc(connector))
return connector_status_connected;
/* Load detection is broken on HPD capable machines. Whoever wants a
* broken monitor (without edid) to work behind a broken kvm (that fails
* to have the right resistors for HP detection) needs to fix this up.
* For now just bail out. */
if (I915_HAS_HOTPLUG(dev))
return connector_status_disconnected;
if (!force)
return connector->status;
/* for pre-945g platforms use load detect */
if (intel_get_load_detect_pipe(&crt->base, connector, NULL,
&tmp)) {
if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
if (intel_crt_detect_ddc(connector))
status = connector_status_connected;
else
status = intel_crt_load_detect(crt);
intel_release_load_detect_pipe(&crt->base, connector,
&tmp);
intel_release_load_detect_pipe(connector, &tmp);
} else
status = connector_status_unknown;
@ -485,10 +620,6 @@ intel_crt_detect(struct drm_connector *connector, bool force)
static void intel_crt_destroy(struct drm_connector *connector)
{
#if 0
drm_sysfs_connector_remove(connector);
#endif
drm_connector_cleanup(connector);
free(connector, DRM_MEM_KMS);
}
@ -501,13 +632,13 @@ static int intel_crt_get_modes(struct drm_connector *connector)
device_t i2c;
i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
ret = intel_ddc_get_modes(connector, i2c);
ret = intel_crt_ddc_get_modes(connector, i2c);
if (ret || !IS_G4X(dev))
return ret;
/* Try to probe digital port for output in DVI-I -> VGA mode. */
i2c = intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPB);
return intel_ddc_get_modes(connector, i2c);
return intel_crt_ddc_get_modes(connector, i2c);
}
static int intel_crt_set_property(struct drm_connector *connector,
@ -520,36 +651,37 @@ static int intel_crt_set_property(struct drm_connector *connector,
static void intel_crt_reset(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_attached_crt(connector);
if (HAS_PCH_SPLIT(dev)) {
u32 adpa;
adpa = I915_READ(PCH_ADPA);
adpa &= ~ADPA_CRT_HOTPLUG_MASK;
adpa |= ADPA_HOTPLUG_BITS;
I915_WRITE(PCH_ADPA, adpa);
POSTING_READ(PCH_ADPA);
DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa);
crt->force_hotplug_required = 1;
}
}
/*
* Routines for controlling stuff on the analog port
*/
static const struct drm_encoder_helper_funcs pch_encoder_funcs = {
static const struct drm_encoder_helper_funcs crt_encoder_funcs = {
.mode_fixup = intel_crt_mode_fixup,
.prepare = intel_encoder_prepare,
.commit = intel_encoder_commit,
.mode_set = intel_crt_mode_set,
.dpms = pch_crt_dpms,
};
static const struct drm_encoder_helper_funcs gmch_encoder_funcs = {
.mode_fixup = intel_crt_mode_fixup,
.prepare = intel_encoder_prepare,
.commit = intel_encoder_commit,
.mode_set = intel_crt_mode_set,
.dpms = gmch_crt_dpms,
.disable = intel_encoder_noop,
};
static const struct drm_connector_funcs intel_crt_connector_funcs = {
.reset = intel_crt_reset,
.dpms = drm_helper_connector_dpms,
.dpms = intel_crt_dpms,
.detect = intel_crt_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = intel_crt_destroy,
@ -566,7 +698,7 @@ static const struct drm_encoder_funcs intel_crt_enc_funcs = {
.destroy = intel_encoder_destroy,
};
static int intel_no_crt_dmi_callback(const struct dmi_system_id *id)
static int __init intel_no_crt_dmi_callback(const struct dmi_system_id *id)
{
DRM_INFO("Skipping CRT initialization for %s\n", id->ident);
return 1;
@ -590,17 +722,23 @@ void intel_crt_init(struct drm_device *dev)
struct intel_crt *crt;
struct intel_connector *intel_connector;
struct drm_i915_private *dev_priv = dev->dev_private;
const struct drm_encoder_helper_funcs *encoder_helper_funcs;
/* Skip machines without VGA that falsely report hotplug events */
if (dmi_check_system(intel_no_crt))
return;
crt = malloc(sizeof(struct intel_crt), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!crt)
return;
intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_connector) {
free(crt, DRM_MEM_KMS);
return;
}
connector = &intel_connector->base;
crt->connector = intel_connector;
drm_connector_init(dev, &intel_connector->base,
&intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
@ -610,13 +748,11 @@ void intel_crt_init(struct drm_device *dev)
intel_connector_attach_encoder(intel_connector, &crt->base);
crt->base.type = INTEL_OUTPUT_ANALOG;
crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT |
1 << INTEL_ANALOG_CLONE_BIT |
1 << INTEL_SDVO_LVDS_CLONE_BIT);
if (IS_HASWELL(dev))
crt->base.cloneable = true;
if (IS_I830(dev))
crt->base.crtc_mask = (1 << 0);
else
crt->base.crtc_mask = (1 << 0) | (1 << 1);
crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
if (IS_GEN2(dev))
connector->interlace_allowed = 0;
@ -625,17 +761,23 @@ void intel_crt_init(struct drm_device *dev)
connector->doublescan_allowed = 0;
if (HAS_PCH_SPLIT(dev))
encoder_helper_funcs = &pch_encoder_funcs;
crt->adpa_reg = PCH_ADPA;
else if (IS_VALLEYVIEW(dev))
crt->adpa_reg = VLV_ADPA;
else
encoder_helper_funcs = &gmch_encoder_funcs;
crt->adpa_reg = ADPA;
drm_encoder_helper_add(&crt->base.base, encoder_helper_funcs);
crt->base.disable = intel_disable_crt;
crt->base.enable = intel_enable_crt;
if (IS_HASWELL(dev))
crt->base.get_hw_state = intel_ddi_get_hw_state;
else
crt->base.get_hw_state = intel_crt_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
drm_encoder_helper_add(&crt->base.base, &crt_encoder_funcs);
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
#if 0
drm_sysfs_connector_add(connector);
#endif
if (I915_HAS_HOTPLUG(dev))
connector->polled = DRM_CONNECTOR_POLL_HPD;
else
@ -645,18 +787,18 @@ void intel_crt_init(struct drm_device *dev)
* Configure the automatic hotplug detection stuff
*/
crt->force_hotplug_required = 0;
if (HAS_PCH_SPLIT(dev)) {
u32 adpa;
adpa = I915_READ(PCH_ADPA);
adpa &= ~ADPA_CRT_HOTPLUG_MASK;
adpa |= ADPA_HOTPLUG_BITS;
I915_WRITE(PCH_ADPA, adpa);
POSTING_READ(PCH_ADPA);
DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa);
crt->force_hotplug_required = 1;
}
dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS;
/*
* TODO: find a proper way to discover whether we need to set the the
* polarity and link reversal bits or not, instead of relying on the
* BIOS.
*/
if (HAS_PCH_LPT(dev)) {
u32 fdi_config = FDI_RX_POLARITY_REVERSED_LPT |
FDI_RX_LINK_REVERSAL_OVERRIDE;
dev_priv->fdi_rx_config = I915_READ(_FDI_RXA_CTL) & fdi_config;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,15 +24,15 @@
*
* $FreeBSD$
*/
#ifndef DRM_INTEL_DRV_H
#define DRM_INTEL_DRV_H
#ifndef __INTEL_DRV_H__
#define __INTEL_DRV_H__
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_crtc_helper.h>
#include <dev/drm2/drm_fb_helper.h>
#include <dev/drm2/drm_dp_helper.h>
#define _intel_wait_for(DEV, COND, MS, W, WMSG) \
({ \
@ -55,6 +55,23 @@
ret; \
})
#define _wait_for(COND, MS, W, WMSG) ({ \
int timeout__ = ticks + (MS) * hz / 1000; \
int ret__ = 0; \
while (!(COND)) { \
if (time_after(ticks, timeout__)) { \
ret__ = -ETIMEDOUT; \
break; \
} \
if (W) { \
pause((WMSG), 1); \
} else { \
DELAY(1000); \
} \
} \
ret__; \
})
#define wait_for_atomic_us(COND, US) ({ \
int i, ret__ = -ETIMEDOUT; \
for (i = 0; i < (US); i++) { \
@ -67,8 +84,8 @@
ret__; \
})
#define wait_for(COND, MS) _intel_wait_for(NULL, COND, MS, 1, "915wfi")
#define wait_for_atomic(COND, MS) _intel_wait_for(NULL, COND, MS, 0, "915wfa")
#define wait_for(COND, MS) _intel_wait_for(NULL, COND, MS, 1, "915wfi")
#define wait_for_atomic(COND, MS) _intel_wait_for(NULL, COND, MS, 0, "915wfa")
#define KHz(x) (1000*x)
#define MHz(x) KHz(1000*x)
@ -100,25 +117,6 @@
#define INTEL_OUTPUT_EDP 8
#define INTEL_OUTPUT_UNKNOWN 9
/* Intel Pipe Clone Bit */
#define INTEL_HDMIB_CLONE_BIT 1
#define INTEL_HDMIC_CLONE_BIT 2
#define INTEL_HDMID_CLONE_BIT 3
#define INTEL_HDMIE_CLONE_BIT 4
#define INTEL_HDMIF_CLONE_BIT 5
#define INTEL_SDVO_NON_TV_CLONE_BIT 6
#define INTEL_SDVO_TV_CLONE_BIT 7
#define INTEL_SDVO_LVDS_CLONE_BIT 8
#define INTEL_ANALOG_CLONE_BIT 9
#define INTEL_TV_CLONE_BIT 10
#define INTEL_DP_B_CLONE_BIT 11
#define INTEL_DP_C_CLONE_BIT 12
#define INTEL_DP_D_CLONE_BIT 13
#define INTEL_LVDS_CLONE_BIT 14
#define INTEL_DVO_TMDS_CLONE_BIT 15
#define INTEL_DVO_LVDS_CLONE_BIT 16
#define INTEL_EDP_CLONE_BIT 17
#define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1
#define INTEL_DVO_CHIP_TMDS 2
@ -161,32 +159,87 @@ struct intel_fbdev {
struct intel_encoder {
struct drm_encoder base;
/*
* The new crtc this encoder will be driven from. Only differs from
* base->crtc while a modeset is in progress.
*/
struct intel_crtc *new_crtc;
int type;
bool needs_tv_clock;
/*
* Intel hw has only one MUX where encoders could be clone, hence a
* simple flag is enough to compute the possible_clones mask.
*/
bool cloneable;
bool connectors_active;
void (*hot_plug)(struct intel_encoder *);
void (*pre_enable)(struct intel_encoder *);
void (*enable)(struct intel_encoder *);
void (*disable)(struct intel_encoder *);
void (*post_disable)(struct intel_encoder *);
/* Read out the current hw state of this connector, returning true if
* the encoder is active. If the encoder is enabled it also set the pipe
* it is connected to in the pipe parameter. */
bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe);
int crtc_mask;
int clone_mask;
};
struct intel_panel {
struct drm_display_mode *fixed_mode;
int fitting_mode;
};
struct intel_connector {
struct drm_connector base;
/*
* The fixed encoder this connector is connected to.
*/
struct intel_encoder *encoder;
/*
* The new encoder this connector will be driven. Only differs from
* encoder while a modeset is in progress.
*/
struct intel_encoder *new_encoder;
/* Reads out the current hw, returning true if the connector is enabled
* and active (i.e. dpms ON state). */
bool (*get_hw_state)(struct intel_connector *);
/* Panel info for eDP and LVDS */
struct intel_panel panel;
/* Cached EDID for eDP and LVDS. May hold ERR_PTR for invalid EDID. */
struct edid *edid;
int edid_err;
};
struct intel_crtc {
struct drm_crtc base;
enum pipe pipe;
enum plane plane;
enum transcoder cpu_transcoder;
u8 lut_r[256], lut_g[256], lut_b[256];
int dpms_mode;
bool active; /* is the crtc on? independent of the dpms mode */
bool busy; /* is scanout buffer being updated frequently? */
struct callout idle_callout;
/*
* Whether the crtc and the connected output pipeline is active. Implies
* that crtc->enabled is set, i.e. the current mode configuration has
* some outputs connected to this crtc.
*/
bool active;
bool primary_disabled; /* is the crtc obscured by a plane? */
bool lowfreq_avail;
struct intel_overlay *overlay;
struct intel_unpin_work *unpin_work;
int fdi_lanes;
atomic_t unpin_work_count;
/* Display surface base address adjustement for pageflips. Note that on
* gen4+ this only adjusts up to a tile, offsets within a tile are
* handled in the hw itself (with the TILEOFF register). */
unsigned long dspaddr_offset;
struct drm_i915_gem_object *cursor_bo;
uint32_t cursor_addr;
int16_t cursor_x, cursor_y;
@ -196,13 +249,14 @@ struct intel_crtc {
/* We can share PLLs across outputs if the timings match */
struct intel_pch_pll *pch_pll;
uint32_t ddi_pll_sel;
};
struct intel_plane {
struct drm_plane base;
enum pipe pipe;
struct drm_i915_gem_object *obj;
bool primary_disabled;
bool can_scale;
int max_downscale;
u32 lut_r[1024], lut_g[1024], lut_b[1024];
void (*update_plane)(struct drm_plane *plane,
@ -302,16 +356,52 @@ struct dip_infoframe {
} __attribute__((packed));
struct intel_hdmi {
struct intel_encoder base;
u32 sdvox_reg;
int ddc_bus;
int ddi_port;
uint32_t color_range;
bool has_hdmi_sink;
bool has_audio;
enum hdmi_force_audio force_audio;
void (*write_infoframe)(struct drm_encoder *encoder,
struct dip_infoframe *frame);
void (*set_infoframes)(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode);
};
#define DP_MAX_DOWNSTREAM_PORTS 0x10
#define DP_LINK_CONFIGURATION_SIZE 9
struct intel_dp {
uint32_t output_reg;
uint32_t DP;
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
bool has_audio;
enum hdmi_force_audio force_audio;
uint32_t color_range;
uint8_t link_bw;
uint8_t lane_count;
uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
device_t dp_iic_bus;
device_t adapter;
bool is_pch_edp;
uint8_t train_set[4];
int panel_power_up_delay;
int panel_power_down_delay;
int panel_power_cycle_delay;
int backlight_on_delay;
int backlight_off_delay;
struct timeout_task panel_vdd_work;
bool want_panel_vdd;
struct intel_connector *attached_connector;
};
struct intel_digital_port {
struct intel_encoder base;
enum port port;
u32 port_reversal;
struct intel_dp dp;
struct intel_hdmi hdmi;
};
static inline struct drm_crtc *
@ -329,56 +419,88 @@ intel_get_crtc_for_plane(struct drm_device *dev, int plane)
}
struct intel_unpin_work {
struct task task;
struct drm_device *dev;
struct task work;
struct drm_crtc *crtc;
struct drm_i915_gem_object *old_fb_obj;
struct drm_i915_gem_object *pending_flip_obj;
struct drm_pending_vblank_event *event;
int pending;
atomic_t pending;
#define INTEL_FLIP_INACTIVE 0
#define INTEL_FLIP_PENDING 1
#define INTEL_FLIP_COMPLETE 2
bool enable_stall_check;
};
struct intel_fbc_work {
struct timeout_task task;
struct timeout_task work;
struct drm_crtc *crtc;
struct drm_framebuffer *fb;
int interval;
};
int intel_pch_rawclk(struct drm_device *dev);
int intel_connector_update_modes(struct drm_connector *connector,
struct edid *edid);
int intel_ddc_get_modes(struct drm_connector *c, device_t adapter);
extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus);
extern void intel_attach_force_audio_property(struct drm_connector *connector);
extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
extern void intel_crt_init(struct drm_device *dev);
extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
extern void intel_hdmi_init(struct drm_device *dev,
int sdvox_reg, enum port port);
extern void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
struct intel_connector *intel_connector);
extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder);
extern void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode);
extern void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder);
extern bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if);
extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg,
bool is_sdvob);
extern void intel_dvo_init(struct drm_device *dev);
extern void intel_tv_init(struct drm_device *dev);
extern void intel_mark_busy(struct drm_device *dev,
struct drm_i915_gem_object *obj);
extern void intel_mark_busy(struct drm_device *dev);
extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj);
extern void intel_mark_idle(struct drm_device *dev);
extern bool intel_lvds_init(struct drm_device *dev);
extern void intel_dp_init(struct drm_device *dev, int dp_reg);
extern void intel_dp_init(struct drm_device *dev, int output_reg,
enum port port);
extern void intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
struct intel_connector *intel_connector);
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern void intel_dp_init_link_config(struct intel_dp *intel_dp);
extern void intel_dp_start_link_train(struct intel_dp *intel_dp);
extern void intel_dp_complete_link_train(struct intel_dp *intel_dp);
extern void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
extern void intel_dp_encoder_destroy(struct drm_encoder *encoder);
extern void intel_dp_check_link_status(struct intel_dp *intel_dp);
extern bool intel_dp_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern bool intel_dpd_is_edp(struct drm_device *dev);
extern void ironlake_edp_backlight_on(struct intel_dp *intel_dp);
extern void ironlake_edp_backlight_off(struct intel_dp *intel_dp);
extern void ironlake_edp_panel_on(struct intel_dp *intel_dp);
extern void ironlake_edp_panel_off(struct intel_dp *intel_dp);
extern void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp);
extern void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
extern void intel_edp_link_config(struct intel_encoder *, int *, int *);
extern int intel_edp_target_clock(struct intel_encoder *,
struct drm_display_mode *mode);
extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);
extern int intel_plane_init(struct drm_device *dev, enum pipe pipe);
extern void intel_flush_display_plane(struct drm_i915_private *dev_priv,
enum plane plane);
void intel_sanitize_pm(struct drm_device *dev);
/* intel_panel.c */
extern int intel_panel_init(struct intel_panel *panel,
struct drm_display_mode *fixed_mode);
extern void intel_panel_fini(struct intel_panel *panel);
extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode);
extern void intel_pch_panel_fitting(struct drm_device *dev,
@ -386,24 +508,66 @@ extern void intel_pch_panel_fitting(struct drm_device *dev,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
extern u32 intel_panel_get_backlight(struct drm_device *dev);
extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
extern int intel_panel_setup_backlight(struct drm_device *dev);
extern void intel_panel_enable_backlight(struct drm_device *dev);
extern int intel_panel_setup_backlight(struct drm_connector *connector);
extern void intel_panel_enable_backlight(struct drm_device *dev,
enum pipe pipe);
extern void intel_panel_disable_backlight(struct drm_device *dev);
extern void intel_panel_destroy_backlight(struct drm_device *dev);
extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
struct intel_set_config {
struct drm_encoder **save_connector_encoders;
struct drm_crtc **save_encoder_crtcs;
bool fb_changed;
bool mode_changed;
};
extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
int x, int y, struct drm_framebuffer *old_fb);
extern void intel_modeset_disable(struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare(struct drm_encoder *encoder);
extern void intel_encoder_commit(struct drm_encoder *encoder);
extern void intel_crtc_update_dpms(struct drm_crtc *crtc);
extern void intel_encoder_noop(struct drm_encoder *encoder);
extern void intel_encoder_destroy(struct drm_encoder *encoder);
extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode);
extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder);
extern void intel_connector_dpms(struct drm_connector *, int mode);
extern bool intel_connector_get_hw_state(struct intel_connector *connector);
extern void intel_modeset_check_state(struct drm_device *dev);
static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
{
return to_intel_connector(connector)->encoder;
}
static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
{
struct intel_digital_port *intel_dig_port =
container_of(encoder, struct intel_digital_port, base.base);
return &intel_dig_port->dp;
}
static inline struct intel_digital_port *
enc_to_dig_port(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_digital_port, base.base);
}
static inline struct intel_digital_port *
dp_to_dig_port(struct intel_dp *intel_dp)
{
return container_of(intel_dp, struct intel_digital_port, dp);
}
static inline struct intel_digital_port *
hdmi_to_dig_port(struct intel_hdmi *intel_hdmi)
{
return container_of(intel_hdmi, struct intel_digital_port, hdmi);
}
extern void intel_connector_attach_encoder(struct intel_connector *connector,
struct intel_encoder *encoder);
extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
@ -412,20 +576,22 @@ extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
struct drm_crtc *crtc);
int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern enum transcoder
intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
enum pipe pipe);
extern void intel_wait_for_vblank(struct drm_device *dev, int pipe);
extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
extern int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp);
struct intel_load_detect_pipe {
struct drm_framebuffer *release_fb;
bool load_detect_temp;
int dpms_mode;
};
extern bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector,
extern bool intel_get_load_detect_pipe(struct drm_connector *connector,
struct drm_display_mode *mode,
struct intel_load_detect_pipe *old);
extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector,
extern void intel_release_load_detect_pipe(struct drm_connector *connector,
struct intel_load_detect_pipe *old);
extern void intelfb_restore(void);
@ -434,19 +600,6 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno);
extern void intel_enable_clock_gating(struct drm_device *dev);
extern void ironlake_disable_rc6(struct drm_device *dev);
extern void ironlake_enable_drps(struct drm_device *dev);
extern void ironlake_disable_drps(struct drm_device *dev);
extern void gen6_enable_rps(struct drm_i915_private *dev_priv);
extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv);
extern void gen6_disable_rps(struct drm_device *dev);
extern void intel_init_emon(struct drm_device *dev);
extern int intel_enable_rc6(const struct drm_device *dev);
extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode);
extern void intel_ddi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern int intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_i915_gem_object *obj,
@ -459,7 +612,7 @@ extern int intel_framebuffer_init(struct drm_device *dev,
struct drm_i915_gem_object *obj);
extern int intel_fbdev_init(struct drm_device *dev);
extern void intel_fbdev_fini(struct drm_device *dev);
extern void intel_fbdev_set_suspend(struct drm_device *dev, int state);
extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
extern void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
@ -496,6 +649,11 @@ extern void intel_update_sprite_watermarks(struct drm_device *dev, int pipe,
extern void intel_update_linetime_watermarks(struct drm_device *dev, int pipe,
struct drm_display_mode *mode);
extern unsigned long intel_gen4_compute_page_offset(int *x, int *y,
unsigned int tiling_mode,
unsigned int bpp,
unsigned int pitch);
extern int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
@ -509,5 +667,32 @@ extern void intel_init_pm(struct drm_device *dev);
extern bool intel_fbc_enabled(struct drm_device *dev);
extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
extern void intel_update_fbc(struct drm_device *dev);
/* IPS */
extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv);
extern void intel_gpu_ips_teardown(void);
extern void intel_init_power_wells(struct drm_device *dev);
extern void intel_enable_gt_powersave(struct drm_device *dev);
extern void intel_disable_gt_powersave(struct drm_device *dev);
extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv);
extern void ironlake_teardown_rc6(struct drm_device *dev);
extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe);
extern int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv);
extern void intel_ddi_pll_init(struct drm_device *dev);
extern void intel_ddi_enable_pipe_func(struct drm_crtc *crtc);
extern void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
enum transcoder cpu_transcoder);
extern void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc);
extern void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc);
extern void intel_ddi_setup_hw_pll_state(struct drm_device *dev);
extern bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock);
extern void intel_ddi_put_crtc_pll(struct drm_crtc *crtc);
extern void intel_ddi_set_pipe_settings(struct drm_crtc *crtc);
extern void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
extern bool
intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
#endif /* __INTEL_DRV_H__ */

View File

@ -0,0 +1,534 @@
/*
* Copyright 2006 Dave Airlie <airlied@linux.ie>
* Copyright © 2006-2007 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/dvo.h>
#define SIL164_ADDR 0x38
#define CH7xxx_ADDR 0x76
#define TFP410_ADDR 0x38
#define NS2501_ADDR 0x38
static const struct intel_dvo_device intel_dvo_devices[] = {
{
.type = INTEL_DVO_CHIP_TMDS,
.name = "sil164",
.dvo_reg = DVOC,
.slave_addr = SIL164_ADDR,
.dev_ops = &sil164_ops,
},
{
.type = INTEL_DVO_CHIP_TMDS,
.name = "ch7xxx",
.dvo_reg = DVOC,
.slave_addr = CH7xxx_ADDR,
.dev_ops = &ch7xxx_ops,
},
{
.type = INTEL_DVO_CHIP_LVDS,
.name = "ivch",
.dvo_reg = DVOA,
.slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */
.dev_ops = &ivch_ops,
},
{
.type = INTEL_DVO_CHIP_TMDS,
.name = "tfp410",
.dvo_reg = DVOC,
.slave_addr = TFP410_ADDR,
.dev_ops = &tfp410_ops,
},
{
.type = INTEL_DVO_CHIP_LVDS,
.name = "ch7017",
.dvo_reg = DVOC,
.slave_addr = 0x75,
.gpio = GMBUS_PORT_DPB,
.dev_ops = &ch7017_ops,
},
{
.type = INTEL_DVO_CHIP_TMDS,
.name = "ns2501",
.dvo_reg = DVOC,
.slave_addr = NS2501_ADDR,
.dev_ops = &ns2501_ops,
}
};
struct intel_dvo {
struct intel_encoder base;
struct intel_dvo_device dev;
struct drm_display_mode *panel_fixed_mode;
bool panel_wants_dither;
};
static struct intel_dvo *enc_to_intel_dvo(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_dvo, base.base);
}
static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_dvo, base);
}
static bool intel_dvo_connector_get_hw_state(struct intel_connector *connector)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(&connector->base);
return intel_dvo->dev.dev_ops->get_hw_state(&intel_dvo->dev);
}
static bool intel_dvo_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
u32 tmp;
tmp = I915_READ(intel_dvo->dev.dvo_reg);
if (!(tmp & DVO_ENABLE))
return false;
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void intel_disable_dvo(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
u32 dvo_reg = intel_dvo->dev.dvo_reg;
u32 temp = I915_READ(dvo_reg);
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
I915_READ(dvo_reg);
}
static void intel_enable_dvo(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
u32 dvo_reg = intel_dvo->dev.dvo_reg;
u32 temp = I915_READ(dvo_reg);
I915_WRITE(dvo_reg, temp | DVO_ENABLE);
I915_READ(dvo_reg);
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
}
static void intel_dvo_dpms(struct drm_connector *connector, int mode)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
struct drm_crtc *crtc;
/* dvo supports only 2 dpms states. */
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (mode == connector->dpms)
return;
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
crtc = intel_dvo->base.base.crtc;
if (!crtc) {
intel_dvo->base.connectors_active = false;
return;
}
if (mode == DRM_MODE_DPMS_ON) {
intel_dvo->base.connectors_active = true;
intel_crtc_update_dpms(crtc);
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
} else {
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
intel_dvo->base.connectors_active = false;
intel_crtc_update_dpms(crtc);
}
intel_modeset_check_state(connector->dev);
}
static int intel_dvo_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
/* XXX: Validate clock range */
if (intel_dvo->panel_fixed_mode) {
if (mode->hdisplay > intel_dvo->panel_fixed_mode->hdisplay)
return MODE_PANEL;
if (mode->vdisplay > intel_dvo->panel_fixed_mode->vdisplay)
return MODE_PANEL;
}
return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode);
}
static bool intel_dvo_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
/* If we have timings from the BIOS for the panel, put them in
* to the adjusted mode. The CRTC will be set up for this mode,
* with the panel scaling set up to source from the H/VDisplay
* of the original mode.
*/
if (intel_dvo->panel_fixed_mode != NULL) {
#define C(x) adjusted_mode->x = intel_dvo->panel_fixed_mode->x
C(hdisplay);
C(hsync_start);
C(hsync_end);
C(htotal);
C(vdisplay);
C(vsync_start);
C(vsync_end);
C(vtotal);
C(clock);
#undef C
}
if (intel_dvo->dev.dev_ops->mode_fixup)
return intel_dvo->dev.dev_ops->mode_fixup(&intel_dvo->dev, mode, adjusted_mode);
return true;
}
static void intel_dvo_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
int pipe = intel_crtc->pipe;
u32 dvo_val;
u32 dvo_reg = intel_dvo->dev.dvo_reg, dvo_srcdim_reg;
int dpll_reg = DPLL(pipe);
switch (dvo_reg) {
case DVOA:
default:
dvo_srcdim_reg = DVOA_SRCDIM;
break;
case DVOB:
dvo_srcdim_reg = DVOB_SRCDIM;
break;
case DVOC:
dvo_srcdim_reg = DVOC_SRCDIM;
break;
}
intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev, mode, adjusted_mode);
/* Save the data order, since I don't know what it should be set to. */
dvo_val = I915_READ(dvo_reg) &
(DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG);
dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE |
DVO_BLANK_ACTIVE_HIGH;
if (pipe == 1)
dvo_val |= DVO_PIPE_B_SELECT;
dvo_val |= DVO_PIPE_STALL;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
dvo_val |= DVO_HSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
dvo_val |= DVO_VSYNC_ACTIVE_HIGH;
I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED);
/*I915_WRITE(DVOB_SRCDIM,
(adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
(adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/
I915_WRITE(dvo_srcdim_reg,
(adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
(adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT));
/*I915_WRITE(DVOB, dvo_val);*/
I915_WRITE(dvo_reg, dvo_val);
}
/**
* Detect the output connection on our DVO device.
*
* Unimplemented.
*/
static enum drm_connector_status
intel_dvo_detect(struct drm_connector *connector, bool force)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev);
}
static int intel_dvo_get_modes(struct drm_connector *connector)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
/* We should probably have an i2c driver get_modes function for those
* devices which will have a fixed set of modes determined by the chip
* (TV-out, for example), but for now with just TMDS and LVDS,
* that's not the case.
*/
intel_ddc_get_modes(connector,
intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPC));
if (!list_empty(&connector->probed_modes))
return 1;
if (intel_dvo->panel_fixed_mode != NULL) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, intel_dvo->panel_fixed_mode);
if (mode) {
drm_mode_probed_add(connector, mode);
return 1;
}
}
return 0;
}
static void intel_dvo_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
free(connector, DRM_MEM_KMS);
}
static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
.mode_fixup = intel_dvo_mode_fixup,
.mode_set = intel_dvo_mode_set,
.disable = intel_encoder_noop,
};
static const struct drm_connector_funcs intel_dvo_connector_funcs = {
.dpms = intel_dvo_dpms,
.detect = intel_dvo_detect,
.destroy = intel_dvo_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
};
static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
.mode_valid = intel_dvo_mode_valid,
.get_modes = intel_dvo_get_modes,
.best_encoder = intel_best_encoder,
};
static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
{
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
if (intel_dvo->dev.dev_ops->destroy)
intel_dvo->dev.dev_ops->destroy(&intel_dvo->dev);
free(intel_dvo->panel_fixed_mode, DRM_MEM_KMS);
intel_encoder_destroy(encoder);
}
static const struct drm_encoder_funcs intel_dvo_enc_funcs = {
.destroy = intel_dvo_enc_destroy,
};
/**
* Attempts to get a fixed panel timing for LVDS (currently only the i830).
*
* Other chips with DVO LVDS will need to extend this to deal with the LVDS
* chip being on DVOB/C and having multiple pipes.
*/
static struct drm_display_mode *
intel_dvo_get_current_mode(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
uint32_t dvo_val = I915_READ(intel_dvo->dev.dvo_reg);
struct drm_display_mode *mode = NULL;
/* If the DVO port is active, that'll be the LVDS, so we can pull out
* its timings to get how the BIOS set up the panel.
*/
if (dvo_val & DVO_ENABLE) {
struct drm_crtc *crtc;
int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0;
crtc = intel_get_crtc_for_pipe(dev, pipe);
if (crtc) {
mode = intel_crtc_mode_get(dev, crtc);
if (mode) {
mode->type |= DRM_MODE_TYPE_PREFERRED;
if (dvo_val & DVO_HSYNC_ACTIVE_HIGH)
mode->flags |= DRM_MODE_FLAG_PHSYNC;
if (dvo_val & DVO_VSYNC_ACTIVE_HIGH)
mode->flags |= DRM_MODE_FLAG_PVSYNC;
}
}
}
return mode;
}
void intel_dvo_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *intel_encoder;
struct intel_dvo *intel_dvo;
struct intel_connector *intel_connector;
int i;
int encoder_type = DRM_MODE_ENCODER_NONE;
intel_dvo = malloc(sizeof(struct intel_dvo), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_dvo)
return;
intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_connector) {
free(intel_dvo, DRM_MEM_KMS);
return;
}
intel_encoder = &intel_dvo->base;
drm_encoder_init(dev, &intel_encoder->base,
&intel_dvo_enc_funcs, encoder_type);
intel_encoder->disable = intel_disable_dvo;
intel_encoder->enable = intel_enable_dvo;
intel_encoder->get_hw_state = intel_dvo_get_hw_state;
intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
/* Now, try to find a controller */
for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
struct drm_connector *connector = &intel_connector->base;
const struct intel_dvo_device *dvo = &intel_dvo_devices[i];
device_t i2c;
int gpio;
bool dvoinit;
/* Allow the I2C driver info to specify the GPIO to be used in
* special cases, but otherwise default to what's defined
* in the spec.
*/
if (intel_gmbus_is_port_valid(dvo->gpio))
gpio = dvo->gpio;
else if (dvo->type == INTEL_DVO_CHIP_LVDS)
gpio = GMBUS_PORT_SSC;
else
gpio = GMBUS_PORT_DPB;
/* Set up the I2C bus necessary for the chip we're probing.
* It appears that everything is on GPIOE except for panels
* on i830 laptops, which are on GPIOB (DVOA).
*/
i2c = intel_gmbus_get_adapter(dev_priv, gpio);
intel_dvo->dev = *dvo;
/* GMBUS NAK handling seems to be unstable, hence let the
* transmitter detection run in bit banging mode for now.
*/
intel_gmbus_force_bit(i2c, true);
dvoinit = dvo->dev_ops->init(&intel_dvo->dev, i2c);
intel_gmbus_force_bit(i2c, false);
if (!dvoinit)
continue;
intel_encoder->type = INTEL_OUTPUT_DVO;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
switch (dvo->type) {
case INTEL_DVO_CHIP_TMDS:
intel_encoder->cloneable = true;
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_DVII);
encoder_type = DRM_MODE_ENCODER_TMDS;
break;
case INTEL_DVO_CHIP_LVDS:
intel_encoder->cloneable = false;
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
encoder_type = DRM_MODE_ENCODER_LVDS;
break;
}
drm_connector_helper_add(connector,
&intel_dvo_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
drm_encoder_helper_add(&intel_encoder->base,
&intel_dvo_helper_funcs);
intel_connector_attach_encoder(intel_connector, intel_encoder);
if (dvo->type == INTEL_DVO_CHIP_LVDS) {
/* For our LVDS chipsets, we should hopefully be able
* to dig the fixed panel mode out of the BIOS data.
* However, it's in a different format from the BIOS
* data on chipsets with integrated LVDS (stored in AIM
* headers, likely), so for now, just get the current
* mode being output through DVO.
*/
intel_dvo->panel_fixed_mode =
intel_dvo_get_current_mode(connector);
intel_dvo->panel_wants_dither = true;
}
return;
}
drm_encoder_cleanup(&intel_encoder->base);
free(intel_dvo, DRM_MEM_KMS);
free(intel_connector, DRM_MEM_KMS);
}

View File

@ -29,20 +29,33 @@ __FBSDID("$FreeBSD$");
#include "opt_syscons.h"
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_fb_helper.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#if defined(__linux__)
static struct fb_ops intelfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_blank = drm_fb_helper_blank,
.fb_setcmap = drm_fb_helper_setcmap,
.fb_debug_enter = drm_fb_helper_debug_enter,
.fb_debug_leave = drm_fb_helper_debug_leave,
};
#endif
static int intelfb_create(struct intel_fbdev *ifbdev,
struct drm_fb_helper_surface_size *sizes)
{
struct drm_device *dev = ifbdev->helper.dev;
#if 0
struct drm_i915_private *dev_priv = dev->dev_private;
#endif
struct fb_info *info;
struct drm_framebuffer *fb;
struct drm_mode_fb_cmd2 mode_cmd = {};
@ -85,17 +98,12 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
goto out_unpin;
}
#if 0
info->par = ifbdev;
#else
info->fb_size = size;
info->fb_bpp = sizes->surface_bpp;
info->fb_pbase = dev->agp->base + obj->gtt_offset;
info->fb_pbase = dev_priv->mm.gtt_base_addr + obj->gtt_offset;
info->fb_vbase = (vm_offset_t)pmap_mapdev_attr(info->fb_pbase, size,
PAT_WRITE_COMBINING);
#endif
ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj);
if (ret)
goto out_unpin;
@ -104,40 +112,6 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
ifbdev->helper.fb = fb;
ifbdev->helper.fbdev = info;
#if 0
strcpy(info->fix.id, "inteldrmfb");
info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &intelfb_ops;
ret = fb_alloc_cmap(&info->cmap, 256, 0);
if (ret) {
ret = -ENOMEM;
goto out_unpin;
}
/* setup aperture base/size for vesafb takeover */
info->apertures = alloc_apertures(1);
if (!info->apertures) {
ret = -ENOMEM;
goto out_unpin;
}
info->apertures->ranges[0].base = dev->mode_config.fb_base;
info->apertures->ranges[0].size =
dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset;
info->fix.smem_len = size;
info->screen_base = ioremap_wc(dev->agp->base + obj->gtt_offset, size);
if (!info->screen_base) {
ret = -ENOSPC;
goto out_unpin;
}
info->screen_size = size;
// memset(info->screen_base, 0, size);
#endif
drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
@ -148,10 +122,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
fb->width, fb->height, fb->depth,
obj->gtt_offset, obj);
DRM_UNLOCK(dev);
#if 1
KIB_NOTYET();
#else
#ifdef __linux__
vga_switcheroo_client_fb_set(dev->pdev, info);
#endif
return 0;
@ -195,12 +168,8 @@ static void intel_fbdev_destroy(struct drm_device *dev,
if (ifbdev->helper.fbdev) {
info = ifbdev->helper.fbdev;
#if 0
unregister_framebuffer(info);
iounmap(info->screen_base);
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
#endif
if (info->fb_fbd_dev != NULL)
device_delete_child(dev->dev, info->fb_fbd_dev);
framebuffer_release(info);
}
@ -224,6 +193,8 @@ int intel_fbdev_init(struct drm_device *dev)
int ret;
ifbdev = malloc(sizeof(struct intel_fbdev), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!ifbdev)
return -ENOMEM;
dev_priv->fbdev = ifbdev;
ifbdev->helper.funcs = &intel_fb_helper_funcs;
@ -255,6 +226,19 @@ void intel_fbdev_fini(struct drm_device *dev)
dev_priv->fbdev = NULL;
}
void intel_fbdev_set_suspend(struct drm_device *dev, int state)
{
drm_i915_private_t *dev_priv = dev->dev_private;
if (!dev_priv->fbdev)
return;
#ifdef FREEBSD_WIP
fb_set_suspend(dev_priv->fbdev->helper.fbdev, state);
#endif /* FREEBSD_WIP */
}
MODULE_LICENSE("GPL and additional rights");
void intel_fb_output_poll_changed(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;

View File

@ -30,22 +30,42 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_edid.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
#define mmiowb() barrier()
static struct drm_device *intel_hdmi_to_dev(struct intel_hdmi *intel_hdmi)
{
return hdmi_to_dig_port(intel_hdmi)->base.base.dev;
}
static void
assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi)
{
struct drm_device *dev = intel_hdmi_to_dev(intel_hdmi);
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t enabled_bits;
enabled_bits = IS_HASWELL(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE;
WARN(I915_READ(intel_hdmi->sdvox_reg) & enabled_bits,
"HDMI port enabled, expecting disabled\n");
}
struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_hdmi, base.base);
struct intel_digital_port *intel_dig_port =
container_of(encoder, struct intel_digital_port, base.base);
return &intel_dig_port->hdmi;
}
static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_hdmi, base);
return enc_to_intel_hdmi(&intel_attached_encoder(connector)->base);
}
void intel_dip_infoframe_csum(struct dip_infoframe *frame)
@ -121,36 +141,34 @@ static void g4x_write_infoframe(struct drm_encoder *encoder,
uint32_t *data = (uint32_t *)frame;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 val = I915_READ(VIDEO_DIP_CTL);
unsigned i, len = DIP_HEADER_SIZE + frame->len;
val &= ~VIDEO_DIP_PORT_MASK;
if (intel_hdmi->sdvox_reg == SDVOB)
val |= VIDEO_DIP_PORT_B;
else if (intel_hdmi->sdvox_reg == SDVOC)
val |= VIDEO_DIP_PORT_C;
else
return;
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(frame);
val &= ~g4x_infoframe_enable(frame);
val |= VIDEO_DIP_ENABLE;
I915_WRITE(VIDEO_DIP_CTL, val);
mmiowb();
for (i = 0; i < len; i += 4) {
I915_WRITE(VIDEO_DIP_DATA, *data);
data++;
}
/* Write every possible data byte to force correct ECC calculation. */
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
I915_WRITE(VIDEO_DIP_DATA, 0);
mmiowb();
val |= g4x_infoframe_enable(frame);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
I915_WRITE(VIDEO_DIP_CTL, val);
POSTING_READ(VIDEO_DIP_CTL);
}
static void ibx_write_infoframe(struct drm_encoder *encoder,
@ -160,46 +178,35 @@ static void ibx_write_infoframe(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
unsigned i, len = DIP_HEADER_SIZE + frame->len;
u32 val = I915_READ(reg);
val &= ~VIDEO_DIP_PORT_MASK;
switch (intel_hdmi->sdvox_reg) {
case HDMIB:
val |= VIDEO_DIP_PORT_B;
break;
case HDMIC:
val |= VIDEO_DIP_PORT_C;
break;
case HDMID:
val |= VIDEO_DIP_PORT_D;
break;
default:
return;
}
intel_wait_for_vblank(dev, intel_crtc->pipe);
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(frame);
val &= ~g4x_infoframe_enable(frame);
val |= VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
mmiowb();
for (i = 0; i < len; i += 4) {
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
data++;
}
/* Write every possible data byte to force correct ECC calculation. */
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
mmiowb();
val |= g4x_infoframe_enable(frame);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
I915_WRITE(reg, val);
POSTING_READ(reg);
}
static void cpt_write_infoframe(struct drm_encoder *encoder,
@ -213,32 +220,34 @@ static void cpt_write_infoframe(struct drm_encoder *encoder,
unsigned i, len = DIP_HEADER_SIZE + frame->len;
u32 val = I915_READ(reg);
intel_wait_for_vblank(dev, intel_crtc->pipe);
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(frame);
/* The DIP control register spec says that we need to update the AVI
* infoframe without clearing its enable bit */
if (frame->type == DIP_TYPE_AVI)
val |= VIDEO_DIP_ENABLE_AVI;
else
if (frame->type != DIP_TYPE_AVI)
val &= ~g4x_infoframe_enable(frame);
val |= VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
mmiowb();
for (i = 0; i < len; i += 4) {
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
data++;
}
/* Write every possible data byte to force correct ECC calculation. */
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
mmiowb();
val |= g4x_infoframe_enable(frame);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
I915_WRITE(reg, val);
POSTING_READ(reg);
}
static void vlv_write_infoframe(struct drm_encoder *encoder,
@ -252,26 +261,31 @@ static void vlv_write_infoframe(struct drm_encoder *encoder,
unsigned i, len = DIP_HEADER_SIZE + frame->len;
u32 val = I915_READ(reg);
intel_wait_for_vblank(dev, intel_crtc->pipe);
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(frame);
val &= ~g4x_infoframe_enable(frame);
val |= VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
mmiowb();
for (i = 0; i < len; i += 4) {
I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
data++;
}
/* Write every possible data byte to force correct ECC calculation. */
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
mmiowb();
val |= g4x_infoframe_enable(frame);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
I915_WRITE(reg, val);
POSTING_READ(reg);
}
static void hsw_write_infoframe(struct drm_encoder *encoder,
@ -289,18 +303,22 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
if (data_reg == 0)
return;
intel_wait_for_vblank(dev, intel_crtc->pipe);
val &= ~hsw_infoframe_enable(frame);
I915_WRITE(ctl_reg, val);
mmiowb();
for (i = 0; i < len; i += 4) {
I915_WRITE(data_reg + i, *data);
data++;
}
/* Write every possible data byte to force correct ECC calculation. */
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
I915_WRITE(data_reg + i, 0);
mmiowb();
val |= hsw_infoframe_enable(frame);
I915_WRITE(ctl_reg, val);
POSTING_READ(ctl_reg);
}
static void intel_set_infoframe(struct drm_encoder *encoder,
@ -308,14 +326,11 @@ static void intel_set_infoframe(struct drm_encoder *encoder,
{
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
if (!intel_hdmi->has_hdmi_sink)
return;
intel_dip_infoframe_csum(frame);
intel_hdmi->write_infoframe(encoder, frame);
}
void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct dip_infoframe avi_if = {
@ -327,10 +342,12 @@ void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)
avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2;
avi_if.body.avi.VIC = drm_mode_cea_vic(adjusted_mode);
intel_set_infoframe(encoder, &avi_if);
}
void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
{
struct dip_infoframe spd_if;
@ -345,6 +362,225 @@ void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
intel_set_infoframe(encoder, &spd_if);
}
static void g4x_set_infoframes(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 reg = VIDEO_DIP_CTL;
u32 val = I915_READ(reg);
u32 port;
assert_hdmi_port_disabled(intel_hdmi);
/* If the registers were not initialized yet, they might be zeroes,
* which means we're selecting the AVI DIP and we're setting its
* frequency to once. This seems to really confuse the HW and make
* things stop working (the register spec says the AVI always needs to
* be sent every VSync). So here we avoid writing to the register more
* than we need and also explicitly select the AVI DIP and explicitly
* set its frequency to every VSync. Avoiding to write it twice seems to
* be enough to solve the problem, but being defensive shouldn't hurt us
* either. */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!intel_hdmi->has_hdmi_sink) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
POSTING_READ(reg);
return;
}
switch (intel_hdmi->sdvox_reg) {
case SDVOB:
port = VIDEO_DIP_PORT_B;
break;
case SDVOC:
port = VIDEO_DIP_PORT_C;
break;
default:
BUG();
return;
}
if (port != (val & VIDEO_DIP_PORT_MASK)) {
if (val & VIDEO_DIP_ENABLE) {
val &= ~VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
POSTING_READ(reg);
}
val &= ~VIDEO_DIP_PORT_MASK;
val |= port;
}
val |= VIDEO_DIP_ENABLE;
val &= ~VIDEO_DIP_ENABLE_VENDOR;
I915_WRITE(reg, val);
POSTING_READ(reg);
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
intel_hdmi_set_spd_infoframe(encoder);
}
static void ibx_set_infoframes(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
u32 val = I915_READ(reg);
u32 port;
assert_hdmi_port_disabled(intel_hdmi);
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!intel_hdmi->has_hdmi_sink) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
POSTING_READ(reg);
return;
}
switch (intel_hdmi->sdvox_reg) {
case HDMIB:
port = VIDEO_DIP_PORT_B;
break;
case HDMIC:
port = VIDEO_DIP_PORT_C;
break;
case HDMID:
port = VIDEO_DIP_PORT_D;
break;
default:
BUG();
return;
}
if (port != (val & VIDEO_DIP_PORT_MASK)) {
if (val & VIDEO_DIP_ENABLE) {
val &= ~VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
POSTING_READ(reg);
}
val &= ~VIDEO_DIP_PORT_MASK;
val |= port;
}
val |= VIDEO_DIP_ENABLE;
val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_GCP);
I915_WRITE(reg, val);
POSTING_READ(reg);
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
intel_hdmi_set_spd_infoframe(encoder);
}
static void cpt_set_infoframes(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
u32 val = I915_READ(reg);
assert_hdmi_port_disabled(intel_hdmi);
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!intel_hdmi->has_hdmi_sink) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI);
I915_WRITE(reg, val);
POSTING_READ(reg);
return;
}
/* Set both together, unset both together: see the spec. */
val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI;
val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_GCP);
I915_WRITE(reg, val);
POSTING_READ(reg);
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
intel_hdmi_set_spd_infoframe(encoder);
}
static void vlv_set_infoframes(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
u32 val = I915_READ(reg);
assert_hdmi_port_disabled(intel_hdmi);
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!intel_hdmi->has_hdmi_sink) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~VIDEO_DIP_ENABLE;
I915_WRITE(reg, val);
POSTING_READ(reg);
return;
}
val |= VIDEO_DIP_ENABLE;
val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_GCP);
I915_WRITE(reg, val);
POSTING_READ(reg);
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
intel_hdmi_set_spd_infoframe(encoder);
}
static void hsw_set_infoframes(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe);
u32 val = I915_READ(reg);
assert_hdmi_port_disabled(intel_hdmi);
if (!intel_hdmi->has_hdmi_sink) {
I915_WRITE(reg, 0);
POSTING_READ(reg);
return;
}
val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_GCP_HSW |
VIDEO_DIP_ENABLE_VS_HSW | VIDEO_DIP_ENABLE_GMP_HSW);
I915_WRITE(reg, val);
POSTING_READ(reg);
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
intel_hdmi_set_spd_infoframe(encoder);
}
static void intel_hdmi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -355,7 +591,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 sdvox;
sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;
sdvox = SDVO_ENCODING_HDMI;
if (!HAS_PCH_SPLIT(dev))
sdvox |= intel_hdmi->color_range;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
@ -373,7 +609,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
sdvox |= HDMI_MODE_SELECT;
if (intel_hdmi->has_audio) {
DRM_DEBUG_KMS("Enabling HDMI audio on pipe %c\n",
DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
pipe_name(intel_crtc->pipe));
sdvox |= SDVO_AUDIO_ENABLE;
sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
@ -382,21 +618,41 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
if (HAS_PCH_CPT(dev))
sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
else if (intel_crtc->pipe == 1)
else if (intel_crtc->pipe == PIPE_B)
sdvox |= SDVO_PIPE_B_SELECT;
I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
POSTING_READ(intel_hdmi->sdvox_reg);
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
intel_hdmi_set_spd_infoframe(encoder);
intel_hdmi->set_infoframes(encoder, adjusted_mode);
}
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_device *dev = encoder->dev;
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
u32 tmp;
tmp = I915_READ(intel_hdmi->sdvox_reg);
if (!(tmp & SDVO_ENABLE))
return false;
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void intel_enable_hdmi(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
u32 temp;
u32 enable_bits = SDVO_ENABLE;
@ -405,6 +661,17 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
temp = I915_READ(intel_hdmi->sdvox_reg);
/* HW workaround for IBX, we need to move the port to transcoder A
* before disabling it. */
if (HAS_PCH_IBX(dev)) {
struct drm_crtc *crtc = encoder->base.crtc;
int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
/* Restore the transcoder select bit. */
if (pipe == PIPE_B)
enable_bits |= SDVO_PIPE_B_SELECT;
}
/* HW workaround, need to toggle enable bit off and on for 12bpc, but
* we do this anyway which shows more stable in testing.
*/
@ -413,11 +680,63 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
POSTING_READ(intel_hdmi->sdvox_reg);
}
if (mode != DRM_MODE_DPMS_ON) {
temp &= ~enable_bits;
} else {
temp |= enable_bits;
temp |= enable_bits;
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
/* HW workaround, need to write this twice for issue that may result
* in first write getting masked.
*/
if (HAS_PCH_SPLIT(dev)) {
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
}
}
static void intel_disable_hdmi(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
u32 temp;
u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
temp = I915_READ(intel_hdmi->sdvox_reg);
/* HW workaround for IBX, we need to move the port to transcoder A
* before disabling it. */
if (HAS_PCH_IBX(dev)) {
struct drm_crtc *crtc = encoder->base.crtc;
int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
if (temp & SDVO_PIPE_B_SELECT) {
temp &= ~SDVO_PIPE_B_SELECT;
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
/* Again we need to write this twice. */
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
/* Transcoder selection bits only update
* effectively on vblank. */
if (crtc)
intel_wait_for_vblank(dev, pipe);
else
DRM_MSLEEP(50);
}
}
/* HW workaround, need to toggle enable bit off and on for 12bpc, but
* we do this anyway which shows more stable in testing.
*/
if (HAS_PCH_SPLIT(dev)) {
I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);
POSTING_READ(intel_hdmi->sdvox_reg);
}
temp &= ~enable_bits;
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
@ -445,25 +764,53 @@ static int intel_hdmi_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi)
{
struct drm_device *dev = intel_hdmi_to_dev(intel_hdmi);
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t bit;
switch (intel_hdmi->sdvox_reg) {
case SDVOB:
bit = HDMIB_HOTPLUG_LIVE_STATUS;
break;
case SDVOC:
bit = HDMIC_HOTPLUG_LIVE_STATUS;
break;
default:
bit = 0;
break;
}
return I915_READ(PORT_HOTPLUG_STAT) & bit;
}
static enum drm_connector_status
intel_hdmi_detect(struct drm_connector *connector, bool force)
{
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct intel_digital_port *intel_dig_port =
hdmi_to_dig_port(intel_hdmi);
struct intel_encoder *intel_encoder = &intel_dig_port->base;
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct edid *edid;
enum drm_connector_status status = connector_status_disconnected;
if (IS_G4X(connector->dev) && !g4x_hdmi_connected(intel_hdmi))
return status;
intel_hdmi->has_hdmi_sink = false;
intel_hdmi->has_audio = false;
edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv,
intel_hdmi->ddc_bus));
edid = drm_get_edid(connector,
intel_gmbus_get_adapter(dev_priv,
intel_hdmi->ddc_bus));
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
@ -474,16 +821,13 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
}
free(edid, DRM_MEM_KMS);
} else {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] got no edid, ddc port %d\n",
connector->base.id, drm_get_connector_name(connector),
intel_hdmi->ddc_bus);
}
if (status == connector_status_connected) {
if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
intel_hdmi->has_audio =
(intel_hdmi->force_audio == HDMI_AUDIO_ON);
intel_encoder->type = INTEL_OUTPUT_HDMI;
}
return status;
@ -517,7 +861,6 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL)
has_audio = drm_detect_monitor_audio(edid);
free(edid, DRM_MEM_KMS);
}
@ -526,10 +869,12 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
static int
intel_hdmi_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
struct drm_property *property,
uint64_t val)
{
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct intel_digital_port *intel_dig_port =
hdmi_to_dig_port(intel_hdmi);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
int ret;
@ -569,11 +914,10 @@ intel_hdmi_set_property(struct drm_connector *connector,
return -EINVAL;
done:
if (intel_hdmi->base.base.crtc) {
struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y,
crtc->fb);
if (intel_dig_port->base.base.crtc) {
struct drm_crtc *crtc = intel_dig_port->base.base.crtc;
intel_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb);
}
return 0;
@ -581,31 +925,18 @@ intel_hdmi_set_property(struct drm_connector *connector,
static void intel_hdmi_destroy(struct drm_connector *connector)
{
#if 0
drm_sysfs_connector_remove(connector);
#endif
drm_connector_cleanup(connector);
free(connector, DRM_MEM_KMS);
}
static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = {
.dpms = intel_ddi_dpms,
.mode_fixup = intel_hdmi_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_ddi_mode_set,
.commit = intel_encoder_commit,
};
static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
.dpms = intel_hdmi_dpms,
.mode_fixup = intel_hdmi_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_hdmi_mode_set,
.commit = intel_encoder_commit,
.disable = intel_encoder_noop,
};
static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = intel_connector_dpms,
.detect = intel_hdmi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_hdmi_set_property,
@ -629,117 +960,68 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
intel_attach_broadcast_rgb_property(connector);
}
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
struct intel_connector *intel_connector)
{
struct drm_connector *connector = &intel_connector->base;
struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
struct intel_encoder *intel_encoder = &intel_dig_port->base;
struct drm_device *dev = intel_encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_connector *connector;
struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector;
struct intel_hdmi *intel_hdmi;
int i;
enum port port = intel_dig_port->port;
intel_hdmi = malloc(sizeof(struct intel_hdmi), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
intel_encoder = &intel_hdmi->base;
drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
DRM_MODE_ENCODER_TMDS);
connector = &intel_connector->base;
drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
intel_encoder->type = INTEL_OUTPUT_HDMI;
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
/* Set up the DDC bus. */
if (sdvox_reg == SDVOB) {
intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
switch (port) {
case PORT_B:
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == SDVOC) {
intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
break;
case PORT_C:
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMIB) {
intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMIC) {
intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMID) {
intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
break;
case PORT_D:
intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == DDI_BUF_CTL(PORT_B)) {
DRM_DEBUG_DRIVER("LPT: detected output on DDI B\n");
intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
intel_hdmi->ddi_port = PORT_B;
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == DDI_BUF_CTL(PORT_C)) {
DRM_DEBUG_DRIVER("LPT: detected output on DDI C\n");
intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
intel_hdmi->ddi_port = PORT_C;
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == DDI_BUF_CTL(PORT_D)) {
DRM_DEBUG_DRIVER("LPT: detected output on DDI D\n");
intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
intel_hdmi->ddi_port = PORT_D;
dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
} else {
/* If we got an unknown sdvox_reg, things are pretty much broken
* in a way that we should let the kernel know about it */
DRM_DEBUG_KMS("unknown sdvox_reg %d\n", sdvox_reg);
break;
case PORT_A:
/* Internal port only for eDP. */
default:
BUG();
}
intel_hdmi->sdvox_reg = sdvox_reg;
if (!HAS_PCH_SPLIT(dev)) {
intel_hdmi->write_infoframe = g4x_write_infoframe;
I915_WRITE(VIDEO_DIP_CTL, 0);
intel_hdmi->set_infoframes = g4x_set_infoframes;
} else if (IS_VALLEYVIEW(dev)) {
intel_hdmi->write_infoframe = vlv_write_infoframe;
for_each_pipe(i)
I915_WRITE(VLV_TVIDEO_DIP_CTL(i), 0);
intel_hdmi->set_infoframes = vlv_set_infoframes;
} else if (IS_HASWELL(dev)) {
/* FIXME: Haswell has a new set of DIP frame registers, but we are
* just doing the minimal required for HDMI to work at this stage.
*/
intel_hdmi->write_infoframe = hsw_write_infoframe;
for_each_pipe(i)
I915_WRITE(HSW_TVIDEO_DIP_CTL(i), 0);
intel_hdmi->set_infoframes = hsw_set_infoframes;
} else if (HAS_PCH_IBX(dev)) {
intel_hdmi->write_infoframe = ibx_write_infoframe;
for_each_pipe(i)
I915_WRITE(TVIDEO_DIP_CTL(i), 0);
intel_hdmi->set_infoframes = ibx_set_infoframes;
} else {
intel_hdmi->write_infoframe = cpt_write_infoframe;
for_each_pipe(i)
I915_WRITE(TVIDEO_DIP_CTL(i), 0);
intel_hdmi->set_infoframes = cpt_set_infoframes;
}
if (IS_HASWELL(dev))
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw);
intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
else
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
intel_connector->get_hw_state = intel_connector_get_hw_state;
intel_hdmi_add_properties(intel_hdmi, connector);
intel_connector_attach_encoder(intel_connector, intel_encoder);
#if 0
drm_sysfs_connector_add(connector);
#endif
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
@ -750,3 +1032,42 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
}
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port)
{
struct intel_digital_port *intel_dig_port;
struct intel_encoder *intel_encoder;
struct drm_encoder *encoder;
struct intel_connector *intel_connector;
intel_dig_port = malloc(sizeof(struct intel_digital_port), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_dig_port)
return;
intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_connector) {
free(intel_dig_port, DRM_MEM_KMS);
return;
}
intel_encoder = &intel_dig_port->base;
encoder = &intel_encoder->base;
drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
intel_encoder->enable = intel_enable_hdmi;
intel_encoder->disable = intel_disable_hdmi;
intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
intel_encoder->type = INTEL_OUTPUT_HDMI;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
intel_encoder->cloneable = false;
intel_dig_port->port = port;
intel_dig_port->hdmi.sdvox_reg = sdvox_reg;
intel_dig_port->dp.output_reg = 0;
intel_hdmi_init_connector(intel_dig_port, intel_connector);
}

View File

@ -25,50 +25,20 @@
* Authors:
* Eric Anholt <eric@anholt.net>
* Chris Wilson <chris@chris-wilson.co.uk>
*
* Copyright (c) 2011 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Konstantin Belousov under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/iicbus/iic.h>
#include <dev/iicbus/iiconf.h>
#include <dev/iicbus/iicbus.h>
#include "iicbus_if.h"
#include "iicbb_if.h"
static void intel_teardown_gmbus_m(struct drm_device *dev, int m);
struct gmbus_port {
const char *name;
int reg;
@ -87,17 +57,37 @@ static const struct gmbus_port gmbus_ports[] = {
#define I2C_RISEFALL_TIME 10
/*
* FIXME Linux<->FreeBSD: dvo_ns2501.C wants the struct intel_gmbus
* below but it just has the device_t at hand. It still uses
* device_get_softc(), thus expects struct intel_gmbus to remain the
* first member.
*/
struct intel_iic_softc {
struct drm_device *drm_dev;
struct intel_gmbus *bus;
device_t iic_dev;
bool force_bit_dev;
char name[32];
uint32_t reg;
uint32_t reg0;
};
static inline struct intel_gmbus *
to_intel_gmbus(device_t i2c)
{
struct intel_iic_softc *sc;
sc = device_get_softc(i2c);
return sc->bus;
}
bool intel_gmbus_is_forced_bit(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
return bus->force_bit;
}
void
intel_iic_reset(struct drm_device *dev)
intel_i2c_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
@ -110,9 +100,9 @@ intel_iicbus_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr)
struct drm_device *dev;
sc = device_get_softc(idev);
dev = sc->drm_dev;
dev = sc->bus->dev_priv->dev;
intel_iic_reset(dev);
intel_i2c_reset(dev);
return (0);
}
@ -132,15 +122,15 @@ static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
I915_WRITE(DSPCLK_GATE_D, val);
}
static u32 get_reserved(device_t idev)
static u32 get_reserved(struct intel_gmbus *bus)
{
struct intel_iic_softc *sc = device_get_softc(idev);
struct drm_device *dev = sc->drm_dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_private *dev_priv = bus->dev_priv;
struct drm_device *dev = dev_priv->dev;
u32 reserved = 0;
/* On most chips, these bits must be preserved in software. */
if (!IS_I830(dev) && !IS_845G(dev))
reserved = I915_READ_NOTRACE(sc->reg) &
reserved = I915_READ_NOTRACE(bus->gpio_reg) &
(GPIO_DATA_PULLUP_DISABLE |
GPIO_CLOCK_PULLUP_DISABLE);
@ -150,28 +140,31 @@ static u32 get_reserved(device_t idev)
static int get_clock(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct drm_i915_private *dev_priv = sc->drm_dev->dev_private;
u32 reserved = get_reserved(adapter);
I915_WRITE_NOTRACE(sc->reg, reserved | GPIO_CLOCK_DIR_MASK);
I915_WRITE_NOTRACE(sc->reg, reserved);
return ((I915_READ_NOTRACE(sc->reg) & GPIO_CLOCK_VAL_IN) != 0);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
u32 reserved = get_reserved(bus);
I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_CLOCK_DIR_MASK);
I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_CLOCK_VAL_IN) != 0;
}
static int get_data(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct drm_i915_private *dev_priv = sc->drm_dev->dev_private;
u32 reserved = get_reserved(adapter);
I915_WRITE_NOTRACE(sc->reg, reserved | GPIO_DATA_DIR_MASK);
I915_WRITE_NOTRACE(sc->reg, reserved);
return ((I915_READ_NOTRACE(sc->reg) & GPIO_DATA_VAL_IN) != 0);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
u32 reserved = get_reserved(bus);
I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_DATA_DIR_MASK);
I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_DATA_VAL_IN) != 0;
}
static void set_clock(device_t adapter, int state_high)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct drm_i915_private *dev_priv = sc->drm_dev->dev_private;
u32 reserved = get_reserved(adapter);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
u32 reserved = get_reserved(bus);
u32 clock_bits;
if (state_high)
@ -180,15 +173,16 @@ static void set_clock(device_t adapter, int state_high)
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
GPIO_CLOCK_VAL_MASK;
I915_WRITE_NOTRACE(sc->reg, reserved | clock_bits);
POSTING_READ(sc->reg);
I915_WRITE_NOTRACE(bus->gpio_reg, reserved | clock_bits);
POSTING_READ(bus->gpio_reg);
}
static void set_data(device_t adapter, int state_high)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct drm_i915_private *dev_priv = sc->drm_dev->dev_private;
u32 reserved = get_reserved(adapter);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
u32 reserved = get_reserved(bus);
u32 data_bits;
if (state_high)
@ -197,21 +191,22 @@ static void set_data(device_t adapter, int state_high)
data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
GPIO_DATA_VAL_MASK;
I915_WRITE_NOTRACE(sc->reg, reserved | data_bits);
POSTING_READ(sc->reg);
I915_WRITE_NOTRACE(bus->gpio_reg, reserved | data_bits);
POSTING_READ(bus->gpio_reg);
}
static int
intel_gpio_pre_xfer(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct drm_i915_private *dev_priv = sc->drm_dev->dev_private;
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
intel_iic_reset(sc->drm_dev);
intel_i2c_reset(dev_priv->dev);
intel_i2c_quirk_set(dev_priv, true);
IICBB_SETSDA(adapter, 1);
IICBB_SETSCL(adapter, 1);
DELAY(I2C_RISEFALL_TIME);
udelay(I2C_RISEFALL_TIME);
return 0;
}
@ -219,13 +214,23 @@ static void
intel_gpio_post_xfer(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct drm_i915_private *dev_priv = sc->drm_dev->dev_private;
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
IICBB_SETSDA(adapter, 1);
IICBB_SETSCL(adapter, 1);
intel_i2c_quirk_set(dev_priv, false);
}
static void
intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
{
struct drm_i915_private *dev_priv = bus->dev_priv;
/* -1 to map pin pair to gmbus index */
bus->gpio_reg = dev_priv->gpio_mmio_base + gmbus_ports[pin - 1].reg;
}
static int
gmbus_xfer_read(struct drm_i915_private *dev_priv, struct iic_msg *msg,
u32 gmbus1_index)
@ -245,10 +250,9 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct iic_msg *msg,
u32 val, loop = 0;
u32 gmbus2;
ret = _intel_wait_for(sc->drm_dev,
((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_RDY)),
50, 1, "915gbr");
ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_RDY),
50);
if (ret)
return -ETIMEDOUT;
if (gmbus2 & GMBUS_SATOER)
@ -295,10 +299,9 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct iic_msg *msg)
I915_WRITE(GMBUS3 + reg_offset, val);
ret = _intel_wait_for(sc->drm_dev,
((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_RDY)),
50, 1, "915gbw");
ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_RDY),
50);
if (ret)
return -ETIMEDOUT;
if (gmbus2 & GMBUS_SATOER)
@ -315,8 +318,8 @@ static bool
gmbus_is_index_read(struct iic_msg *msgs, int i, int num)
{
return (i + 1 < num &&
!(msgs[i].flags & IIC_M_RD) && msgs[i].len <= 2 &&
(msgs[i + 1].flags & IIC_M_RD));
!(msgs[i].flags & I2C_M_RD) && msgs[i].len <= 2 &&
(msgs[i + 1].flags & I2C_M_RD));
}
static int
@ -353,43 +356,42 @@ gmbus_xfer(device_t adapter,
uint32_t num)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct drm_i915_private *dev_priv = sc->drm_dev->dev_private;
int error, i, ret, reg_offset, unit;
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
int i, reg_offset;
int ret = 0;
error = 0;
unit = device_get_unit(adapter);
sx_xlock(&dev_priv->gmbus_mutex);
sx_xlock(&dev_priv->gmbus_sx);
if (sc->force_bit_dev) {
error = -IICBUS_TRANSFER(dev_priv->bbbus[unit], msgs, num);
if (bus->force_bit) {
ret = -IICBUS_TRANSFER(bus->bbbus, msgs, num);
goto out;
}
reg_offset = dev_priv->gpio_mmio_base;
I915_WRITE(GMBUS0 + reg_offset, sc->reg0);
I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
for (i = 0; i < num; i++) {
u32 gmbus2;
if (gmbus_is_index_read(msgs, i, num)) {
error = gmbus_xfer_index_read(dev_priv, &msgs[i]);
ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
i += 1; /* set i to the index of the read xfer */
} else if (msgs[i].flags & IIC_M_RD) {
error = gmbus_xfer_read(dev_priv, &msgs[i], 0);
} else if (msgs[i].flags & I2C_M_RD) {
ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
} else {
error = gmbus_xfer_write(dev_priv, &msgs[i]);
ret = gmbus_xfer_write(dev_priv, &msgs[i]);
}
if (error == -ETIMEDOUT)
if (ret == -ETIMEDOUT)
goto timeout;
if (error == -ENXIO)
if (ret == -ENXIO)
goto clear_err;
ret = _intel_wait_for(sc->drm_dev,
((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_WAIT_PHASE)),
50, 1, "915gbh");
ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_WAIT_PHASE),
50);
if (ret)
goto timeout;
if (gmbus2 & GMBUS_SATOER)
@ -406,12 +408,11 @@ gmbus_xfer(device_t adapter,
* We will re-enable it at the start of the next xfer,
* till then let it sleep.
*/
if (_intel_wait_for(dev,
(I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
10, 1, "915gbu")) {
if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
10)) {
DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n",
sc->name);
error = -ETIMEDOUT;
device_get_desc(adapter));
ret = -ETIMEDOUT;
}
I915_WRITE(GMBUS0 + reg_offset, 0);
goto out;
@ -421,11 +422,22 @@ gmbus_xfer(device_t adapter,
* Wait for bus to IDLE before clearing NAK.
* If we clear the NAK while bus is still active, then it will stay
* active and the next transaction may fail.
*
* If no ACK is received during the address phase of a transaction, the
* adapter must report -ENXIO. It is not clear what to return if no ACK
* is received at other times. But we have to be careful to not return
* spurious -ENXIO because that will prevent i2c and drm edid functions
* from retrying. So return -ENXIO only when gmbus properly quiescents -
* timing out seems to happen when there _is_ a ddc chip present, but
* it's slow responding and only answers on the 2nd retry.
*/
if (_intel_wait_for(dev,
(I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
10, 1, "915gbu"))
DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n", sc->name);
ret = -ENXIO;
if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
10)) {
DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n",
device_get_desc(adapter));
ret = -ETIMEDOUT;
}
/* Toggle the Software Clear Interrupt bit. This has the effect
* of resetting the GMBUS controller and so clearing the
@ -436,31 +448,23 @@ gmbus_xfer(device_t adapter,
I915_WRITE(GMBUS0 + reg_offset, 0);
DRM_DEBUG_KMS("GMBUS [%s] NAK for addr: %04x %c(%d)\n",
sc->name, msgs[i].slave,
(msgs[i].flags & IIC_M_RD) ? 'r' : 'w', msgs[i].len);
device_get_desc(adapter), msgs[i].slave >> 1,
(msgs[i].flags & I2C_M_RD) ? 'r' : 'w', msgs[i].len);
/*
* If no ACK is received during the address phase of a transaction,
* the adapter must report -ENXIO.
* It is not clear what to return if no ACK is received at other times.
* So, we always return -ENXIO in all NAK cases, to ensure we send
* it at least during the one case that is specified.
*/
error = -ENXIO;
goto out;
timeout:
DRM_INFO("GMBUS [%s] timed out, falling back to bit banging on pin %d\n",
sc->name, sc->reg0 & 0xff);
device_get_desc(adapter), bus->reg0 & 0xff);
I915_WRITE(GMBUS0 + reg_offset, 0);
/* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
sc->force_bit_dev = true;
error = -IICBUS_TRANSFER(dev_priv->bbbus[unit], msgs, num);
bus->force_bit = 1;
ret = -IICBUS_TRANSFER(bus->bbbus, msgs, num);
out:
sx_xunlock(&dev_priv->gmbus_sx);
return -error;
sx_xunlock(&dev_priv->gmbus_mutex);
return -ret;
}
static int
@ -473,32 +477,23 @@ intel_gmbus_probe(device_t dev)
static int
intel_gmbus_attach(device_t idev)
{
struct drm_i915_private *dev_priv;
struct intel_iic_softc *sc;
struct drm_device *dev;
struct drm_i915_private *dev_priv;
int pin, port;
sc = device_get_softc(idev);
sc->drm_dev = device_get_softc(device_get_parent(idev));
dev_priv = sc->drm_dev->dev_private;
pin = device_get_unit(idev);
port = pin + 1;
port = pin + 1; /* +1 to map gmbus index to pin pair */
snprintf(sc->name, sizeof(sc->name), "gmbus %s",
snprintf(sc->name, sizeof(sc->name), "i915 gmbus %s",
intel_gmbus_is_port_valid(port) ? gmbus_ports[pin].name :
"reserved");
device_set_desc(idev, sc->name);
/* By default use a conservative clock rate */
sc->reg0 = port | GMBUS_RATE_100KHZ;
/* gmbus seems to be broken on i830 */
if (IS_I830(sc->drm_dev))
sc->force_bit_dev = true;
#if 0
if (IS_GEN2(sc->drm_dev)) {
sc->force_bit_dev = true;
}
#endif
dev = device_get_softc(device_get_parent(idev));
dev_priv = dev->dev_private;
sc->bus = &dev_priv->gmbus[pin];
/* add bus interface device */
sc->iic_dev = device_add_child(idev, "iicbus", -1);
@ -513,78 +508,13 @@ intel_gmbus_attach(device_t idev)
static int
intel_gmbus_detach(device_t idev)
{
struct intel_iic_softc *sc;
struct drm_i915_private *dev_priv;
device_t child;
int u;
sc = device_get_softc(idev);
u = device_get_unit(idev);
dev_priv = sc->drm_dev->dev_private;
child = sc->iic_dev;
bus_generic_detach(idev);
if (child != NULL)
device_delete_child(idev, child);
device_delete_children(idev);
return (0);
}
static int
intel_iicbb_probe(device_t dev)
{
return (BUS_PROBE_DEFAULT);
}
static int
intel_iicbb_attach(device_t idev)
{
struct intel_iic_softc *sc;
struct drm_i915_private *dev_priv;
int pin, port;
sc = device_get_softc(idev);
sc->drm_dev = device_get_softc(device_get_parent(idev));
dev_priv = sc->drm_dev->dev_private;
pin = device_get_unit(idev);
port = pin + 1;
snprintf(sc->name, sizeof(sc->name), "i915 iicbb %s",
intel_gmbus_is_port_valid(port) ? gmbus_ports[pin].name :
"reserved");
device_set_desc(idev, sc->name);
if (!intel_gmbus_is_port_valid(port))
pin = 1 ; /* GPIOA, VGA */
sc->reg0 = pin | GMBUS_RATE_100KHZ;
sc->reg = dev_priv->gpio_mmio_base + gmbus_ports[pin].reg;
/* add generic bit-banging code */
sc->iic_dev = device_add_child(idev, "iicbb", -1);
if (sc->iic_dev == NULL)
return (ENXIO);
device_quiet(sc->iic_dev);
bus_generic_attach(idev);
iicbus_set_nostop(idev, true);
return (0);
}
static int
intel_iicbb_detach(device_t idev)
{
struct intel_iic_softc *sc;
device_t child;
sc = device_get_softc(idev);
child = sc->iic_dev;
bus_generic_detach(idev);
if (child)
device_delete_child(idev, child);
return (0);
}
static device_method_t intel_gmbus_methods[] = {
DEVMETHOD(device_probe, intel_gmbus_probe),
DEVMETHOD(device_attach, intel_gmbus_attach),
@ -603,6 +533,55 @@ DRIVER_MODULE_ORDERED(intel_gmbus, drmn, intel_gmbus_driver,
intel_gmbus_devclass, 0, 0, SI_ORDER_FIRST);
DRIVER_MODULE(iicbus, intel_gmbus, iicbus_driver, iicbus_devclass, 0, 0);
static int
intel_iicbb_probe(device_t dev)
{
return (BUS_PROBE_DEFAULT);
}
static int
intel_iicbb_attach(device_t idev)
{
struct intel_iic_softc *sc;
struct drm_device *dev;
struct drm_i915_private *dev_priv;
int pin, port;
sc = device_get_softc(idev);
pin = device_get_unit(idev);
port = pin + 1;
snprintf(sc->name, sizeof(sc->name), "i915 iicbb %s",
intel_gmbus_is_port_valid(port) ? gmbus_ports[pin].name :
"reserved");
device_set_desc(idev, sc->name);
dev = device_get_softc(device_get_parent(idev));
dev_priv = dev->dev_private;
sc->bus = &dev_priv->gmbus[pin];
/* add generic bit-banging code */
sc->iic_dev = device_add_child(idev, "iicbb", -1);
if (sc->iic_dev == NULL)
return (ENXIO);
device_quiet(sc->iic_dev);
bus_generic_attach(idev);
iicbus_set_nostop(idev, true);
return (0);
}
static int
intel_iicbb_detach(device_t idev)
{
bus_generic_detach(idev);
device_delete_children(idev);
return (0);
}
static device_method_t intel_iicbb_methods[] = {
DEVMETHOD(device_probe, intel_iicbb_probe),
DEVMETHOD(device_attach, intel_iicbb_attach),
@ -631,6 +610,10 @@ DRIVER_MODULE_ORDERED(intel_iicbb, drmn, intel_iicbb_driver,
intel_iicbb_devclass, 0, 0, SI_ORDER_FIRST);
DRIVER_MODULE(iicbb, intel_iicbb, iicbb_driver, iicbb_devclass, 0, 0);
/**
* intel_gmbus_setup - instantiate all Intel i2c GMBuses
* @dev: DRM device
*/
int intel_setup_gmbus(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@ -642,7 +625,7 @@ int intel_setup_gmbus(struct drm_device *dev)
else
dev_priv->gpio_mmio_base = 0;
sx_init(&dev_priv->gmbus_sx, "gmbus");
sx_init(&dev_priv->gmbus_mutex, "gmbus");
/*
* The Giant there is recursed, most likely. Normally, the
@ -650,29 +633,46 @@ int intel_setup_gmbus(struct drm_device *dev)
* driver.
*/
mtx_lock(&Giant);
for (i = 0; i <= GMBUS_NUM_PORTS; i++) {
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
u32 port = i + 1; /* +1 to map gmbus index to pin pair */
bus->dev_priv = dev_priv;
/* By default use a conservative clock rate */
bus->reg0 = port | GMBUS_RATE_100KHZ;
/* gmbus seems to be broken on i830 */
if (IS_I830(dev))
bus->force_bit = 1;
intel_gpio_setup(bus, port);
/*
* bbbus_bridge
*
* Initialized bbbus_bridge before gmbus_bridge, since
* gmbus may decide to force quirk transfer in the
* attachment code.
*/
dev_priv->bbbus_bridge[i] = device_add_child(dev->dev,
bus->bbbus_bridge = device_add_child(dev->dev,
"intel_iicbb", i);
if (dev_priv->bbbus_bridge[i] == NULL) {
if (bus->bbbus_bridge == NULL) {
DRM_ERROR("bbbus bridge %d creation failed\n", i);
ret = -ENXIO;
goto err;
}
device_quiet(dev_priv->bbbus_bridge[i]);
ret = -device_probe_and_attach(dev_priv->bbbus_bridge[i]);
device_quiet(bus->bbbus_bridge);
ret = -device_probe_and_attach(bus->bbbus_bridge);
if (ret != 0) {
DRM_ERROR("bbbus bridge %d attach failed, %d\n", i,
ret);
goto err;
}
iic_dev = device_find_child(dev_priv->bbbus_bridge[i], "iicbb",
-1);
/* bbbus */
iic_dev = device_find_child(bus->bbbus_bridge,
"iicbb", -1);
if (iic_dev == NULL) {
DRM_ERROR("bbbus bridge doesn't have iicbb child\n");
goto err;
@ -684,17 +684,18 @@ int intel_setup_gmbus(struct drm_device *dev)
goto err;
}
dev_priv->bbbus[i] = iic_dev;
bus->bbbus = iic_dev;
dev_priv->gmbus_bridge[i] = device_add_child(dev->dev,
/* gmbus_bridge */
bus->gmbus_bridge = device_add_child(dev->dev,
"intel_gmbus", i);
if (dev_priv->gmbus_bridge[i] == NULL) {
if (bus->gmbus_bridge == NULL) {
DRM_ERROR("gmbus bridge %d creation failed\n", i);
ret = -ENXIO;
goto err;
}
device_quiet(dev_priv->gmbus_bridge[i]);
ret = -device_probe_and_attach(dev_priv->gmbus_bridge[i]);
device_quiet(bus->gmbus_bridge);
ret = -device_probe_and_attach(bus->gmbus_bridge);
if (ret != 0) {
DRM_ERROR("gmbus bridge %d attach failed, %d\n", i,
ret);
@ -702,67 +703,84 @@ int intel_setup_gmbus(struct drm_device *dev)
goto err;
}
iic_dev = device_find_child(dev_priv->gmbus_bridge[i],
/* gmbus */
iic_dev = device_find_child(bus->gmbus_bridge,
"iicbus", -1);
if (iic_dev == NULL) {
DRM_ERROR("gmbus bridge doesn't have iicbus child\n");
goto err;
}
dev_priv->gmbus[i] = iic_dev;
intel_iic_reset(dev);
bus->gmbus = iic_dev;
}
mtx_unlock(&Giant);
intel_i2c_reset(dev_priv->dev);
return 0;
err:
intel_teardown_gmbus_m(dev, i);
while (--i) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
if (bus->gmbus_bridge != NULL)
device_delete_child(dev->dev, bus->gmbus_bridge);
if (bus->bbbus_bridge != NULL)
device_delete_child(dev->dev, bus->bbbus_bridge);
}
mtx_unlock(&Giant);
sx_destroy(&dev_priv->gmbus_mutex);
return ret;
}
device_t intel_gmbus_get_adapter(struct drm_i915_private *dev_priv,
unsigned port)
{
if (!intel_gmbus_is_port_valid(port))
DRM_ERROR("GMBUS get adapter %d: invalid port\n", port);
return (intel_gmbus_is_port_valid(port) ? dev_priv->gmbus[port - 1] :
NULL);
WARN_ON(!intel_gmbus_is_port_valid(port));
/* -1 to map pin pair to gmbus index */
return (intel_gmbus_is_port_valid(port)) ?
dev_priv->gmbus[port - 1].gmbus : NULL;
}
void intel_gmbus_set_speed(device_t adapter, int speed)
{
struct intel_iic_softc *sc;
struct intel_gmbus *bus = to_intel_gmbus(adapter);
sc = device_get_softc(device_get_parent(adapter));
sc->reg0 = (sc->reg0 & ~(0x3 << 8)) | speed;
bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed;
}
void intel_gmbus_force_bit(device_t adapter, bool force_bit)
{
struct intel_iic_softc *sc;
struct intel_gmbus *bus = to_intel_gmbus(adapter);
sc = device_get_softc(device_get_parent(adapter));
sc->force_bit_dev = force_bit;
}
static void
intel_teardown_gmbus_m(struct drm_device *dev, int m)
{
struct drm_i915_private *dev_priv;
dev_priv = dev->dev_private;
sx_destroy(&dev_priv->gmbus_sx);
bus->force_bit += force_bit ? 1 : -1;
DRM_DEBUG_KMS("%sabling bit-banging on %s. force bit now %d\n",
force_bit ? "en" : "dis", device_get_desc(adapter),
bus->force_bit);
}
void intel_teardown_gmbus(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
int ret;
mtx_lock(&Giant);
intel_teardown_gmbus_m(dev, GMBUS_NUM_PORTS);
mtx_unlock(&Giant);
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
mtx_lock(&Giant);
ret = device_delete_child(dev->dev, bus->gmbus_bridge);
mtx_unlock(&Giant);
KASSERT(ret == 0, ("unable to detach iic gmbus %s: %d",
device_get_desc(bus->gmbus_bridge), ret));
mtx_lock(&Giant);
ret = device_delete_child(dev->dev, bus->bbbus_bridge);
mtx_unlock(&Giant);
KASSERT(ret == 0, ("unable to detach iic bbbus %s: %d",
device_get_desc(bus->bbbus_bridge), ret));
}
sx_destroy(&dev_priv->gmbus_mutex);
}

View File

@ -31,44 +31,75 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_edid.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
/* Private structure for the integrated LVDS support */
struct intel_lvds {
struct intel_lvds_connector {
struct intel_connector base;
#ifdef FREEBSD_WIP
struct notifier_block lid_notifier;
#endif /* FREEBSD_WIP */
};
struct intel_lvds_encoder {
struct intel_encoder base;
struct edid *edid;
int fitting_mode;
u32 pfit_control;
u32 pfit_pgm_ratios;
bool pfit_dirty;
struct drm_display_mode *fixed_mode;
struct intel_lvds_connector *attached_connector;
};
static struct intel_lvds *to_intel_lvds(struct drm_encoder *encoder)
static struct intel_lvds_encoder *to_lvds_encoder(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_lvds, base.base);
return container_of(encoder, struct intel_lvds_encoder, base.base);
}
static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
static struct intel_lvds_connector *to_lvds_connector(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_lvds, base);
return container_of(connector, struct intel_lvds_connector, base.base);
}
static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 lvds_reg, tmp;
if (HAS_PCH_SPLIT(dev)) {
lvds_reg = PCH_LVDS;
} else {
lvds_reg = LVDS;
}
tmp = I915_READ(lvds_reg);
if (!(tmp & LVDS_PORT_EN))
return false;
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
return true;
}
/**
* Sets the power state for the panel.
*/
static void intel_lvds_enable(struct intel_lvds *intel_lvds)
static void intel_enable_lvds(struct intel_encoder *encoder)
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_device *dev = encoder->base.dev;
struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
struct drm_i915_private *dev_priv = dev->dev_private;
u32 ctl_reg, lvds_reg, stat_reg;
@ -84,7 +115,7 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds)
I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
if (intel_lvds->pfit_dirty) {
if (lvds_encoder->pfit_dirty) {
/*
* Enable automatic panel scaling so that non-native modes
* fill the screen. The panel fitter should only be
@ -92,27 +123,26 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds)
* register description and PRM.
*/
DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
intel_lvds->pfit_control,
intel_lvds->pfit_pgm_ratios);
lvds_encoder->pfit_control,
lvds_encoder->pfit_pgm_ratios);
I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
intel_lvds->pfit_dirty = false;
I915_WRITE(PFIT_PGM_RATIOS, lvds_encoder->pfit_pgm_ratios);
I915_WRITE(PFIT_CONTROL, lvds_encoder->pfit_control);
lvds_encoder->pfit_dirty = false;
}
I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
POSTING_READ(lvds_reg);
if (_intel_wait_for(dev,
(I915_READ(stat_reg) & PP_ON) == 0, 1000,
1, "915lvds"))
DRM_ERROR("timed out waiting for panel to power off\n");
if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000))
DRM_ERROR("timed out waiting for panel to power on\n");
intel_panel_enable_backlight(dev);
intel_panel_enable_backlight(dev, intel_crtc->pipe);
}
static void intel_lvds_disable(struct intel_lvds *intel_lvds)
static void intel_disable_lvds(struct intel_encoder *encoder)
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_device *dev = encoder->base.dev;
struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
struct drm_i915_private *dev_priv = dev->dev_private;
u32 ctl_reg, lvds_reg, stat_reg;
@ -129,37 +159,23 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds)
intel_panel_disable_backlight(dev);
I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
if (_intel_wait_for(dev,
(I915_READ(stat_reg) & PP_ON) == 0, 1000,
1, "915lvo"))
if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000))
DRM_ERROR("timed out waiting for panel to power off\n");
if (intel_lvds->pfit_control) {
if (lvds_encoder->pfit_control) {
I915_WRITE(PFIT_CONTROL, 0);
intel_lvds->pfit_dirty = true;
lvds_encoder->pfit_dirty = true;
}
I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN);
POSTING_READ(lvds_reg);
}
static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
if (mode == DRM_MODE_DPMS_ON)
intel_lvds_enable(intel_lvds);
else
intel_lvds_disable(intel_lvds);
/* XXX: We never power down the LVDS pairs. */
}
static int intel_lvds_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode;
struct intel_connector *intel_connector = to_intel_connector(connector);
struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
if (mode->hdisplay > fixed_mode->hdisplay)
return MODE_PANEL;
@ -235,9 +251,10 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
struct drm_encoder *tmp_encoder;
struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
struct intel_connector *intel_connector =
&lvds_encoder->attached_connector->base;
struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc;
u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
int pipe;
@ -247,14 +264,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
return false;
}
/* Should never happen!! */
list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) {
if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) {
DRM_ERROR("Can't enable LVDS and another "
"encoder on the same pipe\n");
return false;
}
}
if (intel_encoder_check_is_cloned(&lvds_encoder->base))
return false;
/*
* We have timings from the BIOS for the panel, put them in
@ -262,10 +273,12 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
* with the panel scaling set up to source from the H/VDisplay
* of the original mode.
*/
intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode);
intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
adjusted_mode);
if (HAS_PCH_SPLIT(dev)) {
intel_pch_panel_fitting(dev, intel_lvds->fitting_mode,
intel_pch_panel_fitting(dev,
intel_connector->panel.fitting_mode,
mode, adjusted_mode);
return true;
}
@ -291,7 +304,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
drm_mode_set_crtcinfo(adjusted_mode, 0);
switch (intel_lvds->fitting_mode) {
switch (intel_connector->panel.fitting_mode) {
case DRM_MODE_SCALE_CENTER:
/*
* For centered modes, we have to calculate border widths &
@ -389,11 +402,11 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither)
pfit_control |= PANEL_8TO6_DITHER_ENABLE;
if (pfit_control != intel_lvds->pfit_control ||
pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) {
intel_lvds->pfit_control = pfit_control;
intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios;
intel_lvds->pfit_dirty = true;
if (pfit_control != lvds_encoder->pfit_control ||
pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) {
lvds_encoder->pfit_control = pfit_control;
lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios;
lvds_encoder->pfit_dirty = true;
}
dev_priv->lvds_border_bits = border;
@ -406,29 +419,6 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
return true;
}
static void intel_lvds_prepare(struct drm_encoder *encoder)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
/*
* Prior to Ironlake, we must disable the pipe if we want to adjust
* the panel fitter. However at all other times we can just reset
* the registers regardless.
*/
if (!HAS_PCH_SPLIT(encoder->dev) && intel_lvds->pfit_dirty)
intel_lvds_disable(intel_lvds);
}
static void intel_lvds_commit(struct drm_encoder *encoder)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
/* Always do a full power on as we do not know what state
* we were left in.
*/
intel_lvds_enable(intel_lvds);
}
static void intel_lvds_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -465,14 +455,15 @@ intel_lvds_detect(struct drm_connector *connector, bool force)
*/
static int intel_lvds_get_modes(struct drm_connector *connector)
{
struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct intel_lvds_connector *lvds_connector = to_lvds_connector(connector);
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode;
if (intel_lvds->edid)
return drm_add_edid_modes(connector, intel_lvds->edid);
/* use cached edid if we have one */
if (lvds_connector->base.edid)
return drm_add_edid_modes(connector, lvds_connector->base.edid);
mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode);
mode = drm_mode_duplicate(dev, lvds_connector->base.panel.fixed_mode);
if (mode == NULL)
return 0;
@ -500,7 +491,7 @@ static const struct dmi_system_id intel_no_modeset_on_lid[] = {
{ } /* terminating entry */
};
#ifdef NOTYET
#ifdef FREEBSD_WIP
/*
* Lid events. Note the use of 'modeset_on_lid':
* - we set it on lid close, and reset it on open
@ -513,10 +504,11 @@ static const struct dmi_system_id intel_no_modeset_on_lid[] = {
static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
void *unused)
{
struct drm_i915_private *dev_priv =
container_of(nb, struct drm_i915_private, lid_notifier);
struct drm_device *dev = dev_priv->dev;
struct drm_connector *connector = dev_priv->int_lvds_connector;
struct intel_lvds_connector *lvds_connector =
container_of(nb, struct intel_lvds_connector, lid_notifier);
struct drm_connector *connector = &lvds_connector->base.base;
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
return NOTIFY_OK;
@ -525,9 +517,7 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
* check and update the status of LVDS connector after receiving
* the LID nofication event.
*/
if (connector)
connector->status = connector->funcs->detect(connector,
false);
connector->status = connector->funcs->detect(connector, false);
/* Don't force modeset on machines where it causes a GPU lockup */
if (dmi_check_system(intel_no_modeset_on_lid))
@ -543,12 +533,12 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
dev_priv->modeset_on_lid = 0;
mutex_lock(&dev->mode_config.mutex);
drm_helper_resume_force_mode(dev);
intel_modeset_setup_hw_state(dev, true);
mutex_unlock(&dev->mode_config.mutex);
return NOTIFY_OK;
}
#endif
#endif /* FREEBSD_WIP */
/**
* intel_lvds_destroy - unregister and free LVDS structures
@ -559,20 +549,18 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
*/
static void intel_lvds_destroy(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
#if 0
struct drm_i915_private *dev_priv = dev->dev_private;
#endif
struct intel_lvds_connector *lvds_connector =
to_lvds_connector(connector);
intel_panel_destroy_backlight(dev);
#ifdef FREEBSD_WIP
if (lvds_connector->lid_notifier.notifier_call)
acpi_lid_notifier_unregister(&lvds_connector->lid_notifier);
#endif /* FREEBSD_WIP */
free(lvds_connector->base.edid, DRM_MEM_KMS);
intel_panel_fini(&lvds_connector->base.panel);
#if 0
if (dev_priv->lid_notifier.notifier_call)
acpi_lid_notifier_unregister(&dev_priv->lid_notifier);
#endif
#if 0
drm_sysfs_connector_remove(connector);
#endif
drm_connector_cleanup(connector);
free(connector, DRM_MEM_KMS);
}
@ -581,29 +569,31 @@ static int intel_lvds_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct intel_connector *intel_connector = to_intel_connector(connector);
struct drm_device *dev = connector->dev;
if (property == dev->mode_config.scaling_mode_property) {
struct drm_crtc *crtc = intel_lvds->base.base.crtc;
struct drm_crtc *crtc;
if (value == DRM_MODE_SCALE_NONE) {
DRM_DEBUG_KMS("no scaling not supported\n");
return -EINVAL;
}
if (intel_lvds->fitting_mode == value) {
if (intel_connector->panel.fitting_mode == value) {
/* the LVDS scaling property is not changed */
return 0;
}
intel_lvds->fitting_mode = value;
intel_connector->panel.fitting_mode = value;
crtc = intel_attached_encoder(connector)->base.crtc;
if (crtc && crtc->enabled) {
/*
* If the CRTC is enabled, the display will be changed
* according to the new panel fitting mode.
*/
drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb);
intel_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb);
}
}
@ -611,11 +601,9 @@ static int intel_lvds_set_property(struct drm_connector *connector,
}
static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
.dpms = intel_lvds_dpms,
.mode_fixup = intel_lvds_mode_fixup,
.prepare = intel_lvds_prepare,
.mode_set = intel_lvds_mode_set,
.commit = intel_lvds_commit,
.disable = intel_encoder_noop,
};
static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
@ -625,7 +613,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs
};
static const struct drm_connector_funcs intel_lvds_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = intel_connector_dpms,
.detect = intel_lvds_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_lvds_set_property,
@ -636,7 +624,7 @@ static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
.destroy = intel_encoder_destroy,
};
static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
static int __init intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
{
DRM_INFO("Skipping LVDS initialization for %s\n", id->ident);
return 1;
@ -755,6 +743,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Hewlett-Packard HP t5740e Thin Client",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP t5740e Thin Client"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Hewlett-Packard t5745",
@ -779,6 +775,30 @@ static const struct dmi_system_id intel_no_lvds[] = {
DMI_MATCH(DMI_BOARD_NAME, "MS-7469"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Gigabyte GA-D525TUD",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
DMI_MATCH(DMI_BOARD_NAME, "D525TUD"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Supermicro X7SPA-H",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Fujitsu Esprimo Q900",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
},
},
{ } /* terminating entry */
};
@ -906,12 +926,16 @@ static bool intel_lvds_supported(struct drm_device *dev)
bool intel_lvds_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds;
struct intel_lvds_encoder *lvds_encoder;
struct intel_encoder *intel_encoder;
struct intel_lvds_connector *lvds_connector;
struct intel_connector *intel_connector;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct drm_display_mode *scan; /* *modes, *bios_mode; */
struct drm_display_mode *fixed_mode = NULL;
struct edid *edid;
int edid_err = 0;
struct drm_crtc *crtc;
u32 lvds;
int pipe;
@ -939,17 +963,25 @@ bool intel_lvds_init(struct drm_device *dev)
}
}
intel_lvds = malloc(sizeof(struct intel_lvds), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
lvds_encoder = malloc(sizeof(struct intel_lvds_encoder), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!lvds_encoder)
return false;
if (!HAS_PCH_SPLIT(dev)) {
intel_lvds->pfit_control = I915_READ(PFIT_CONTROL);
lvds_connector = malloc(sizeof(struct intel_lvds_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!lvds_connector) {
free(lvds_encoder, DRM_MEM_KMS);
return false;
}
intel_encoder = &intel_lvds->base;
lvds_encoder->attached_connector = lvds_connector;
if (!HAS_PCH_SPLIT(dev)) {
lvds_encoder->pfit_control = I915_READ(PFIT_CONTROL);
}
intel_encoder = &lvds_encoder->base;
encoder = &intel_encoder->base;
intel_connector = &lvds_connector->base;
connector = &intel_connector->base;
drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
@ -957,12 +989,19 @@ bool intel_lvds_init(struct drm_device *dev)
drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
DRM_MODE_ENCODER_LVDS);
intel_encoder->enable = intel_enable_lvds;
intel_encoder->disable = intel_disable_lvds;
intel_encoder->get_hw_state = intel_lvds_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_LVDS;
intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
intel_encoder->cloneable = false;
if (HAS_PCH_SPLIT(dev))
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
else if (IS_GEN4(dev))
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
else
intel_encoder->crtc_mask = (1 << 1);
@ -974,14 +1013,10 @@ bool intel_lvds_init(struct drm_device *dev)
/* create the scaling mode property */
drm_mode_create_scaling_mode_property(dev);
/*
* the initial panel fitting mode will be FULL_SCREEN.
*/
drm_object_attach_property(&connector->base,
dev->mode_config.scaling_mode_property,
DRM_MODE_SCALE_ASPECT);
intel_lvds->fitting_mode = DRM_MODE_SCALE_ASPECT;
intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT;
/*
* LVDS discovery:
* 1) check for EDID on DDC
@ -996,19 +1031,24 @@ bool intel_lvds_init(struct drm_device *dev)
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
intel_lvds->edid = drm_get_edid(connector,
intel_gmbus_get_adapter(dev_priv, pin));
if (intel_lvds->edid) {
if (drm_add_edid_modes(connector,
intel_lvds->edid)) {
edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, pin));
if (edid) {
if (drm_add_edid_modes(connector, edid)) {
drm_mode_connector_update_edid_property(connector,
intel_lvds->edid);
edid);
} else {
free(intel_lvds->edid, DRM_MEM_KMS);
intel_lvds->edid = NULL;
free(edid, DRM_MEM_KMS);
edid = NULL;
edid_err = -EINVAL;
}
} else {
edid = NULL;
edid_err = -ENOENT;
}
if (!intel_lvds->edid) {
lvds_connector->base.edid = edid;
lvds_connector->base.edid_err = edid_err;
if (edid_err) {
/* Didn't get an EDID, so
* Set wide sync ranges so we get all modes
* handed to valid_mode for checking
@ -1021,22 +1061,26 @@ bool intel_lvds_init(struct drm_device *dev)
list_for_each_entry(scan, &connector->probed_modes, head) {
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
intel_lvds->fixed_mode =
drm_mode_duplicate(dev, scan);
intel_find_lvds_downclock(dev,
intel_lvds->fixed_mode,
connector);
goto out;
DRM_DEBUG_KMS("using preferred mode from EDID: ");
drm_mode_debug_printmodeline(scan);
fixed_mode = drm_mode_duplicate(dev, scan);
if (fixed_mode) {
intel_find_lvds_downclock(dev, fixed_mode,
connector);
goto out;
}
}
}
/* Failed to get EDID, what about VBT? */
if (dev_priv->lfp_lvds_vbt_mode) {
intel_lvds->fixed_mode =
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
if (intel_lvds->fixed_mode) {
intel_lvds->fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
DRM_DEBUG_KMS("using mode from VBT: ");
drm_mode_debug_printmodeline(dev_priv->lfp_lvds_vbt_mode);
fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
if (fixed_mode) {
fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
goto out;
}
}
@ -1056,71 +1100,51 @@ bool intel_lvds_init(struct drm_device *dev)
crtc = intel_get_crtc_for_pipe(dev, pipe);
if (crtc && (lvds & LVDS_PORT_EN)) {
intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc);
if (intel_lvds->fixed_mode) {
intel_lvds->fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
fixed_mode = intel_crtc_mode_get(dev, crtc);
if (fixed_mode) {
DRM_DEBUG_KMS("using current (BIOS) mode: ");
drm_mode_debug_printmodeline(fixed_mode);
fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
goto out;
}
}
/* If we still don't have a mode after all that, give up. */
if (!intel_lvds->fixed_mode)
if (!fixed_mode)
goto failed;
out:
/*
* Unlock registers and just
* leave them unlocked
*/
if (HAS_PCH_SPLIT(dev)) {
u32 pwm;
pipe = (I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT) ? 1 : 0;
/* make sure PWM is enabled and locked to the LVDS pipe */
pwm = I915_READ(BLC_PWM_CPU_CTL2);
if (pipe == 0 && (pwm & PWM_PIPE_B))
I915_WRITE(BLC_PWM_CPU_CTL2, pwm & ~PWM_ENABLE);
if (pipe)
pwm |= PWM_PIPE_B;
else
pwm &= ~PWM_PIPE_B;
I915_WRITE(BLC_PWM_CPU_CTL2, pwm | PWM_ENABLE);
pwm = I915_READ(BLC_PWM_PCH_CTL1);
pwm |= PWM_PCH_ENABLE;
I915_WRITE(BLC_PWM_PCH_CTL1, pwm);
/*
* Unlock registers and just
* leave them unlocked
*/
I915_WRITE(PCH_PP_CONTROL,
I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
} else {
/*
* Unlock registers and just
* leave them unlocked
*/
I915_WRITE(PP_CONTROL,
I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
}
#ifdef NOTYET
dev_priv->lid_notifier.notifier_call = intel_lid_notify;
if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) {
#ifdef FREEBSD_WIP
lvds_connector->lid_notifier.notifier_call = intel_lid_notify;
if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) {
DRM_DEBUG_KMS("lid notifier registration failed\n");
dev_priv->lid_notifier.notifier_call = NULL;
lvds_connector->lid_notifier.notifier_call = NULL;
}
#endif
/* keep the LVDS connector */
dev_priv->int_lvds_connector = connector;
#if 0
drm_sysfs_connector_add(connector);
#endif
intel_panel_setup_backlight(dev);
#endif /* FREEBSD_WIP */
intel_panel_init(&intel_connector->panel, fixed_mode);
intel_panel_setup_backlight(connector);
return true;
failed:
DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
drm_connector_cleanup(connector);
drm_encoder_cleanup(encoder);
free(intel_lvds, DRM_MEM_KMS);
free(intel_connector, DRM_MEM_KMS);
if (fixed_mode)
drm_mode_destroy(dev, fixed_mode);
free(lvds_encoder, DRM_MEM_KMS);
free(lvds_connector, DRM_MEM_KMS);
return false;
}

View File

@ -27,39 +27,26 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/drm_edid.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/iicbus/iiconf.h>
/**
* intel_ddc_probe
*
* intel_connector_update_modes - update connector from edid
* @connector: DRM connector device to use
* @edid: previously read EDID information
*/
bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus)
int intel_connector_update_modes(struct drm_connector *connector,
struct edid *edid)
{
struct drm_i915_private *dev_priv = intel_encoder->base.dev->dev_private;
u8 out_buf[] = { 0x0, 0x0};
u8 buf[2];
struct iic_msg msgs[] = {
{
.slave = DDC_ADDR << 1,
.flags = IIC_M_WR,
.len = 1,
.buf = out_buf,
},
{
.slave = DDC_ADDR << 1,
.flags = IIC_M_RD,
.len = 1,
.buf = buf,
}
};
int ret;
return (iicbus_transfer(intel_gmbus_get_adapter(dev_priv, ddc_bus),
msgs, 2) == 0/* XXXKIB 2*/);
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
drm_edid_to_eld(connector, edid);
return ret;
}
/**
@ -69,19 +56,18 @@ bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus)
*
* Fetch the EDID information from @connector using the DDC bus.
*/
int
intel_ddc_get_modes(struct drm_connector *connector, device_t adapter)
int intel_ddc_get_modes(struct drm_connector *connector,
device_t adapter)
{
struct edid *edid;
int ret = 0;
int ret;
edid = drm_get_edid(connector, adapter);
if (edid) {
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
drm_edid_to_eld(connector, edid);
free(edid, DRM_MEM_KMS);
}
if (!edid)
return 0;
ret = intel_connector_update_modes(connector, edid);
free(edid, DRM_MEM_KMS);
return ret;
}
@ -129,9 +115,9 @@ intel_attach_broadcast_rgb_property(struct drm_connector *connector)
prop = dev_priv->broadcast_rgb_property;
if (prop == NULL) {
prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
"Broadcast RGB",
broadcast_rgb_names,
ARRAY_SIZE(broadcast_rgb_names));
"Broadcast RGB",
broadcast_rgb_names,
ARRAY_SIZE(broadcast_rgb_names));
if (prop == NULL)
return;

View File

@ -28,10 +28,13 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <dev/drm2/drmP.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <contrib/dev/acpica/include/accommon.h>
#include <dev/acpica/acpivar.h>
@ -147,13 +150,15 @@ struct opregion_asle {
#define ACPI_DIGITAL_OUTPUT (3<<8)
#define ACPI_LVDS_OUTPUT (4<<8)
#if defined(CONFIG_ACPI)
#ifdef CONFIG_ACPI
static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
u32 max;
DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp);
if (!(bclp & ASLE_BCLP_VALID))
return ASLE_BACKLIGHT_FAILED;
@ -163,7 +168,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
max = intel_panel_get_max_backlight(dev);
intel_panel_set_backlight(dev, bclp * max / 255);
asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
iowrite32((bclp*0x64)/0xff | ASLE_CBLV_VALID, &asle->cblv);
return 0;
}
@ -200,71 +205,71 @@ static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
void intel_opregion_asle_intr(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
u32 asle_stat = 0;
u32 asle_req;
if (!asle)
return;
asle_req = asle->aslc & ASLE_REQ_MSK;
asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK;
if (!asle_req) {
DRM_DEBUG("non asle set request??\n");
DRM_DEBUG_DRIVER("non asle set request??\n");
return;
}
if (asle_req & ASLE_SET_ALS_ILLUM)
asle_stat |= asle_set_als_illum(dev, asle->alsi);
asle_stat |= asle_set_als_illum(dev, ioread32(&asle->alsi));
if (asle_req & ASLE_SET_BACKLIGHT)
asle_stat |= asle_set_backlight(dev, asle->bclp);
asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp));
if (asle_req & ASLE_SET_PFIT)
asle_stat |= asle_set_pfit(dev, asle->pfit);
asle_stat |= asle_set_pfit(dev, ioread32(&asle->pfit));
if (asle_req & ASLE_SET_PWM_FREQ)
asle_stat |= asle_set_pwm_freq(dev, asle->pfmb);
asle_stat |= asle_set_pwm_freq(dev, ioread32(&asle->pfmb));
asle->aslc = asle_stat;
iowrite32(asle_stat, &asle->aslc);
}
void intel_opregion_gse_intr(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
u32 asle_stat = 0;
u32 asle_req;
if (!asle)
return;
asle_req = asle->aslc & ASLE_REQ_MSK;
asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK;
if (!asle_req) {
DRM_DEBUG("non asle set request??\n");
DRM_DEBUG_DRIVER("non asle set request??\n");
return;
}
if (asle_req & ASLE_SET_ALS_ILLUM) {
DRM_DEBUG("Illum is not supported\n");
DRM_DEBUG_DRIVER("Illum is not supported\n");
asle_stat |= ASLE_ALS_ILLUM_FAILED;
}
if (asle_req & ASLE_SET_BACKLIGHT)
asle_stat |= asle_set_backlight(dev, asle->bclp);
asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp));
if (asle_req & ASLE_SET_PFIT) {
DRM_DEBUG("Pfit is not supported\n");
DRM_DEBUG_DRIVER("Pfit is not supported\n");
asle_stat |= ASLE_PFIT_FAILED;
}
if (asle_req & ASLE_SET_PWM_FREQ) {
DRM_DEBUG("PWM freq is not supported\n");
DRM_DEBUG_DRIVER("PWM freq is not supported\n");
asle_stat |= ASLE_PWM_FREQ_FAILED;
}
asle->aslc = asle_stat;
iowrite32(asle_stat, &asle->aslc);
}
#define ASLE_ALS_EN (1<<0)
#define ASLE_BLC_EN (1<<1)
@ -274,15 +279,16 @@ void intel_opregion_gse_intr(struct drm_device *dev)
void intel_opregion_enable_asle(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
if (asle) {
if (IS_MOBILE(dev))
intel_enable_asle(dev);
asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |
ASLE_PFMB_EN;
asle->ardy = 1;
iowrite32(ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |
ASLE_PFMB_EN,
&asle->tche);
iowrite32(1, &asle->ardy);
}
}
@ -292,7 +298,7 @@ void intel_opregion_enable_asle(struct drm_device *dev)
static struct intel_opregion *system_opregion;
#if 0
#ifdef FREEBSD_WIP
static int intel_opregion_video_event(struct notifier_block *nb,
unsigned long val, void *data)
{
@ -300,7 +306,8 @@ static int intel_opregion_video_event(struct notifier_block *nb,
either a docking event, lid switch or display switch request. In
Linux, these are handled by the dock, button and video drivers.
*/
struct opregion_acpi *acpi;
struct opregion_acpi __iomem *acpi;
struct acpi_bus_event *event = data;
int ret = NOTIFY_OK;
@ -312,10 +319,11 @@ static int intel_opregion_video_event(struct notifier_block *nb,
acpi = system_opregion->acpi;
if (event->type == 0x80 && !(acpi->cevt & 0x1))
if (event->type == 0x80 &&
(ioread32(&acpi->cevt) & 1) == 0)
ret = NOTIFY_BAD;
acpi->csts = 0;
iowrite32(0, &acpi->csts);
return ret;
}
@ -323,7 +331,7 @@ static int intel_opregion_video_event(struct notifier_block *nb,
static struct notifier_block intel_opregion_notifier = {
.notifier_call = intel_opregion_video_event,
};
#endif
#endif /* FREEBSD_WIP */
/*
* Initialise the DIDL field in opregion. This passes a list of devices to
@ -345,9 +353,10 @@ static void intel_didl_outputs(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
struct drm_connector *connector;
ACPI_HANDLE handle, acpi_cdev, acpi_video_bus = NULL;
u32 device_id;
ACPI_HANDLE handle, acpi_video_bus, acpi_cdev;
ACPI_STATUS status;
u32 temp;
int i = 0;
handle = acpi_get_handle(dev->dev);
@ -358,7 +367,6 @@ static void intel_didl_outputs(struct drm_device *dev)
acpi_video_bus = handle;
else {
acpi_cdev = NULL;
acpi_video_bus = NULL;
while (AcpiGetNextObject(ACPI_TYPE_DEVICE, handle, acpi_cdev,
&acpi_cdev) != AE_NOT_FOUND) {
if (acpi_is_video_device(acpi_cdev)) {
@ -366,14 +374,6 @@ static void intel_didl_outputs(struct drm_device *dev)
break;
}
}
#if 0
list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {
if (acpi_is_video_device(acpi_cdev)) {
acpi_video_bus = acpi_cdev;
break;
}
}
#endif
}
if (!acpi_video_bus) {
@ -388,37 +388,22 @@ static void intel_didl_outputs(struct drm_device *dev)
device_printf(dev->dev, "More than 8 outputs detected\n");
return;
}
status = acpi_GetInteger(acpi_cdev, "_ADR", &device_id);
if (ACPI_SUCCESS(status)) {
if (!device_id)
goto blind_set;
opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f);
i++;
}
}
#if 0
list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
if (i >= 8) {
dev_printk(KERN_ERR, &dev->pdev->dev,
"More than 8 outputs detected\n");
return;
}
status =
acpi_evaluate_integer(acpi_cdev->handle, "_ADR",
NULL, &device_id);
acpi_GetInteger(acpi_cdev, "_ADR",
&device_id);
if (ACPI_SUCCESS(status)) {
if (!device_id)
goto blind_set;
opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f);
iowrite32((u32)(device_id & 0x0f0f),
&opregion->acpi->didl[i]);
i++;
}
}
#endif
end:
/* If fewer than 8 outputs, the list must be null terminated */
if (i < 8)
opregion->acpi->didl[i] = 0;
iowrite32(0, &opregion->acpi->didl[i]);
return;
blind_set:
@ -452,7 +437,9 @@ static void intel_didl_outputs(struct drm_device *dev)
output_type = ACPI_LVDS_OUTPUT;
break;
}
opregion->acpi->didl[i] |= (1<<31) | output_type | i;
temp = ioread32(&opregion->acpi->didl[i]);
iowrite32(temp | (1<<31) | output_type | i,
&opregion->acpi->didl[i]);
i++;
}
goto end;
@ -472,8 +459,8 @@ static void intel_setup_cadls(struct drm_device *dev)
* display switching hotkeys. Just like DIDL, CADL is NULL-terminated if
* there are less than eight devices. */
do {
disp_id = opregion->acpi->didl[i];
opregion->acpi->cadl[i] = disp_id;
disp_id = ioread32(&opregion->acpi->didl[i]);
iowrite32(disp_id, &opregion->acpi->cadl[i]);
} while (++i < 8 && disp_id != 0);
}
@ -494,13 +481,13 @@ void intel_opregion_init(struct drm_device *dev)
/* Notify BIOS we are ready to handle ACPI video ext notifs.
* Right now, all the events are handled by the ACPI video module.
* We don't actually need to do anything with them. */
opregion->acpi->csts = 0;
opregion->acpi->drdy = 1;
iowrite32(0, &opregion->acpi->csts);
iowrite32(1, &opregion->acpi->drdy);
system_opregion = opregion;
#if 0
#ifdef FREEBSD_WIP
register_acpi_notifier(&intel_opregion_notifier);
#endif
#endif /* FREEBSD_WIP */
}
if (opregion->asle)
@ -516,12 +503,12 @@ void intel_opregion_fini(struct drm_device *dev)
return;
if (opregion->acpi) {
opregion->acpi->drdy = 0;
iowrite32(0, &opregion->acpi->drdy);
system_opregion = NULL;
#if 0
#ifdef FREEBSD_WIP
unregister_acpi_notifier(&intel_opregion_notifier);
#endif
#endif /* FREEBSD_WIP */
}
/* just clear all opregion memory pointers now */
@ -532,45 +519,21 @@ void intel_opregion_fini(struct drm_device *dev)
opregion->asle = NULL;
opregion->vbt = NULL;
}
#else
void
intel_opregion_init(struct drm_device *dev)
{
}
void
intel_opregion_fini(struct drm_device *dev)
{
struct drm_i915_private *dev_priv;
struct intel_opregion *opregion;
dev_priv = dev->dev_private;
opregion = &dev_priv->opregion;
if (opregion->header == NULL)
return;
pmap_unmapdev((vm_offset_t)opregion->header, OPREGION_SIZE);
opregion->header = NULL;
opregion->acpi = NULL;
opregion->swsci = NULL;
opregion->asle = NULL;
opregion->vbt = NULL;
}
#endif
int intel_opregion_setup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
char *base;
void __iomem *base;
u32 asls, mboxes;
char buf[sizeof(OPREGION_SIGNATURE)];
int err = 0;
asls = pci_read_config(dev->dev, PCI_ASLS, 4);
DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
pci_read_config_dword(dev->dev, PCI_ASLS, &asls);
DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);
if (asls == 0) {
DRM_DEBUG("ACPI OpRegion not supported!\n");
DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");
return -ENOTSUP;
}
@ -578,31 +541,33 @@ int intel_opregion_setup(struct drm_device *dev)
if (!base)
return -ENOMEM;
if (memcmp(base, OPREGION_SIGNATURE, 16)) {
DRM_DEBUG("opregion signature mismatch\n");
memcpy_fromio(buf, base, sizeof(buf));
if (memcmp(buf, OPREGION_SIGNATURE, 16)) {
DRM_DEBUG_DRIVER("opregion signature mismatch\n");
err = -EINVAL;
goto err_out;
}
opregion->header = (struct opregion_header *)base;
opregion->vbt = base + OPREGION_VBT_OFFSET;
opregion->vbt = (char *)base + OPREGION_VBT_OFFSET;
opregion->lid_state = (u32 *)(base + ACPI_CLID);
opregion->lid_state = (u32 *)((char *)base + ACPI_CLID);
mboxes = opregion->header->mboxes;
if (mboxes & MBOX_ACPI) {
DRM_DEBUG("Public ACPI methods supported\n");
opregion->acpi = (struct opregion_acpi *)(base +
DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
opregion->acpi = (struct opregion_acpi *)((char *)base +
OPREGION_ACPI_OFFSET);
}
if (mboxes & MBOX_SWSCI) {
DRM_DEBUG("SWSCI supported\n");
opregion->swsci = (struct opregion_swsci *)(base +
DRM_DEBUG_DRIVER("SWSCI supported\n");
opregion->swsci = (struct opregion_swsci *)((char *)base +
OPREGION_SWSCI_OFFSET);
}
if (mboxes & MBOX_ASLE) {
DRM_DEBUG("ASLE supported\n");
opregion->asle = (struct opregion_asle *)(base +
DRM_DEBUG_DRIVER("ASLE supported\n");
opregion->asle = (struct opregion_asle *)((char *)base +
OPREGION_ASLE_OFFSET);
}

View File

@ -25,12 +25,10 @@
*
* Derived from Xorg ddx, xf86-video-intel, src/i830_video.c
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/i915_reg.h>
@ -191,46 +189,44 @@ struct intel_overlay {
void (*flip_tail)(struct intel_overlay *);
};
static struct overlay_registers *
static struct overlay_registers __iomem *
intel_overlay_map_regs(struct intel_overlay *overlay)
{
struct overlay_registers *regs;
drm_i915_private_t *dev_priv = overlay->dev->dev_private;
struct overlay_registers __iomem *regs;
if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) {
regs = overlay->reg_bo->phys_obj->handle->vaddr;
} else {
regs = pmap_mapdev_attr(overlay->dev->agp->base +
if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr;
else
regs = pmap_mapdev_attr(dev_priv->mm.gtt_base_addr +
overlay->reg_bo->gtt_offset, PAGE_SIZE,
PAT_WRITE_COMBINING);
}
return (regs);
return regs;
}
static void intel_overlay_unmap_regs(struct intel_overlay *overlay,
struct overlay_registers *regs)
struct overlay_registers __iomem *regs)
{
if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev))
pmap_unmapdev((vm_offset_t)regs, PAGE_SIZE);
}
static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
struct drm_i915_gem_request *request,
void (*tail)(struct intel_overlay *))
{
struct drm_device *dev = overlay->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->rings[RCS];
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
KASSERT(!overlay->last_flip_req, ("Overlay already has flip req"));
ret = i915_add_request(ring, NULL, request);
if (ret) {
free(request, DRM_I915_GEM);
BUG_ON(overlay->last_flip_req);
ret = i915_add_request(ring, NULL, &overlay->last_flip_req);
if (ret)
return ret;
}
overlay->last_flip_req = request->seqno;
overlay->flip_tail = tail;
ret = i915_wait_request(ring, overlay->last_flip_req);
ret = i915_wait_seqno(ring, overlay->last_flip_req);
if (ret)
return ret;
i915_gem_retire_requests(dev);
@ -239,80 +235,22 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
return 0;
}
/* Workaround for i830 bug where pipe a must be enable to change control regs */
static int
i830_activate_pipe_a(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_crtc *crtc;
struct drm_crtc_helper_funcs *crtc_funcs;
struct drm_display_mode vesa_640x480 = {
DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
752, 800, 0, 480, 489, 492, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
}, *mode;
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]);
if (crtc->dpms_mode == DRM_MODE_DPMS_ON)
return 0;
/* most i8xx have pipe a forced on, so don't trust dpms mode */
if (I915_READ(_PIPEACONF) & PIPECONF_ENABLE)
return 0;
crtc_funcs = crtc->base.helper_private;
if (crtc_funcs->dpms == NULL)
return 0;
DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n");
mode = drm_mode_duplicate(dev, &vesa_640x480);
if (!drm_crtc_helper_set_mode(&crtc->base, mode,
crtc->base.x, crtc->base.y,
crtc->base.fb))
return 0;
crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON);
return 1;
}
static void
i830_deactivate_pipe_a(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0];
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
}
/* overlay needs to be disable in OCMD reg */
static int intel_overlay_on(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->rings[RCS];
struct drm_i915_gem_request *request;
int pipe_a_quirk = 0;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
KASSERT(!overlay->active, ("Overlay is active"));
BUG_ON(overlay->active);
overlay->active = 1;
if (IS_I830(dev)) {
pipe_a_quirk = i830_activate_pipe_a(dev);
if (pipe_a_quirk < 0)
return pipe_a_quirk;
}
request = malloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
WARN_ON(IS_I830(dev) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE));
ret = intel_ring_begin(ring, 4);
if (ret) {
free(request, DRM_I915_GEM);
goto out;
}
if (ret)
return ret;
intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_ON);
intel_ring_emit(ring, overlay->flip_addr | OFC_UPDATE);
@ -320,12 +258,7 @@ static int intel_overlay_on(struct intel_overlay *overlay)
intel_ring_emit(ring, MI_NOOP);
intel_ring_advance(ring);
ret = intel_overlay_do_wait_request(overlay, request, NULL);
out:
if (pipe_a_quirk)
i830_deactivate_pipe_a(dev);
return ret;
return intel_overlay_do_wait_request(overlay, NULL);
}
/* overlay needs to be enabled in OCMD reg */
@ -334,15 +267,12 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
{
struct drm_device *dev = overlay->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->rings[RCS];
struct drm_i915_gem_request *request;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
u32 flip_addr = overlay->flip_addr;
u32 tmp;
int ret;
KASSERT(overlay->active, ("Overlay not active"));
request = malloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
BUG_ON(!overlay->active);
if (load_polyphase_filter)
flip_addr |= OFC_UPDATE;
@ -353,22 +283,14 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
ret = intel_ring_begin(ring, 2);
if (ret) {
free(request, DRM_I915_GEM);
if (ret)
return ret;
}
intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
intel_ring_emit(ring, flip_addr);
intel_ring_advance(ring);
ret = i915_add_request(ring, NULL, request);
if (ret) {
free(request, DRM_I915_GEM);
return ret;
}
overlay->last_flip_req = request->seqno;
return 0;
return i915_add_request(ring, NULL, &overlay->last_flip_req);
}
static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay)
@ -386,7 +308,7 @@ static void intel_overlay_off_tail(struct intel_overlay *overlay)
struct drm_i915_gem_object *obj = overlay->vid_bo;
/* never have the overlay hw on without showing a frame */
KASSERT(overlay->vid_bo != NULL, ("No vid_bo"));
BUG_ON(!overlay->vid_bo);
i915_gem_object_unpin(obj);
drm_gem_object_unreference(&obj->base);
@ -402,14 +324,11 @@ static int intel_overlay_off(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->rings[RCS];
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
u32 flip_addr = overlay->flip_addr;
struct drm_i915_gem_request *request;
int ret;
KASSERT(overlay->active, ("Overlay is not active"));
request = malloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
BUG_ON(!overlay->active);
/* According to intel docs the overlay hw may hang (when switching
* off) without loading the filter coeffs. It is however unclear whether
@ -418,22 +337,28 @@ static int intel_overlay_off(struct intel_overlay *overlay)
flip_addr |= OFC_UPDATE;
ret = intel_ring_begin(ring, 6);
if (ret) {
free(request, DRM_I915_GEM);
if (ret)
return ret;
}
/* wait for overlay to go idle */
intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
intel_ring_emit(ring, flip_addr);
intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
/* turn overlay off */
intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
intel_ring_emit(ring, flip_addr);
intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
if (IS_I830(dev)) {
/* Workaround: Don't disable the overlay fully, since otherwise
* it dies on the next OVERLAY_ON cmd. */
intel_ring_emit(ring, MI_NOOP);
intel_ring_emit(ring, MI_NOOP);
intel_ring_emit(ring, MI_NOOP);
} else {
intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
intel_ring_emit(ring, flip_addr);
intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
}
intel_ring_advance(ring);
return intel_overlay_do_wait_request(overlay, request,
intel_overlay_off_tail);
return intel_overlay_do_wait_request(overlay, intel_overlay_off_tail);
}
/* recover from an interruption due to a signal
@ -442,13 +367,13 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->rings[RCS];
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
if (overlay->last_flip_req == 0)
return 0;
ret = i915_wait_request(ring, overlay->last_flip_req);
ret = i915_wait_seqno(ring, overlay->last_flip_req);
if (ret)
return ret;
i915_gem_retire_requests(dev);
@ -468,7 +393,7 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->rings[RCS];
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
/* Only wait if there is actually an old frame to release to
@ -478,22 +403,16 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
return 0;
if (I915_READ(ISR) & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT) {
struct drm_i915_gem_request *request;
/* synchronous slowpath */
request = malloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
ret = intel_ring_begin(ring, 2);
if (ret) {
free(request, DRM_I915_GEM);
if (ret)
return ret;
}
intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
intel_ring_emit(ring, MI_NOOP);
intel_ring_advance(ring);
ret = intel_overlay_do_wait_request(overlay, request,
ret = intel_overlay_do_wait_request(overlay,
intel_overlay_release_old_vid_tail);
if (ret)
return ret;
@ -619,14 +538,15 @@ static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
0x3000, 0x0800, 0x3000
};
static void update_polyphase_filter(struct overlay_registers *regs)
static void update_polyphase_filter(struct overlay_registers __iomem *regs)
{
memcpy(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs));
memcpy(regs->UV_HCOEFS, uv_static_hcoeffs, sizeof(uv_static_hcoeffs));
memcpy_toio(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs));
memcpy_toio(regs->UV_HCOEFS, uv_static_hcoeffs,
sizeof(uv_static_hcoeffs));
}
static bool update_scaling_factors(struct intel_overlay *overlay,
struct overlay_registers *regs,
struct overlay_registers __iomem *regs,
struct put_image_params *params)
{
/* fixed point with a 12 bit shift */
@ -665,16 +585,19 @@ static bool update_scaling_factors(struct intel_overlay *overlay,
overlay->old_xscale = xscale;
overlay->old_yscale = yscale;
regs->YRGBSCALE = (((yscale & FRACT_MASK) << 20) |
((xscale >> FP_SHIFT) << 16) |
((xscale & FRACT_MASK) << 3));
iowrite32(((yscale & FRACT_MASK) << 20) |
((xscale >> FP_SHIFT) << 16) |
((xscale & FRACT_MASK) << 3),
&regs->YRGBSCALE);
regs->UVSCALE = (((yscale_UV & FRACT_MASK) << 20) |
((xscale_UV >> FP_SHIFT) << 16) |
((xscale_UV & FRACT_MASK) << 3));
iowrite32(((yscale_UV & FRACT_MASK) << 20) |
((xscale_UV >> FP_SHIFT) << 16) |
((xscale_UV & FRACT_MASK) << 3),
&regs->UVSCALE);
regs->UVSCALEV = ((((yscale >> FP_SHIFT) << 16) |
((yscale_UV >> FP_SHIFT) << 0)));
iowrite32((((yscale >> FP_SHIFT) << 16) |
((yscale_UV >> FP_SHIFT) << 0)),
&regs->UVSCALEV);
if (scale_changed)
update_polyphase_filter(regs);
@ -683,30 +606,32 @@ static bool update_scaling_factors(struct intel_overlay *overlay,
}
static void update_colorkey(struct intel_overlay *overlay,
struct overlay_registers *regs)
struct overlay_registers __iomem *regs)
{
u32 key = overlay->color_key;
switch (overlay->crtc->base.fb->bits_per_pixel) {
case 8:
regs->DCLRKV = 0;
regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE;
iowrite32(0, &regs->DCLRKV);
iowrite32(CLK_RGB8I_MASK | DST_KEY_ENABLE, &regs->DCLRKM);
break;
case 16:
if (overlay->crtc->base.fb->depth == 15) {
regs->DCLRKV = RGB15_TO_COLORKEY(key);
regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE;
iowrite32(RGB15_TO_COLORKEY(key), &regs->DCLRKV);
iowrite32(CLK_RGB15_MASK | DST_KEY_ENABLE,
&regs->DCLRKM);
} else {
regs->DCLRKV = RGB16_TO_COLORKEY(key);
regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE;
iowrite32(RGB16_TO_COLORKEY(key), &regs->DCLRKV);
iowrite32(CLK_RGB16_MASK | DST_KEY_ENABLE,
&regs->DCLRKM);
}
break;
case 24:
case 32:
regs->DCLRKV = key;
regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE;
iowrite32(key, &regs->DCLRKV);
iowrite32(CLK_RGB24_MASK | DST_KEY_ENABLE, &regs->DCLRKM);
break;
}
}
@ -756,24 +681,21 @@ static u32 overlay_cmd_reg(struct put_image_params *params)
return cmd;
}
static u32
max_u32(u32 a, u32 b)
{
return (a > b ? a : b);
}
static int intel_overlay_do_put_image(struct intel_overlay *overlay,
struct drm_i915_gem_object *new_bo,
struct put_image_params *params)
{
int ret, tmp_width;
struct overlay_registers *regs;
struct overlay_registers __iomem *regs;
bool scale_changed = false;
#ifdef INVARIANTS
struct drm_device *dev = overlay->dev;
#endif
u32 swidth, swidthsw, sheight, ostride;
KASSERT(overlay != NULL, ("No overlay ?"));
DRM_LOCK_ASSERT(overlay->dev);
DRM_LOCK_ASSERT(dev);
sx_assert(&dev->mode_config.mutex, SA_XLOCKED);
BUG_ON(!overlay);
ret = intel_overlay_release_old_vid(overlay);
if (ret != 0)
@ -781,7 +703,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
ret = i915_gem_object_pin_to_display_plane(new_bo, 0, NULL);
if (ret != 0)
goto out_unpin;
return ret;
ret = i915_gem_object_put_fence(new_bo);
if (ret)
@ -799,7 +721,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
oconfig |= OCONF_CSC_MODE_BT709;
oconfig |= overlay->crtc->pipe == 0 ?
OCONF_PIPE_A : OCONF_PIPE_B;
regs->OCONFIG = oconfig;
iowrite32(oconfig, &regs->OCONFIG);
intel_overlay_unmap_regs(overlay, regs);
ret = intel_overlay_on(overlay);
@ -813,8 +735,8 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
goto out_unpin;
}
regs->DWINPOS = (params->dst_y << 16) | params->dst_x;
regs->DWINSZ = (params->dst_h << 16) | params->dst_w;
iowrite32((params->dst_y << 16) | params->dst_x, &regs->DWINPOS);
iowrite32((params->dst_h << 16) | params->dst_w, &regs->DWINSZ);
if (params->format & I915_OVERLAY_YUV_PACKED)
tmp_width = packed_width_bytes(params->format, params->src_w);
@ -824,7 +746,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
swidth = params->src_w;
swidthsw = calc_swidthsw(overlay->dev, params->offset_Y, tmp_width);
sheight = params->src_h;
regs->OBUF_0Y = new_bo->gtt_offset + params->offset_Y;
iowrite32(new_bo->gtt_offset + params->offset_Y, &regs->OBUF_0Y);
ostride = params->stride_Y;
if (params->format & I915_OVERLAY_YUV_PLANAR) {
@ -836,23 +758,23 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
params->src_w/uv_hscale);
tmp_V = calc_swidthsw(overlay->dev, params->offset_V,
params->src_w/uv_hscale);
swidthsw |= max_u32(tmp_U, tmp_V) << 16;
swidthsw |= max_t(u32, tmp_U, tmp_V) << 16;
sheight |= (params->src_h/uv_vscale) << 16;
regs->OBUF_0U = new_bo->gtt_offset + params->offset_U;
regs->OBUF_0V = new_bo->gtt_offset + params->offset_V;
iowrite32(new_bo->gtt_offset + params->offset_U, &regs->OBUF_0U);
iowrite32(new_bo->gtt_offset + params->offset_V, &regs->OBUF_0V);
ostride |= params->stride_UV << 16;
}
regs->SWIDTH = swidth;
regs->SWIDTHSW = swidthsw;
regs->SHEIGHT = sheight;
regs->OSTRIDE = ostride;
iowrite32(swidth, &regs->SWIDTH);
iowrite32(swidthsw, &regs->SWIDTHSW);
iowrite32(sheight, &regs->SHEIGHT);
iowrite32(ostride, &regs->OSTRIDE);
scale_changed = update_scaling_factors(overlay, regs, params);
update_colorkey(overlay, regs);
regs->OCMD = overlay_cmd_reg(params);
iowrite32(overlay_cmd_reg(params), &regs->OCMD);
intel_overlay_unmap_regs(overlay, regs);
@ -872,10 +794,14 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
int intel_overlay_switch_off(struct intel_overlay *overlay)
{
struct overlay_registers *regs;
struct overlay_registers __iomem *regs;
#ifdef INVARIANTS
struct drm_device *dev = overlay->dev;
#endif
int ret;
DRM_LOCK_ASSERT(overlay->dev);
DRM_LOCK_ASSERT(dev);
sx_assert(&dev->mode_config.mutex, SA_XLOCKED);
ret = intel_overlay_recover_from_interrupt(overlay);
if (ret != 0)
@ -889,7 +815,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
return ret;
regs = intel_overlay_map_regs(overlay);
regs->OCMD = 0;
iowrite32(0, &regs->OCMD);
intel_overlay_unmap_regs(overlay, regs);
ret = intel_overlay_off(overlay);
@ -1138,7 +1064,9 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
return ret;
}
params = malloc(sizeof(struct put_image_params), DRM_I915_GEM, M_WAITOK | M_ZERO);
params = malloc(sizeof(struct put_image_params), DRM_I915_GEM, M_WAITOK);
if (!params)
return -ENOMEM;
drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
DRM_MODE_OBJECT_CRTC);
@ -1254,10 +1182,11 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
}
static void update_reg_attrs(struct intel_overlay *overlay,
struct overlay_registers *regs)
struct overlay_registers __iomem *regs)
{
regs->OCLRC0 = (overlay->contrast << 18) | (overlay->brightness & 0xff);
regs->OCLRC1 = overlay->saturation;
iowrite32((overlay->contrast << 18) | (overlay->brightness & 0xff),
&regs->OCLRC0);
iowrite32(overlay->saturation, &regs->OCLRC1);
}
static bool check_gamma_bounds(u32 gamma1, u32 gamma2)
@ -1310,7 +1239,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
struct drm_intel_overlay_attrs *attrs = data;
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
struct overlay_registers *regs;
struct overlay_registers __iomem *regs;
int ret;
/* No need to check for DRIVER_MODESET - we don't set it up then. */
@ -1396,16 +1325,20 @@ void intel_setup_overlay(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
struct drm_i915_gem_object *reg_bo;
struct overlay_registers *regs;
struct overlay_registers __iomem *regs;
int ret;
if (!HAS_OVERLAY(dev))
return;
overlay = malloc(sizeof(struct intel_overlay), DRM_I915_GEM, M_WAITOK | M_ZERO);
if (!overlay)
return;
DRM_LOCK(dev);
if (dev_priv->overlay != NULL)
if (WARN_ON(dev_priv->overlay))
goto out_free;
overlay->dev = dev;
reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE);
@ -1423,7 +1356,7 @@ void intel_setup_overlay(struct drm_device *dev)
}
overlay->flip_addr = reg_bo->phys_obj->handle->busaddr;
} else {
ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true);
ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true, false);
if (ret) {
DRM_ERROR("failed to pin overlay register bo\n");
goto out_free_bo;
@ -1447,7 +1380,7 @@ void intel_setup_overlay(struct drm_device *dev)
if (!regs)
goto out_unpin_bo;
memset(regs, 0, sizeof(struct overlay_registers));
memset_io(regs, 0, sizeof(struct overlay_registers));
update_polyphase_filter(regs);
update_reg_attrs(overlay, regs);
@ -1479,12 +1412,15 @@ void intel_cleanup_overlay(struct drm_device *dev)
/* The bo's should be free'd by the generic code already.
* Furthermore modesetting teardown happens beforehand so the
* hardware should be off already */
KASSERT(!dev_priv->overlay->active, ("Overlay still active"));
BUG_ON(dev_priv->overlay->active);
drm_gem_object_unreference_unlocked(&dev_priv->overlay->reg_bo->base);
free(dev_priv->overlay, DRM_I915_GEM);
}
//#ifdef CONFIG_DEBUG_FS
#define seq_printf(m, fmt, ...) sbuf_printf((m), (fmt), ##__VA_ARGS__)
struct intel_overlay_error_state {
struct overlay_registers regs;
unsigned long base;
@ -1492,6 +1428,11 @@ struct intel_overlay_error_state {
u32 isr;
};
/*
* NOTE Linux<->FreeBSD: We use the normal intel_overlay_map_regs() and
* intel_overlay_unmap_regs() defined at the top of this file.
*/
struct intel_overlay_error_state *
intel_overlay_capture_error_state(struct drm_device *dev)
{
@ -1510,15 +1451,15 @@ intel_overlay_capture_error_state(struct drm_device *dev)
error->dovsta = I915_READ(DOVSTA);
error->isr = I915_READ(ISR);
if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
error->base = (long) overlay->reg_bo->phys_obj->handle->vaddr;
error->base = (__force long)overlay->reg_bo->phys_obj->handle->vaddr;
else
error->base = (long) overlay->reg_bo->gtt_offset;
error->base = overlay->reg_bo->gtt_offset;
regs = intel_overlay_map_regs(overlay);
if (!regs)
goto err;
memcpy(&error->regs, regs, sizeof(struct overlay_registers));
memcpy_fromio(&error->regs, regs, sizeof(struct overlay_registers));
intel_overlay_unmap_regs(overlay, regs);
return error;
@ -1531,12 +1472,12 @@ intel_overlay_capture_error_state(struct drm_device *dev)
void
intel_overlay_print_error_state(struct sbuf *m, struct intel_overlay_error_state *error)
{
sbuf_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n",
error->dovsta, error->isr);
sbuf_printf(m, " Register file at 0x%08lx:\n",
error->base);
seq_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n",
error->dovsta, error->isr);
seq_printf(m, " Register file at 0x%08lx:\n",
error->base);
#define P(x) sbuf_printf(m, " " #x ": 0x%08x\n", error->regs.x)
#define P(x) seq_printf(m, " " #x ": 0x%08x\n", error->regs.x)
P(OBUF_0Y);
P(OBUF_1Y);
P(OBUF_0U);
@ -1580,3 +1521,4 @@ intel_overlay_print_error_state(struct sbuf *m, struct intel_overlay_error_state
P(UVSCALEV);
#undef P
}
//#endif

View File

@ -31,9 +31,9 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/intel_drv.h>
#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
@ -95,7 +95,7 @@ intel_pch_panel_fitting(struct drm_device *dev,
} else if (scaled_width < scaled_height) { /* letter */
height = scaled_width / mode->hdisplay;
if (height & 1)
height++;
height++;
y = (adjusted_mode->vdisplay - height + 1) / 2;
x = 0;
width = adjusted_mode->hdisplay;
@ -133,53 +133,45 @@ static int is_backlight_combination_mode(struct drm_device *dev)
return 0;
}
static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
static u32 i915_read_blc_pwm_ctl(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 val;
/* Restore the CTL value if it lost, e.g. GPU reset */
if (HAS_PCH_SPLIT(dev_priv->dev)) {
val = I915_READ(BLC_PWM_PCH_CTL2);
if (dev_priv->saveBLC_PWM_CTL2 == 0) {
dev_priv->saveBLC_PWM_CTL2 = val;
if (dev_priv->regfile.saveBLC_PWM_CTL2 == 0) {
dev_priv->regfile.saveBLC_PWM_CTL2 = val;
} else if (val == 0) {
I915_WRITE(BLC_PWM_PCH_CTL2,
dev_priv->saveBLC_PWM_CTL2);
val = dev_priv->saveBLC_PWM_CTL2;
val = dev_priv->regfile.saveBLC_PWM_CTL2;
I915_WRITE(BLC_PWM_PCH_CTL2, val);
}
} else {
val = I915_READ(BLC_PWM_CTL);
if (dev_priv->saveBLC_PWM_CTL == 0) {
dev_priv->saveBLC_PWM_CTL = val;
dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
if (dev_priv->regfile.saveBLC_PWM_CTL == 0) {
dev_priv->regfile.saveBLC_PWM_CTL = val;
if (INTEL_INFO(dev)->gen >= 4)
dev_priv->regfile.saveBLC_PWM_CTL2 =
I915_READ(BLC_PWM_CTL2);
} else if (val == 0) {
I915_WRITE(BLC_PWM_CTL,
dev_priv->saveBLC_PWM_CTL);
I915_WRITE(BLC_PWM_CTL2,
dev_priv->saveBLC_PWM_CTL2);
val = dev_priv->saveBLC_PWM_CTL;
val = dev_priv->regfile.saveBLC_PWM_CTL;
I915_WRITE(BLC_PWM_CTL, val);
if (INTEL_INFO(dev)->gen >= 4)
I915_WRITE(BLC_PWM_CTL2,
dev_priv->regfile.saveBLC_PWM_CTL2);
}
}
return val;
}
u32 intel_panel_get_max_backlight(struct drm_device *dev)
static u32 _intel_panel_get_max_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 max;
max = i915_read_blc_pwm_ctl(dev_priv);
if (max == 0) {
/* XXX add code here to query mode clock or hardware clock
* and program max PWM appropriately.
*/
#if 0
printf("fixme: max PWM is zero.\n");
#endif
return 1;
}
max = i915_read_blc_pwm_ctl(dev);
if (HAS_PCH_SPLIT(dev)) {
max >>= 16;
@ -193,10 +185,34 @@ u32 intel_panel_get_max_backlight(struct drm_device *dev)
max *= 0xff;
}
DRM_DEBUG("max backlight PWM = %d\n", max);
return max;
}
u32 intel_panel_get_max_backlight(struct drm_device *dev)
{
u32 max;
max = _intel_panel_get_max_backlight(dev);
if (max == 0) {
/* XXX add code here to query mode clock or hardware clock
* and program max PWM appropriately.
*/
pr_warn_once("fixme: max PWM is zero\n");
return 1;
}
DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
return max;
}
static int i915_panel_invert_brightness;
TUNABLE_INT("drm.i915.invert_brightness", &i915_panel_invert_brightness);
MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness "
"(-1 force normal, 0 machine defaults, 1 force inversion), please "
"report PCI device ID, subsystem vendor and subsystem device ID "
"to dri-devel@lists.freedesktop.org, if your machine needs it. "
"It will then be included in an upcoming module version.");
module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600);
static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@ -211,7 +227,7 @@ static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
return val;
}
u32 intel_panel_get_backlight(struct drm_device *dev)
static u32 intel_panel_get_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 val;
@ -226,7 +242,7 @@ u32 intel_panel_get_backlight(struct drm_device *dev)
if (is_backlight_combination_mode(dev)) {
u8 lbpc;
lbpc = pci_read_config(dev->dev, PCI_LBPC, 1);
pci_read_config_byte(dev->dev, PCI_LBPC, &lbpc);
val *= lbpc;
}
}
@ -260,11 +276,11 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
lbpc = level * 0xfe / max + 1;
level /= lbpc;
pci_write_config(dev->dev, PCI_LBPC, lbpc, 4);
pci_write_config_byte(dev->dev, PCI_LBPC, lbpc);
}
tmp = I915_READ(BLC_PWM_CTL);
if (INTEL_INFO(dev)->gen < 4)
if (INTEL_INFO(dev)->gen < 4)
level <<= 1;
tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
I915_WRITE(BLC_PWM_CTL, tmp | level);
@ -285,15 +301,69 @@ void intel_panel_disable_backlight(struct drm_device *dev)
dev_priv->backlight_enabled = false;
intel_panel_actually_set_backlight(dev, 0);
if (INTEL_INFO(dev)->gen >= 4) {
uint32_t reg, tmp;
reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
I915_WRITE(reg, I915_READ(reg) & ~BLM_PWM_ENABLE);
if (HAS_PCH_SPLIT(dev)) {
tmp = I915_READ(BLC_PWM_PCH_CTL1);
tmp &= ~BLM_PCH_PWM_ENABLE;
I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
}
}
}
void intel_panel_enable_backlight(struct drm_device *dev)
void intel_panel_enable_backlight(struct drm_device *dev,
enum pipe pipe)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (dev_priv->backlight_level == 0)
dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
if (INTEL_INFO(dev)->gen >= 4) {
uint32_t reg, tmp;
reg = HAS_PCH_SPLIT(dev) ? BLC_PWM_CPU_CTL2 : BLC_PWM_CTL2;
tmp = I915_READ(reg);
/* Note that this can also get called through dpms changes. And
* we don't track the backlight dpms state, hence check whether
* we have to do anything first. */
if (tmp & BLM_PWM_ENABLE)
goto set_level;
if (dev_priv->num_pipe == 3)
tmp &= ~BLM_PIPE_SELECT_IVB;
else
tmp &= ~BLM_PIPE_SELECT;
tmp |= BLM_PIPE(pipe);
tmp &= ~BLM_PWM_ENABLE;
I915_WRITE(reg, tmp);
POSTING_READ(reg);
I915_WRITE(reg, tmp | BLM_PWM_ENABLE);
if (HAS_PCH_SPLIT(dev)) {
tmp = I915_READ(BLC_PWM_PCH_CTL1);
tmp |= BLM_PCH_PWM_ENABLE;
tmp &= ~BLM_PCH_OVERRIDE_ENABLE;
I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
}
}
set_level:
/* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1.
* BLC_PWM_CPU_CTL may be cleared to zero automatically when these
* registers are set.
*/
dev_priv->backlight_enabled = true;
intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
}
@ -309,31 +379,90 @@ static void intel_panel_init_backlight(struct drm_device *dev)
enum drm_connector_status
intel_panel_detect(struct drm_device *dev)
{
#if 0
struct drm_i915_private *dev_priv = dev->dev_private;
#endif
if (i915_panel_ignore_lid)
return i915_panel_ignore_lid > 0 ?
connector_status_connected :
connector_status_disconnected;
/* opregion lid state on HP 2540p is wrong at boot up,
* appears to be either the BIOS or Linux ACPI fault */
#if 0
/* Assume that the BIOS does not lie through the OpRegion... */
if (dev_priv->opregion.lid_state)
if (!i915_panel_ignore_lid && dev_priv->opregion.lid_state) {
return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
connector_status_connected :
connector_status_disconnected;
#endif
}
return connector_status_unknown;
switch (i915_panel_ignore_lid) {
case -2:
return connector_status_connected;
case -1:
return connector_status_disconnected;
default:
return connector_status_unknown;
}
}
int intel_panel_setup_backlight(struct drm_device *dev)
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
static int intel_panel_update_status(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
intel_panel_set_backlight(dev, bd->props.brightness);
return 0;
}
static int intel_panel_get_brightness(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
struct drm_i915_private *dev_priv = dev->dev_private;
return dev_priv->backlight_level;
}
static const struct backlight_ops intel_panel_bl_ops = {
.update_status = intel_panel_update_status,
.get_brightness = intel_panel_get_brightness,
};
int intel_panel_setup_backlight(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct backlight_properties props;
intel_panel_init_backlight(dev);
if (WARN_ON(dev_priv->backlight))
return -ENODEV;
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
props.max_brightness = _intel_panel_get_max_backlight(dev);
if (props.max_brightness == 0) {
DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n");
return -ENODEV;
}
dev_priv->backlight =
backlight_device_register("intel_backlight",
&connector->kdev, dev,
&intel_panel_bl_ops, &props);
if (IS_ERR(dev_priv->backlight)) {
DRM_ERROR("Failed to register backlight: %ld\n",
PTR_ERR(dev_priv->backlight));
dev_priv->backlight = NULL;
return -ENODEV;
}
dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev);
return 0;
}
void intel_panel_destroy_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (dev_priv->backlight) {
backlight_device_unregister(dev_priv->backlight);
dev_priv->backlight = NULL;
}
}
#else
int intel_panel_setup_backlight(struct drm_connector *connector)
{
intel_panel_init_backlight(connector->dev);
return 0;
}
@ -341,3 +470,21 @@ void intel_panel_destroy_backlight(struct drm_device *dev)
{
return;
}
#endif
int intel_panel_init(struct intel_panel *panel,
struct drm_display_mode *fixed_mode)
{
panel->fixed_mode = fixed_mode;
return 0;
}
void intel_panel_fini(struct intel_panel *panel)
{
struct intel_connector *intel_connector =
container_of(panel, struct intel_connector, panel);
if (panel->fixed_mode)
drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,9 @@
/*
* $FreeBSD$
*/
#ifndef _INTEL_RINGBUFFER_H_
#define _INTEL_RINGBUFFER_H_
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* Gen2 BSpec "1. Programming Environment" / 1.4.4.6 "Ring Buffer Use"
* Gen3 BSpec "vol1c Memory Interface Functions" / 2.3.4.5 "Ring Buffer Use"
@ -50,7 +49,7 @@ struct intel_ring_buffer {
} id;
#define I915_NUM_RINGS 3
u32 mmio_base;
void *virtual_start;
void __iomem *virtual_start;
struct drm_device *dev;
struct drm_i915_gem_object *obj;
@ -75,21 +74,28 @@ struct intel_ring_buffer {
u32 irq_enable_mask; /* bitmask to enable ring interrupt */
u32 trace_irq_seqno;
u32 sync_seqno[I915_NUM_RINGS-1];
bool (*irq_get)(struct intel_ring_buffer *ring);
bool __must_check (*irq_get)(struct intel_ring_buffer *ring);
void (*irq_put)(struct intel_ring_buffer *ring);
int (*init)(struct intel_ring_buffer *ring);
void (*write_tail)(struct intel_ring_buffer *ring,
u32 value);
int (*flush)(struct intel_ring_buffer *ring,
int __must_check (*flush)(struct intel_ring_buffer *ring,
u32 invalidate_domains,
u32 flush_domains);
int (*add_request)(struct intel_ring_buffer *ring,
uint32_t *seqno);
uint32_t (*get_seqno)(struct intel_ring_buffer *ring);
int (*add_request)(struct intel_ring_buffer *ring);
/* Some chipsets are not quite as coherent as advertised and need
* an expensive kick to force a true read of the up-to-date seqno.
* However, the up-to-date seqno is not always required and the last
* seen value is good enough. Note that the seqno will always be
* monotonic, even if not coherent.
*/
u32 (*get_seqno)(struct intel_ring_buffer *ring,
bool lazy_coherency);
int (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
u32 offset, u32 length);
u32 offset, u32 length,
unsigned flags);
#define I915_DISPATCH_SECURE 0x1
#define I915_DISPATCH_PINNED 0x2
void (*cleanup)(struct intel_ring_buffer *ring);
@ -117,19 +123,13 @@ struct intel_ring_buffer {
*/
struct list_head request_list;
/**
* List of objects currently pending a GPU write flush.
*
* All elements on this list will belong to either the
* active_list or flushing_list, last_rendering_seqno can
* be used to differentiate between the two elements.
*/
struct list_head gpu_write_list;
/**
* Do we have some not yet emitted requests outstanding?
*/
u32 outstanding_lazy_request;
bool gpu_caches_dirty;
wait_queue_head_t irq_queue;
/**
* Do an explicit TLB flush before MI_SET_CONTEXT
@ -138,8 +138,6 @@ struct intel_ring_buffer {
struct i915_hw_context *default_context;
struct drm_i915_gem_object *last_context_obj;
drm_local_map_t map;
void *private;
};
@ -179,32 +177,43 @@ intel_read_status_page(struct intel_ring_buffer *ring,
int reg)
{
/* Ensure that the compiler doesn't optimize away the load. */
__compiler_membar();
barrier();
return atomic_load_acq_32(ring->status_page.page_addr + reg);
}
/**
* Reads a dword out of the status page, which is written to from the command
* queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or
* MI_STORE_DATA_IMM.
*
* The following dwords have a reserved meaning:
* 0x00: ISR copy, updated when an ISR bit not set in the HWSTAM changes.
* 0x04: ring 0 head pointer
* 0x05: ring 1 head pointer (915-class)
* 0x06: ring 2 head pointer (915-class)
* 0x10-0x1b: Context status DWords (GM45)
* 0x1f: Last written status offset. (GM45)
*
* The area from dword 0x20 to 0x3ff is available for driver usage.
*/
#define I915_GEM_HWS_INDEX 0x20
#define I915_GEM_HWS_SCRATCH_INDEX 0x30
#define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT)
void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring);
int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n);
static inline int intel_wait_ring_idle(struct intel_ring_buffer *ring)
{
return (intel_wait_ring_buffer(ring, ring->size - 8));
}
int intel_ring_begin(struct intel_ring_buffer *ring, int n);
int __must_check intel_ring_begin(struct intel_ring_buffer *ring, int n);
static inline void intel_ring_emit(struct intel_ring_buffer *ring,
u32 data)
{
*(volatile uint32_t *)((char *)ring->virtual_start +
ring->tail) = data;
iowrite32(data, ring->virtual_start + ring->tail);
ring->tail += 4;
}
void intel_ring_advance(struct intel_ring_buffer *ring);
int __must_check intel_ring_idle(struct intel_ring_buffer *ring);
uint32_t intel_ring_get_seqno(struct intel_ring_buffer *ring);
int intel_ring_flush_all_caches(struct intel_ring_buffer *ring);
int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring);
int intel_init_render_ring_buffer(struct drm_device *dev);
int intel_init_bsd_ring_buffer(struct drm_device *dev);
@ -218,7 +227,19 @@ static inline u32 intel_ring_get_tail(struct intel_ring_buffer *ring)
return ring->tail;
}
void i915_trace_irq_get(struct intel_ring_buffer *ring, uint32_t seqno);
static inline u32 intel_ring_get_seqno(struct intel_ring_buffer *ring)
{
BUG_ON(ring->outstanding_lazy_request == 0);
return ring->outstanding_lazy_request;
}
#ifdef __linux__
static inline void i915_trace_irq_get(struct intel_ring_buffer *ring, u32 seqno)
{
if (ring->trace_irq_seqno == 0 && ring->irq_get(ring))
ring->trace_irq_seqno = seqno;
}
#endif
/* DRI warts */
int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size);

File diff suppressed because it is too large Load Diff

View File

@ -34,11 +34,11 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_fourcc.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/drm_fourcc.h>
static void
ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
@ -52,7 +52,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
struct intel_plane *intel_plane = to_intel_plane(plane);
int pipe = intel_plane->pipe;
u32 sprctl, sprscale = 0;
int pixel_size;
unsigned long sprsurf_offset, linear_offset;
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
sprctl = I915_READ(SPRCTL(pipe));
@ -60,37 +61,29 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
sprctl &= ~SPRITE_PIXFORMAT_MASK;
sprctl &= ~SPRITE_RGB_ORDER_RGBX;
sprctl &= ~SPRITE_YUV_BYTE_ORDER_MASK;
sprctl &= ~SPRITE_TILED;
switch (fb->pixel_format) {
case DRM_FORMAT_XBGR8888:
sprctl |= SPRITE_FORMAT_RGBX888;
pixel_size = 4;
sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
break;
case DRM_FORMAT_XRGB8888:
sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
pixel_size = 4;
sprctl |= SPRITE_FORMAT_RGBX888;
break;
case DRM_FORMAT_YUYV:
sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YUYV;
pixel_size = 2;
break;
case DRM_FORMAT_YVYU:
sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YVYU;
pixel_size = 2;
break;
case DRM_FORMAT_UYVY:
sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_UYVY;
pixel_size = 2;
break;
case DRM_FORMAT_VYUY:
sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY;
pixel_size = 2;
break;
default:
DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n");
sprctl |= DVS_FORMAT_RGBX888;
pixel_size = 4;
break;
BUG();
}
if (obj->tiling_mode != I915_TILING_NONE)
@ -130,18 +123,27 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
if (obj->tiling_mode != I915_TILING_NONE) {
I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x);
} else {
unsigned long offset;
offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
I915_WRITE(SPRLINOFF(pipe), offset);
}
linear_offset = y * fb->pitches[0] + x * pixel_size;
sprsurf_offset =
intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode,
pixel_size, fb->pitches[0]);
linear_offset -= sprsurf_offset;
/* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET
* register */
if (IS_HASWELL(dev))
I915_WRITE(SPROFFSET(pipe), (y << 16) | x);
else if (obj->tiling_mode != I915_TILING_NONE)
I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x);
else
I915_WRITE(SPRLINOFF(pipe), linear_offset);
I915_WRITE(SPRSIZE(pipe), (crtc_h << 16) | crtc_w);
I915_WRITE(SPRSCALE(pipe), sprscale);
if (intel_plane->can_scale)
I915_WRITE(SPRSCALE(pipe), sprscale);
I915_WRITE(SPRCTL(pipe), sprctl);
I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset);
I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset + sprsurf_offset);
POSTING_READ(SPRSURF(pipe));
}
@ -155,7 +157,8 @@ ivb_disable_plane(struct drm_plane *plane)
I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
/* Can't leave the scaler enabled... */
I915_WRITE(SPRSCALE(pipe), 0);
if (intel_plane->can_scale)
I915_WRITE(SPRSCALE(pipe), 0);
/* Activate double buffered register update */
I915_MODIFY_DISPBASE(SPRSURF(pipe), 0);
POSTING_READ(SPRSURF(pipe));
@ -228,8 +231,10 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_plane *intel_plane = to_intel_plane(plane);
int pipe = intel_plane->pipe, pixel_size;
int pipe = intel_plane->pipe;
unsigned long dvssurf_offset, linear_offset;
u32 dvscntr, dvsscale;
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
dvscntr = I915_READ(DVSCNTR(pipe));
@ -237,37 +242,29 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
dvscntr &= ~DVS_PIXFORMAT_MASK;
dvscntr &= ~DVS_RGB_ORDER_XBGR;
dvscntr &= ~DVS_YUV_BYTE_ORDER_MASK;
dvscntr &= ~DVS_TILED;
switch (fb->pixel_format) {
case DRM_FORMAT_XBGR8888:
dvscntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_XBGR;
pixel_size = 4;
break;
case DRM_FORMAT_XRGB8888:
dvscntr |= DVS_FORMAT_RGBX888;
pixel_size = 4;
break;
case DRM_FORMAT_YUYV:
dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YUYV;
pixel_size = 2;
break;
case DRM_FORMAT_YVYU:
dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YVYU;
pixel_size = 2;
break;
case DRM_FORMAT_UYVY:
dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_UYVY;
pixel_size = 2;
break;
case DRM_FORMAT_VYUY:
dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY;
pixel_size = 2;
break;
default:
DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n");
dvscntr |= DVS_FORMAT_RGBX888;
pixel_size = 4;
break;
BUG();
}
if (obj->tiling_mode != I915_TILING_NONE)
@ -291,18 +288,22 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
if (obj->tiling_mode != I915_TILING_NONE) {
I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x);
} else {
unsigned long offset;
offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
I915_WRITE(DVSLINOFF(pipe), offset);
}
linear_offset = y * fb->pitches[0] + x * pixel_size;
dvssurf_offset =
intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode,
pixel_size, fb->pitches[0]);
linear_offset -= dvssurf_offset;
if (obj->tiling_mode != I915_TILING_NONE)
I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x);
else
I915_WRITE(DVSLINOFF(pipe), linear_offset);
I915_WRITE(DVSSIZE(pipe), (crtc_h << 16) | crtc_w);
I915_WRITE(DVSSCALE(pipe), dvsscale);
I915_WRITE(DVSCNTR(pipe), dvscntr);
I915_MODIFY_DISPBASE(DVSSURF(pipe), obj->gtt_offset);
I915_MODIFY_DISPBASE(DVSSURF(pipe), obj->gtt_offset + dvssurf_offset);
POSTING_READ(DVSSURF(pipe));
}
@ -330,6 +331,12 @@ intel_enable_primary(struct drm_crtc *crtc)
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int reg = DSPCNTR(intel_crtc->plane);
if (!intel_crtc->primary_disabled)
return;
intel_crtc->primary_disabled = false;
intel_update_fbc(dev);
I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE);
}
@ -341,7 +348,13 @@ intel_disable_primary(struct drm_crtc *crtc)
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int reg = DSPCNTR(intel_crtc->plane);
if (intel_crtc->primary_disabled)
return;
I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
intel_crtc->primary_disabled = true;
intel_update_fbc(dev);
}
static int
@ -412,6 +425,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct intel_framebuffer *intel_fb;
struct drm_i915_gem_object *obj, *old_obj;
int pipe = intel_plane->pipe;
enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
pipe);
int ret = 0;
int x = src_x >> 16, y = src_y >> 16;
int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay;
@ -426,7 +441,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
src_h = src_h >> 16;
/* Pipe must be running... */
if (!(I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE))
if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE))
return -EINVAL;
if (crtc_x >= primary_w || crtc_y >= primary_h)
@ -436,6 +451,15 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
if (intel_plane->pipe != intel_crtc->pipe)
return -EINVAL;
/* Sprite planes can be linear or x-tiled surfaces */
switch (obj->tiling_mode) {
case I915_TILING_NONE:
case I915_TILING_X:
break;
default:
return -EINVAL;
}
/*
* Clamp the width & height into the visible area. Note we don't
* try to scale the source if part of the visible region is offscreen.
@ -462,6 +486,12 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
if (!crtc_w || !crtc_h) /* Again, nothing to display */
goto out;
/*
* We may not have a scaler, eg. HSW does not have it any more
*/
if (!intel_plane->can_scale && (crtc_w != src_w || crtc_h != src_h))
return -EINVAL;
/*
* We can take a larger source and scale it down, but
* only so much... 16x is the max on SNB.
@ -489,18 +519,14 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
* Be sure to re-enable the primary before the sprite is no longer
* covering it fully.
*/
if (!disable_primary && intel_plane->primary_disabled) {
if (!disable_primary)
intel_enable_primary(crtc);
intel_plane->primary_disabled = false;
}
intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y,
crtc_w, crtc_h, x, y, src_w, src_h);
if (disable_primary) {
if (disable_primary)
intel_disable_primary(crtc);
intel_plane->primary_disabled = true;
}
/* Unpin old obj after new one is active to avoid ugliness */
if (old_obj) {
@ -531,11 +557,8 @@ intel_disable_plane(struct drm_plane *plane)
struct intel_plane *intel_plane = to_intel_plane(plane);
int ret = 0;
if (intel_plane->primary_disabled) {
if (plane->crtc)
intel_enable_primary(plane->crtc);
intel_plane->primary_disabled = false;
}
intel_plane->disable_plane(plane);
if (!intel_plane->obj)
@ -575,7 +598,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
return -EINVAL;
sx_xlock(&dev->mode_config.mutex);
obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);
if (!obj) {
ret = -EINVAL;
@ -655,12 +678,14 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe)
if (INTEL_INFO(dev)->gen < 5)
return -ENODEV;
intel_plane = malloc(sizeof(struct intel_plane), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
intel_plane = malloc(sizeof(struct intel_plane), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_plane)
return -ENOMEM;
switch (INTEL_INFO(dev)->gen) {
case 5:
case 6:
intel_plane->can_scale = true;
intel_plane->max_downscale = 16;
intel_plane->update_plane = ilk_update_plane;
intel_plane->disable_plane = ilk_disable_plane;
@ -669,14 +694,18 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe)
if (IS_GEN6(dev)) {
plane_formats = snb_plane_formats;
num_plane_formats = DRM_ARRAY_SIZE(snb_plane_formats);
num_plane_formats = ARRAY_SIZE(snb_plane_formats);
} else {
plane_formats = ilk_plane_formats;
num_plane_formats = DRM_ARRAY_SIZE(ilk_plane_formats);
num_plane_formats = ARRAY_SIZE(ilk_plane_formats);
}
break;
case 7:
if (IS_HASWELL(dev) || IS_VALLEYVIEW(dev))
intel_plane->can_scale = false;
else
intel_plane->can_scale = true;
intel_plane->max_downscale = 2;
intel_plane->update_plane = ivb_update_plane;
intel_plane->disable_plane = ivb_disable_plane;
@ -684,10 +713,11 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe)
intel_plane->get_colorkey = ivb_get_colorkey;
plane_formats = snb_plane_formats;
num_plane_formats = DRM_ARRAY_SIZE(snb_plane_formats);
num_plane_formats = ARRAY_SIZE(snb_plane_formats);
break;
default:
free(intel_plane, DRM_MEM_KMS);
return -ENODEV;
}
@ -702,4 +732,3 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe)
return ret;
}

View File

@ -34,12 +34,11 @@
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_edid.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/i915/intel_drv.h>
enum tv_margin {
TV_MARGIN_LEFT, TV_MARGIN_TOP,
@ -676,6 +675,54 @@ static const struct tv_mode tv_modes[] = {
.filter_table = filter_table,
},
{
.name = "480p",
.clock = 107520,
.refresh = 59940,
.oversample = TV_OVERSAMPLE_4X,
.component_only = 1,
.hsync_end = 64, .hblank_end = 122,
.hblank_start = 842, .htotal = 857,
.progressive = true, .trilevel_sync = false,
.vsync_start_f1 = 12, .vsync_start_f2 = 12,
.vsync_len = 12,
.veq_ena = false,
.vi_end_f1 = 44, .vi_end_f2 = 44,
.nbr_end = 479,
.burst_ena = false,
.filter_table = filter_table,
},
{
.name = "576p",
.clock = 107520,
.refresh = 50000,
.oversample = TV_OVERSAMPLE_4X,
.component_only = 1,
.hsync_end = 64, .hblank_end = 139,
.hblank_start = 859, .htotal = 863,
.progressive = true, .trilevel_sync = false,
.vsync_start_f1 = 10, .vsync_start_f2 = 10,
.vsync_len = 10,
.veq_ena = false,
.vi_end_f1 = 48, .vi_end_f2 = 48,
.nbr_end = 575,
.burst_ena = false,
.filter_table = filter_table,
},
{
.name = "720p@60Hz",
.clock = 148800,
@ -791,22 +838,37 @@ static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
base);
}
static void
intel_tv_dpms(struct drm_encoder *encoder, int mode)
static bool
intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
{
struct drm_device *dev = encoder->dev;
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp = I915_READ(TV_CTL);
if (!(tmp & TV_ENC_ENABLE))
return false;
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void
intel_enable_tv(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
switch (mode) {
case DRM_MODE_DPMS_ON:
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
break;
}
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
}
static void
intel_disable_tv(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
}
static const struct tv_mode *
@ -814,7 +876,7 @@ intel_tv_mode_lookup(const char *tv_format)
{
int i;
for (i = 0; i < DRM_ARRAY_SIZE(tv_modes); i++) {
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
const struct tv_mode *tv_mode = &tv_modes[i];
if (!strcmp(tv_format, tv_mode->name))
@ -846,24 +908,18 @@ intel_tv_mode_valid(struct drm_connector *connector,
static bool
intel_tv_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode,
intel_tv_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_mode_config *drm_config = &dev->mode_config;
struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
struct drm_encoder *other_encoder;
if (!tv_mode)
return false;
/* FIXME: lock encoder list */
list_for_each_entry(other_encoder, &drm_config->encoder_list, head) {
if (other_encoder != encoder &&
other_encoder->crtc == encoder->crtc)
return false;
}
if (intel_encoder_check_is_cloned(&intel_tv->base))
return false;
adjusted_mode->clock = tv_mode->clock;
return true;
@ -1035,13 +1091,11 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
int dspcntr_reg = DSPCNTR(intel_crtc->plane);
int pipeconf = I915_READ(pipeconf_reg);
int dspcntr = I915_READ(dspcntr_reg);
int dspbase_reg = DSPADDR(intel_crtc->plane);
int xpos = 0x0, ypos = 0x0;
unsigned int xsize, ysize;
/* Pipe must be off here */
I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
/* Flush the plane changes */
I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
intel_flush_display_plane(dev_priv, intel_crtc->plane);
/* Wait for vblank for the disable to take effect */
if (IS_GEN2(dev))
@ -1070,8 +1124,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
I915_WRITE(pipeconf_reg, pipeconf);
I915_WRITE(dspcntr_reg, dspcntr);
/* Flush the plane changes */
I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
intel_flush_display_plane(dev_priv, intel_crtc->plane);
}
j = 0;
@ -1196,6 +1249,11 @@ intel_tv_detect_type(struct intel_tv *intel_tv,
I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
I915_WRITE(TV_CTL, save_tv_ctl);
POSTING_READ(TV_CTL);
/* For unknown reasons the hw barfs if we don't do this vblank wait. */
intel_wait_for_vblank(intel_tv->base.base.dev,
to_intel_crtc(intel_tv->base.base.crtc)->pipe);
/* Restore interrupt config */
if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
@ -1251,17 +1309,13 @@ intel_tv_detect(struct drm_connector *connector, bool force)
int type;
mode = reported_modes[0];
drm_mode_set_crtcinfo(&mode, 0);
if (force) {
struct intel_load_detect_pipe tmp;
if (intel_get_load_detect_pipe(&intel_tv->base, connector,
&mode, &tmp)) {
if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
type = intel_tv_detect_type(intel_tv, connector);
intel_release_load_detect_pipe(&intel_tv->base,
connector,
&tmp);
intel_release_load_detect_pipe(connector, &tmp);
} else
return connector_status_unknown;
} else
@ -1360,7 +1414,7 @@ intel_tv_get_modes(struct drm_connector *connector)
tmp = (u64) tv_mode->refresh * mode_ptr->vtotal;
tmp *= mode_ptr->htotal;
tmp = tmp / 1000000;
tmp = div_u64(tmp, 1000000);
mode_ptr->clock = (int) tmp;
mode_ptr->type = DRM_MODE_TYPE_DRIVER;
@ -1375,9 +1429,6 @@ intel_tv_get_modes(struct drm_connector *connector)
static void
intel_tv_destroy(struct drm_connector *connector)
{
#if 0
drm_sysfs_connector_remove(connector);
#endif
drm_connector_cleanup(connector);
free(connector, DRM_MEM_KMS);
}
@ -1429,22 +1480,20 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
}
if (changed && crtc)
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
crtc->y, crtc->fb);
intel_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb);
out:
return ret;
}
static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
.dpms = intel_tv_dpms,
.mode_fixup = intel_tv_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_tv_mode_set,
.commit = intel_encoder_commit,
.disable = intel_encoder_noop,
};
static const struct drm_connector_funcs intel_tv_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = intel_connector_dpms,
.detect = intel_tv_detect,
.destroy = intel_tv_destroy,
.set_property = intel_tv_set_property,
@ -1543,10 +1592,16 @@ intel_tv_init(struct drm_device *dev)
(tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
return;
intel_tv = malloc(sizeof(struct intel_tv), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS,
M_WAITOK | M_ZERO);
intel_tv = malloc(sizeof(struct intel_tv), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_tv) {
return;
}
intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO);
if (!intel_connector) {
free(intel_tv, DRM_MEM_KMS);
return;
}
intel_encoder = &intel_tv->base;
connector = &intel_connector->base;
@ -1568,10 +1623,15 @@ intel_tv_init(struct drm_device *dev)
drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
DRM_MODE_ENCODER_TVDAC);
intel_encoder->enable = intel_enable_tv;
intel_encoder->disable = intel_disable_tv;
intel_encoder->get_hw_state = intel_tv_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_TVOUT;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT);
intel_encoder->cloneable = false;
intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
@ -1610,7 +1670,4 @@ intel_tv_init(struct drm_device *dev)
drm_object_attach_property(&connector->base,
dev->mode_config.tv_bottom_margin_property,
intel_tv->margin[TV_MARGIN_BOTTOM]);
#if 0
drm_sysfs_connector_add(connector);
#endif
}

View File

@ -3,23 +3,31 @@
.PATH: ${.CURDIR}/../../../dev/drm2/i915
KMOD = i915kms
SRCS = \
dvo_ch7017.c \
dvo_ch7xxx.c \
dvo_ivch.c \
dvo_ns2501.c \
dvo_sil164.c \
dvo_tfp410.c \
i915_debug.c \
i915_dma.c \
i915_drv.c \
i915_gem.c \
i915_gem_context.c \
i915_gem_execbuffer.c \
i915_gem_evict.c \
i915_gem_execbuffer.c \
i915_gem_gtt.c \
i915_gem_stolen.c \
i915_gem_tiling.c \
i915_irq.c \
i915_suspend.c \
intel_acpi.c \
intel_bios.c \
intel_crt.c \
intel_ddi.c \
intel_display.c \
intel_dp.c \
intel_dvo.c \
intel_fb.c \
intel_hdmi.c \
intel_iic.c \