提交 b80d6c78 编写于 作者: D Daniel Vetter

Merge branch 'topic/dp-aux-rework' into drm-intel-next-queued

Conflicts:
	drivers/gpu/drm/i915/intel_dp.c

A bit a mess with reverts which differe in details between -fixes and
-next and some other unrelated shuffling.
Signed-off-by: NDaniel Vetter <daniel.vetter@ffwll.ch>
此差异已折叠。
Device-Tree bindings for the NXP TDA998x HDMI transmitter
Required properties;
- compatible: must be "nxp,tda998x"
Optional properties:
- interrupts: interrupt number and trigger type
default: polling
- pinctrl-0: pin control group to be used for
screen plug/unplug interrupt.
- pinctrl-names: must contain a "default" entry.
- video-ports: 24 bits value which defines how the video controller
output is wired to the TDA998x input - default: <0x230145>
Example:
tda998x: hdmi-encoder {
compatible = "nxp,tda998x";
reg = <0x70>;
interrupt-parent = <&gpio0>;
interrupts = <27 2>; /* falling edge */
pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default";
};
...@@ -453,7 +453,7 @@ TP_STATUS_COPY : This flag indicates that the frame (and associated ...@@ -453,7 +453,7 @@ TP_STATUS_COPY : This flag indicates that the frame (and associated
enabled previously with setsockopt() and enabled previously with setsockopt() and
the PACKET_COPY_THRESH option. the PACKET_COPY_THRESH option.
The number of frames than can be buffered to The number of frames that can be buffered to
be read with recvfrom is limited like a normal socket. be read with recvfrom is limited like a normal socket.
See the SO_RCVBUF option in the socket (7) man page. See the SO_RCVBUF option in the socket (7) man page.
......
...@@ -21,26 +21,38 @@ has such a feature). ...@@ -21,26 +21,38 @@ has such a feature).
SO_TIMESTAMPING: SO_TIMESTAMPING:
Instructs the socket layer which kind of information is wanted. The Instructs the socket layer which kind of information should be collected
parameter is an integer with some of the following bits set. Setting and/or reported. The parameter is an integer with some of the following
other bits is an error and doesn't change the current state. bits set. Setting other bits is an error and doesn't change the current
state.
SOF_TIMESTAMPING_TX_HARDWARE: try to obtain send time stamp in hardware
SOF_TIMESTAMPING_TX_SOFTWARE: if SOF_TIMESTAMPING_TX_HARDWARE is off or Four of the bits are requests to the stack to try to generate
fails, then do it in software timestamps. Any combination of them is valid.
SOF_TIMESTAMPING_RX_HARDWARE: return the original, unmodified time stamp
as generated by the hardware SOF_TIMESTAMPING_TX_HARDWARE: try to obtain send time stamps in hardware
SOF_TIMESTAMPING_RX_SOFTWARE: if SOF_TIMESTAMPING_RX_HARDWARE is off or SOF_TIMESTAMPING_TX_SOFTWARE: try to obtain send time stamps in software
fails, then do it in software SOF_TIMESTAMPING_RX_HARDWARE: try to obtain receive time stamps in hardware
SOF_TIMESTAMPING_RAW_HARDWARE: return original raw hardware time stamp SOF_TIMESTAMPING_RX_SOFTWARE: try to obtain receive time stamps in software
SOF_TIMESTAMPING_SYS_HARDWARE: return hardware time stamp transformed to
the system time base The other three bits control which timestamps will be reported in a
SOF_TIMESTAMPING_SOFTWARE: return system time stamp generated in generated control message. If none of these bits are set or if none of
software the set bits correspond to data that is available, then the control
message will not be generated:
SOF_TIMESTAMPING_TX/RX determine how time stamps are generated.
SOF_TIMESTAMPING_RAW/SYS determine how they are reported in the SOF_TIMESTAMPING_SOFTWARE: report systime if available
following control message: SOF_TIMESTAMPING_SYS_HARDWARE: report hwtimetrans if available
SOF_TIMESTAMPING_RAW_HARDWARE: report hwtimeraw if available
It is worth noting that timestamps may be collected for reasons other
than being requested by a particular socket with
SOF_TIMESTAMPING_[TR]X_(HARD|SOFT)WARE. For example, most drivers that
can generate hardware receive timestamps ignore
SOF_TIMESTAMPING_RX_HARDWARE. It is still a good idea to set that flag
in case future drivers pay attention.
If timestamps are reported, they will appear in a control message with
cmsg_level==SOL_SOCKET, cmsg_type==SO_TIMESTAMPING, and a payload like
this:
struct scm_timestamping { struct scm_timestamping {
struct timespec systime; struct timespec systime;
......
...@@ -1738,6 +1738,7 @@ F: include/uapi/linux/bfs_fs.h ...@@ -1738,6 +1738,7 @@ F: include/uapi/linux/bfs_fs.h
BLACKFIN ARCHITECTURE BLACKFIN ARCHITECTURE
M: Steven Miao <realmz6@gmail.com> M: Steven Miao <realmz6@gmail.com>
L: adi-buildroot-devel@lists.sourceforge.net L: adi-buildroot-devel@lists.sourceforge.net
T: git git://git.code.sf.net/p/adi-linux/code
W: http://blackfin.uclinux.org W: http://blackfin.uclinux.org
S: Supported S: Supported
F: arch/blackfin/ F: arch/blackfin/
...@@ -6002,6 +6003,8 @@ F: include/linux/netdevice.h ...@@ -6002,6 +6003,8 @@ F: include/linux/netdevice.h
F: include/uapi/linux/in.h F: include/uapi/linux/in.h
F: include/uapi/linux/net.h F: include/uapi/linux/net.h
F: include/uapi/linux/netdevice.h F: include/uapi/linux/netdevice.h
F: tools/net/
F: tools/testing/selftests/net/
NETWORKING [IPv4/IPv6] NETWORKING [IPv4/IPv6]
M: "David S. Miller" <davem@davemloft.net> M: "David S. Miller" <davem@davemloft.net>
......
VERSION = 3 VERSION = 3
PATCHLEVEL = 14 PATCHLEVEL = 14
SUBLEVEL = 0 SUBLEVEL = 0
EXTRAVERSION = -rc6 EXTRAVERSION = -rc7
NAME = Shuffling Zombie Juror NAME = Shuffling Zombie Juror
# *DOCUMENTATION* # *DOCUMENTATION*
......
...@@ -144,7 +144,7 @@ static inline int test_and_change_bit(int nr, volatile unsigned long *addr) ...@@ -144,7 +144,7 @@ static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
* definition, which doesn't have the same semantics. We don't want to * definition, which doesn't have the same semantics. We don't want to
* use -fno-builtin, so just hide the name ffs. * use -fno-builtin, so just hide the name ffs.
*/ */
#define ffs kernel_ffs #define ffs(x) kernel_ffs(x)
#include <asm-generic/bitops/fls.h> #include <asm-generic/bitops/fls.h>
#include <asm-generic/bitops/__fls.h> #include <asm-generic/bitops/__fls.h>
......
...@@ -98,7 +98,7 @@ static int uncached_add_chunk(struct uncached_pool *uc_pool, int nid) ...@@ -98,7 +98,7 @@ static int uncached_add_chunk(struct uncached_pool *uc_pool, int nid)
/* attempt to allocate a granule's worth of cached memory pages */ /* attempt to allocate a granule's worth of cached memory pages */
page = alloc_pages_exact_node(nid, page = alloc_pages_exact_node(nid,
GFP_KERNEL | __GFP_ZERO | GFP_THISNODE, GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE,
IA64_GRANULE_SHIFT-PAGE_SHIFT); IA64_GRANULE_SHIFT-PAGE_SHIFT);
if (!page) { if (!page) {
mutex_unlock(&uc_pool->add_chunk_mutex); mutex_unlock(&uc_pool->add_chunk_mutex);
......
...@@ -123,7 +123,8 @@ static int __init cbe_ptcal_enable_on_node(int nid, int order) ...@@ -123,7 +123,8 @@ static int __init cbe_ptcal_enable_on_node(int nid, int order)
area->nid = nid; area->nid = nid;
area->order = order; area->order = order;
area->pages = alloc_pages_exact_node(area->nid, GFP_KERNEL|GFP_THISNODE, area->pages = alloc_pages_exact_node(area->nid,
GFP_KERNEL|__GFP_THISNODE,
area->order); area->order);
if (!area->pages) { if (!area->pages) {
......
...@@ -341,10 +341,6 @@ config X86_USE_3DNOW ...@@ -341,10 +341,6 @@ config X86_USE_3DNOW
def_bool y def_bool y
depends on (MCYRIXIII || MK7 || MGEODE_LX) && !UML depends on (MCYRIXIII || MK7 || MGEODE_LX) && !UML
config X86_OOSTORE
def_bool y
depends on (MWINCHIP3D || MWINCHIPC6) && MTRR
# #
# P6_NOPs are a relatively minor optimization that require a family >= # P6_NOPs are a relatively minor optimization that require a family >=
# 6 processor, except that it is broken on certain VIA chips. # 6 processor, except that it is broken on certain VIA chips.
......
...@@ -85,11 +85,7 @@ ...@@ -85,11 +85,7 @@
#else #else
# define smp_rmb() barrier() # define smp_rmb() barrier()
#endif #endif
#ifdef CONFIG_X86_OOSTORE #define smp_wmb() barrier()
# define smp_wmb() wmb()
#else
# define smp_wmb() barrier()
#endif
#define smp_read_barrier_depends() read_barrier_depends() #define smp_read_barrier_depends() read_barrier_depends()
#define set_mb(var, value) do { (void)xchg(&var, value); } while (0) #define set_mb(var, value) do { (void)xchg(&var, value); } while (0)
#else /* !SMP */ #else /* !SMP */
...@@ -100,7 +96,7 @@ ...@@ -100,7 +96,7 @@
#define set_mb(var, value) do { var = value; barrier(); } while (0) #define set_mb(var, value) do { var = value; barrier(); } while (0)
#endif /* SMP */ #endif /* SMP */
#if defined(CONFIG_X86_OOSTORE) || defined(CONFIG_X86_PPRO_FENCE) #if defined(CONFIG_X86_PPRO_FENCE)
/* /*
* For either of these options x86 doesn't have a strong TSO memory * For either of these options x86 doesn't have a strong TSO memory
......
...@@ -237,7 +237,7 @@ memcpy_toio(volatile void __iomem *dst, const void *src, size_t count) ...@@ -237,7 +237,7 @@ memcpy_toio(volatile void __iomem *dst, const void *src, size_t count)
static inline void flush_write_buffers(void) static inline void flush_write_buffers(void)
{ {
#if defined(CONFIG_X86_OOSTORE) || defined(CONFIG_X86_PPRO_FENCE) #if defined(CONFIG_X86_PPRO_FENCE)
asm volatile("lock; addl $0,0(%%esp)": : :"memory"); asm volatile("lock; addl $0,0(%%esp)": : :"memory");
#endif #endif
} }
......
...@@ -26,10 +26,9 @@ ...@@ -26,10 +26,9 @@
# define LOCK_PTR_REG "D" # define LOCK_PTR_REG "D"
#endif #endif
#if defined(CONFIG_X86_32) && \ #if defined(CONFIG_X86_32) && (defined(CONFIG_X86_PPRO_FENCE))
(defined(CONFIG_X86_OOSTORE) || defined(CONFIG_X86_PPRO_FENCE))
/* /*
* On PPro SMP or if we are using OOSTORE, we use a locked operation to unlock * On PPro SMP, we use a locked operation to unlock
* (PPro errata 66, 92) * (PPro errata 66, 92)
*/ */
# define UNLOCK_LOCK_PREFIX LOCK_PREFIX # define UNLOCK_LOCK_PREFIX LOCK_PREFIX
......
...@@ -8,236 +8,6 @@ ...@@ -8,236 +8,6 @@
#include "cpu.h" #include "cpu.h"
#ifdef CONFIG_X86_OOSTORE
static u32 power2(u32 x)
{
u32 s = 1;
while (s <= x)
s <<= 1;
return s >>= 1;
}
/*
* Set up an actual MCR
*/
static void centaur_mcr_insert(int reg, u32 base, u32 size, int key)
{
u32 lo, hi;
hi = base & ~0xFFF;
lo = ~(size-1); /* Size is a power of 2 so this makes a mask */
lo &= ~0xFFF; /* Remove the ctrl value bits */
lo |= key; /* Attribute we wish to set */
wrmsr(reg+MSR_IDT_MCR0, lo, hi);
mtrr_centaur_report_mcr(reg, lo, hi); /* Tell the mtrr driver */
}
/*
* Figure what we can cover with MCR's
*
* Shortcut: We know you can't put 4Gig of RAM on a winchip
*/
static u32 ramtop(void)
{
u32 clip = 0xFFFFFFFFUL;
u32 top = 0;
int i;
for (i = 0; i < e820.nr_map; i++) {
unsigned long start, end;
if (e820.map[i].addr > 0xFFFFFFFFUL)
continue;
/*
* Don't MCR over reserved space. Ignore the ISA hole
* we frob around that catastrophe already
*/
if (e820.map[i].type == E820_RESERVED) {
if (e820.map[i].addr >= 0x100000UL &&
e820.map[i].addr < clip)
clip = e820.map[i].addr;
continue;
}
start = e820.map[i].addr;
end = e820.map[i].addr + e820.map[i].size;
if (start >= end)
continue;
if (end > top)
top = end;
}
/*
* Everything below 'top' should be RAM except for the ISA hole.
* Because of the limited MCR's we want to map NV/ACPI into our
* MCR range for gunk in RAM
*
* Clip might cause us to MCR insufficient RAM but that is an
* acceptable failure mode and should only bite obscure boxes with
* a VESA hole at 15Mb
*
* The second case Clip sometimes kicks in is when the EBDA is marked
* as reserved. Again we fail safe with reasonable results
*/
if (top > clip)
top = clip;
return top;
}
/*
* Compute a set of MCR's to give maximum coverage
*/
static int centaur_mcr_compute(int nr, int key)
{
u32 mem = ramtop();
u32 root = power2(mem);
u32 base = root;
u32 top = root;
u32 floor = 0;
int ct = 0;
while (ct < nr) {
u32 fspace = 0;
u32 high;
u32 low;
/*
* Find the largest block we will fill going upwards
*/
high = power2(mem-top);
/*
* Find the largest block we will fill going downwards
*/
low = base/2;
/*
* Don't fill below 1Mb going downwards as there
* is an ISA hole in the way.
*/
if (base <= 1024*1024)
low = 0;
/*
* See how much space we could cover by filling below
* the ISA hole
*/
if (floor == 0)
fspace = 512*1024;
else if (floor == 512*1024)
fspace = 128*1024;
/* And forget ROM space */
/*
* Now install the largest coverage we get
*/
if (fspace > high && fspace > low) {
centaur_mcr_insert(ct, floor, fspace, key);
floor += fspace;
} else if (high > low) {
centaur_mcr_insert(ct, top, high, key);
top += high;
} else if (low > 0) {
base -= low;
centaur_mcr_insert(ct, base, low, key);
} else
break;
ct++;
}
/*
* We loaded ct values. We now need to set the mask. The caller
* must do this bit.
*/
return ct;
}
static void centaur_create_optimal_mcr(void)
{
int used;
int i;
/*
* Allocate up to 6 mcrs to mark as much of ram as possible
* as write combining and weak write ordered.
*
* To experiment with: Linux never uses stack operations for
* mmio spaces so we could globally enable stack operation wc
*
* Load the registers with type 31 - full write combining, all
* writes weakly ordered.
*/
used = centaur_mcr_compute(6, 31);
/*
* Wipe unused MCRs
*/
for (i = used; i < 8; i++)
wrmsr(MSR_IDT_MCR0+i, 0, 0);
}
static void winchip2_create_optimal_mcr(void)
{
u32 lo, hi;
int used;
int i;
/*
* Allocate up to 6 mcrs to mark as much of ram as possible
* as write combining, weak store ordered.
*
* Load the registers with type 25
* 8 - weak write ordering
* 16 - weak read ordering
* 1 - write combining
*/
used = centaur_mcr_compute(6, 25);
/*
* Mark the registers we are using.
*/
rdmsr(MSR_IDT_MCR_CTRL, lo, hi);
for (i = 0; i < used; i++)
lo |= 1<<(9+i);
wrmsr(MSR_IDT_MCR_CTRL, lo, hi);
/*
* Wipe unused MCRs
*/
for (i = used; i < 8; i++)
wrmsr(MSR_IDT_MCR0+i, 0, 0);
}
/*
* Handle the MCR key on the Winchip 2.
*/
static void winchip2_unprotect_mcr(void)
{
u32 lo, hi;
u32 key;
rdmsr(MSR_IDT_MCR_CTRL, lo, hi);
lo &= ~0x1C0; /* blank bits 8-6 */
key = (lo>>17) & 7;
lo |= key<<6; /* replace with unlock key */
wrmsr(MSR_IDT_MCR_CTRL, lo, hi);
}
static void winchip2_protect_mcr(void)
{
u32 lo, hi;
rdmsr(MSR_IDT_MCR_CTRL, lo, hi);
lo &= ~0x1C0; /* blank bits 8-6 */
wrmsr(MSR_IDT_MCR_CTRL, lo, hi);
}
#endif /* CONFIG_X86_OOSTORE */
#define ACE_PRESENT (1 << 6) #define ACE_PRESENT (1 << 6)
#define ACE_ENABLED (1 << 7) #define ACE_ENABLED (1 << 7)
#define ACE_FCR (1 << 28) /* MSR_VIA_FCR */ #define ACE_FCR (1 << 28) /* MSR_VIA_FCR */
...@@ -362,20 +132,6 @@ static void init_centaur(struct cpuinfo_x86 *c) ...@@ -362,20 +132,6 @@ static void init_centaur(struct cpuinfo_x86 *c)
fcr_clr = DPDC; fcr_clr = DPDC;
printk(KERN_NOTICE "Disabling bugged TSC.\n"); printk(KERN_NOTICE "Disabling bugged TSC.\n");
clear_cpu_cap(c, X86_FEATURE_TSC); clear_cpu_cap(c, X86_FEATURE_TSC);
#ifdef CONFIG_X86_OOSTORE
centaur_create_optimal_mcr();
/*
* Enable:
* write combining on non-stack, non-string
* write combining on string, all types
* weak write ordering
*
* The C6 original lacks weak read order
*
* Note 0x120 is write only on Winchip 1
*/
wrmsr(MSR_IDT_MCR_CTRL, 0x01F0001F, 0);
#endif
break; break;
case 8: case 8:
switch (c->x86_mask) { switch (c->x86_mask) {
...@@ -392,40 +148,12 @@ static void init_centaur(struct cpuinfo_x86 *c) ...@@ -392,40 +148,12 @@ static void init_centaur(struct cpuinfo_x86 *c)
fcr_set = ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK| fcr_set = ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|
E2MMX|EAMD3D; E2MMX|EAMD3D;
fcr_clr = DPDC; fcr_clr = DPDC;
#ifdef CONFIG_X86_OOSTORE
winchip2_unprotect_mcr();
winchip2_create_optimal_mcr();
rdmsr(MSR_IDT_MCR_CTRL, lo, hi);
/*
* Enable:
* write combining on non-stack, non-string
* write combining on string, all types
* weak write ordering
*/
lo |= 31;
wrmsr(MSR_IDT_MCR_CTRL, lo, hi);
winchip2_protect_mcr();
#endif
break; break;
case 9: case 9:
name = "3"; name = "3";
fcr_set = ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK| fcr_set = ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|
E2MMX|EAMD3D; E2MMX|EAMD3D;
fcr_clr = DPDC; fcr_clr = DPDC;
#ifdef CONFIG_X86_OOSTORE
winchip2_unprotect_mcr();
winchip2_create_optimal_mcr();
rdmsr(MSR_IDT_MCR_CTRL, lo, hi);
/*
* Enable:
* write combining on non-stack, non-string
* write combining on string, all types
* weak write ordering
*/
lo |= 31;
wrmsr(MSR_IDT_MCR_CTRL, lo, hi);
winchip2_protect_mcr();
#endif
break; break;
default: default:
name = "??"; name = "??";
......
...@@ -3334,6 +3334,8 @@ static int __init uncore_type_init(struct intel_uncore_type *type) ...@@ -3334,6 +3334,8 @@ static int __init uncore_type_init(struct intel_uncore_type *type)
if (!pmus) if (!pmus)
return -ENOMEM; return -ENOMEM;
type->pmus = pmus;
type->unconstrainted = (struct event_constraint) type->unconstrainted = (struct event_constraint)
__EVENT_CONSTRAINT(0, (1ULL << type->num_counters) - 1, __EVENT_CONSTRAINT(0, (1ULL << type->num_counters) - 1,
0, type->num_counters, 0, 0); 0, type->num_counters, 0, 0);
...@@ -3369,7 +3371,6 @@ static int __init uncore_type_init(struct intel_uncore_type *type) ...@@ -3369,7 +3371,6 @@ static int __init uncore_type_init(struct intel_uncore_type *type)
} }
type->pmu_group = &uncore_pmu_attr_group; type->pmu_group = &uncore_pmu_attr_group;
type->pmus = pmus;
return 0; return 0;
fail: fail:
uncore_type_exit(type); uncore_type_exit(type);
......
...@@ -86,10 +86,19 @@ EXPORT_SYMBOL(__kernel_fpu_begin); ...@@ -86,10 +86,19 @@ EXPORT_SYMBOL(__kernel_fpu_begin);
void __kernel_fpu_end(void) void __kernel_fpu_end(void)
{ {
if (use_eager_fpu()) if (use_eager_fpu()) {
math_state_restore(); /*
else * For eager fpu, most the time, tsk_used_math() is true.
* Restore the user math as we are done with the kernel usage.
* At few instances during thread exit, signal handling etc,
* tsk_used_math() is false. Those few places will take proper
* actions, so we don't need to restore the math here.
*/
if (likely(tsk_used_math(current)))
math_state_restore();
} else {
stts(); stts();
}
} }
EXPORT_SYMBOL(__kernel_fpu_end); EXPORT_SYMBOL(__kernel_fpu_end);
......
...@@ -529,7 +529,7 @@ static void quirk_amd_nb_node(struct pci_dev *dev) ...@@ -529,7 +529,7 @@ static void quirk_amd_nb_node(struct pci_dev *dev)
return; return;
pci_read_config_dword(nb_ht, 0x60, &val); pci_read_config_dword(nb_ht, 0x60, &val);
node = val & 7; node = pcibus_to_node(dev->bus) | (val & 7);
/* /*
* Some hardware may return an invalid node ID, * Some hardware may return an invalid node ID,
* so check it first: * so check it first:
......
...@@ -3002,10 +3002,8 @@ static int cr8_write_interception(struct vcpu_svm *svm) ...@@ -3002,10 +3002,8 @@ static int cr8_write_interception(struct vcpu_svm *svm)
u8 cr8_prev = kvm_get_cr8(&svm->vcpu); u8 cr8_prev = kvm_get_cr8(&svm->vcpu);
/* instruction emulation calls kvm_set_cr8() */ /* instruction emulation calls kvm_set_cr8() */
r = cr_interception(svm); r = cr_interception(svm);
if (irqchip_in_kernel(svm->vcpu.kvm)) { if (irqchip_in_kernel(svm->vcpu.kvm))
clr_cr_intercept(svm, INTERCEPT_CR8_WRITE);
return r; return r;
}
if (cr8_prev <= kvm_get_cr8(&svm->vcpu)) if (cr8_prev <= kvm_get_cr8(&svm->vcpu))
return r; return r;
kvm_run->exit_reason = KVM_EXIT_SET_TPR; kvm_run->exit_reason = KVM_EXIT_SET_TPR;
...@@ -3567,6 +3565,8 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr) ...@@ -3567,6 +3565,8 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr)
if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK)) if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK))
return; return;
clr_cr_intercept(svm, INTERCEPT_CR8_WRITE);
if (irr == -1) if (irr == -1)
return; return;
......
...@@ -140,7 +140,7 @@ bpf_slow_path_byte_msh: ...@@ -140,7 +140,7 @@ bpf_slow_path_byte_msh:
push %r9; \ push %r9; \
push SKBDATA; \ push SKBDATA; \
/* rsi already has offset */ \ /* rsi already has offset */ \
mov $SIZE,%ecx; /* size */ \ mov $SIZE,%edx; /* size */ \
call bpf_internal_load_pointer_neg_helper; \ call bpf_internal_load_pointer_neg_helper; \
test %rax,%rax; \ test %rax,%rax; \
pop SKBDATA; \ pop SKBDATA; \
......
...@@ -40,11 +40,7 @@ ...@@ -40,11 +40,7 @@
#define smp_rmb() barrier() #define smp_rmb() barrier()
#endif /* CONFIG_X86_PPRO_FENCE */ #endif /* CONFIG_X86_PPRO_FENCE */
#ifdef CONFIG_X86_OOSTORE
#define smp_wmb() wmb()
#else /* CONFIG_X86_OOSTORE */
#define smp_wmb() barrier() #define smp_wmb() barrier()
#endif /* CONFIG_X86_OOSTORE */
#define smp_read_barrier_depends() read_barrier_depends() #define smp_read_barrier_depends() read_barrier_depends()
#define set_mb(var, value) do { (void)xchg(&var, value); } while (0) #define set_mb(var, value) do { (void)xchg(&var, value); } while (0)
......
...@@ -71,6 +71,17 @@ static int acpi_sleep_prepare(u32 acpi_state) ...@@ -71,6 +71,17 @@ static int acpi_sleep_prepare(u32 acpi_state)
return 0; return 0;
} }
static bool acpi_sleep_state_supported(u8 sleep_state)
{
acpi_status status;
u8 type_a, type_b;
status = acpi_get_sleep_type_data(sleep_state, &type_a, &type_b);
return ACPI_SUCCESS(status) && (!acpi_gbl_reduced_hardware
|| (acpi_gbl_FADT.sleep_control.address
&& acpi_gbl_FADT.sleep_status.address));
}
#ifdef CONFIG_ACPI_SLEEP #ifdef CONFIG_ACPI_SLEEP
static u32 acpi_target_sleep_state = ACPI_STATE_S0; static u32 acpi_target_sleep_state = ACPI_STATE_S0;
...@@ -604,15 +615,9 @@ static void acpi_sleep_suspend_setup(void) ...@@ -604,15 +615,9 @@ static void acpi_sleep_suspend_setup(void)
{ {
int i; int i;
for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) { for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++)
acpi_status status; if (acpi_sleep_state_supported(i))
u8 type_a, type_b;
status = acpi_get_sleep_type_data(i, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
sleep_states[i] = 1; sleep_states[i] = 1;
}
}
suspend_set_ops(old_suspend_ordering ? suspend_set_ops(old_suspend_ordering ?
&acpi_suspend_ops_old : &acpi_suspend_ops); &acpi_suspend_ops_old : &acpi_suspend_ops);
...@@ -740,11 +745,7 @@ static const struct platform_hibernation_ops acpi_hibernation_ops_old = { ...@@ -740,11 +745,7 @@ static const struct platform_hibernation_ops acpi_hibernation_ops_old = {
static void acpi_sleep_hibernate_setup(void) static void acpi_sleep_hibernate_setup(void)
{ {
acpi_status status; if (!acpi_sleep_state_supported(ACPI_STATE_S4))
u8 type_a, type_b;
status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
if (ACPI_FAILURE(status))
return; return;
hibernation_set_ops(old_suspend_ordering ? hibernation_set_ops(old_suspend_ordering ?
...@@ -793,8 +794,6 @@ static void acpi_power_off(void) ...@@ -793,8 +794,6 @@ static void acpi_power_off(void)
int __init acpi_sleep_init(void) int __init acpi_sleep_init(void)
{ {
acpi_status status;
u8 type_a, type_b;
char supported[ACPI_S_STATE_COUNT * 3 + 1]; char supported[ACPI_S_STATE_COUNT * 3 + 1];
char *pos = supported; char *pos = supported;
int i; int i;
...@@ -806,8 +805,7 @@ int __init acpi_sleep_init(void) ...@@ -806,8 +805,7 @@ int __init acpi_sleep_init(void)
acpi_sleep_suspend_setup(); acpi_sleep_suspend_setup();
acpi_sleep_hibernate_setup(); acpi_sleep_hibernate_setup();
status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); if (acpi_sleep_state_supported(ACPI_STATE_S5)) {
if (ACPI_SUCCESS(status)) {
sleep_states[ACPI_STATE_S5] = 1; sleep_states[ACPI_STATE_S5] = 1;
pm_power_off_prepare = acpi_power_off_prepare; pm_power_off_prepare = acpi_power_off_prepare;
pm_power_off = acpi_power_off; pm_power_off = acpi_power_off;
......
...@@ -4225,8 +4225,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { ...@@ -4225,8 +4225,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
/* devices that don't properly handle queued TRIM commands */ /* devices that don't properly handle queued TRIM commands */
{ "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, },
{ "Crucial_CT???M500SSD1", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, { "Crucial_CT???M500SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, },
{ "Crucial_CT???M500SSD3", NULL, ATA_HORKAGE_NO_NCQ_TRIM, },
/* /*
* Some WD SATA-I drives spin up and down erratically when the link * Some WD SATA-I drives spin up and down erratically when the link
......
...@@ -1129,7 +1129,7 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, ...@@ -1129,7 +1129,7 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
per_cpu(cpufreq_cpu_data, j) = policy; per_cpu(cpufreq_cpu_data, j) = policy;
write_unlock_irqrestore(&cpufreq_driver_lock, flags); write_unlock_irqrestore(&cpufreq_driver_lock, flags);
if (cpufreq_driver->get) { if (cpufreq_driver->get && !cpufreq_driver->setpolicy) {
policy->cur = cpufreq_driver->get(policy->cpu); policy->cur = cpufreq_driver->get(policy->cpu);
if (!policy->cur) { if (!policy->cur) {
pr_err("%s: ->get() failed\n", __func__); pr_err("%s: ->get() failed\n", __func__);
...@@ -2143,7 +2143,7 @@ int cpufreq_update_policy(unsigned int cpu) ...@@ -2143,7 +2143,7 @@ int cpufreq_update_policy(unsigned int cpu)
* BIOS might change freq behind our back * BIOS might change freq behind our back
* -> ask driver for current freq and notify governors about a change * -> ask driver for current freq and notify governors about a change
*/ */
if (cpufreq_driver->get) { if (cpufreq_driver->get && !cpufreq_driver->setpolicy) {
new_policy.cur = cpufreq_driver->get(cpu); new_policy.cur = cpufreq_driver->get(cpu);
if (!policy->cur) { if (!policy->cur) {
pr_debug("Driver did not initialize current freq"); pr_debug("Driver did not initialize current freq");
......
...@@ -259,7 +259,9 @@ int ast_mm_init(struct ast_private *ast) ...@@ -259,7 +259,9 @@ int ast_mm_init(struct ast_private *ast)
ret = ttm_bo_device_init(&ast->ttm.bdev, ret = ttm_bo_device_init(&ast->ttm.bdev,
ast->ttm.bo_global_ref.ref.object, ast->ttm.bo_global_ref.ref.object,
&ast_bo_driver, DRM_FILE_PAGE_OFFSET, &ast_bo_driver,
dev->anon_inode->i_mapping,
DRM_FILE_PAGE_OFFSET,
true); true);
if (ret) { if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret); DRM_ERROR("Error initialising bo driver; %d\n", ret);
...@@ -324,7 +326,6 @@ int ast_bo_create(struct drm_device *dev, int size, int align, ...@@ -324,7 +326,6 @@ int ast_bo_create(struct drm_device *dev, int size, int align,
} }
astbo->bo.bdev = &ast->ttm.bdev; astbo->bo.bdev = &ast->ttm.bdev;
astbo->bo.bdev->dev_mapping = dev->dev_mapping;
ast_ttm_placement(astbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); ast_ttm_placement(astbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
......
...@@ -225,7 +225,9 @@ int bochs_mm_init(struct bochs_device *bochs) ...@@ -225,7 +225,9 @@ int bochs_mm_init(struct bochs_device *bochs)
ret = ttm_bo_device_init(&bochs->ttm.bdev, ret = ttm_bo_device_init(&bochs->ttm.bdev,
bochs->ttm.bo_global_ref.ref.object, bochs->ttm.bo_global_ref.ref.object,
&bochs_bo_driver, DRM_FILE_PAGE_OFFSET, &bochs_bo_driver,
bochs->dev->anon_inode->i_mapping,
DRM_FILE_PAGE_OFFSET,
true); true);
if (ret) { if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret); DRM_ERROR("Error initialising bo driver; %d\n", ret);
...@@ -359,7 +361,7 @@ static int bochs_bo_create(struct drm_device *dev, int size, int align, ...@@ -359,7 +361,7 @@ static int bochs_bo_create(struct drm_device *dev, int size, int align,
} }
bochsbo->bo.bdev = &bochs->ttm.bdev; bochsbo->bo.bdev = &bochs->ttm.bdev;
bochsbo->bo.bdev->dev_mapping = dev->dev_mapping; bochsbo->bo.bdev->dev_mapping = dev->anon_inode->i_mapping;
bochs_ttm_placement(bochsbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); bochs_ttm_placement(bochsbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
......
...@@ -259,7 +259,9 @@ int cirrus_mm_init(struct cirrus_device *cirrus) ...@@ -259,7 +259,9 @@ int cirrus_mm_init(struct cirrus_device *cirrus)
ret = ttm_bo_device_init(&cirrus->ttm.bdev, ret = ttm_bo_device_init(&cirrus->ttm.bdev,
cirrus->ttm.bo_global_ref.ref.object, cirrus->ttm.bo_global_ref.ref.object,
&cirrus_bo_driver, DRM_FILE_PAGE_OFFSET, &cirrus_bo_driver,
dev->anon_inode->i_mapping,
DRM_FILE_PAGE_OFFSET,
true); true);
if (ret) { if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret); DRM_ERROR("Error initialising bo driver; %d\n", ret);
...@@ -329,7 +331,6 @@ int cirrus_bo_create(struct drm_device *dev, int size, int align, ...@@ -329,7 +331,6 @@ int cirrus_bo_create(struct drm_device *dev, int size, int align,
} }
cirrusbo->bo.bdev = &cirrus->ttm.bdev; cirrusbo->bo.bdev = &cirrus->ttm.bdev;
cirrusbo->bo.bdev->dev_mapping = dev->dev_mapping;
cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
......
此差异已折叠。
...@@ -105,9 +105,6 @@ static void drm_mode_validate_flag(struct drm_connector *connector, ...@@ -105,9 +105,6 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
* @maxX: max width for modes * @maxX: max width for modes
* @maxY: max height for modes * @maxY: max height for modes
* *
* LOCKING:
* Caller must hold mode config lock.
*
* Based on the helper callbacks implemented by @connector try to detect all * Based on the helper callbacks implemented by @connector try to detect all
* valid modes. Modes will first be added to the connector's probed_modes list, * valid modes. Modes will first be added to the connector's probed_modes list,
* then culled (based on validity and the @maxX, @maxY parameters) and put into * then culled (based on validity and the @maxX, @maxY parameters) and put into
...@@ -117,8 +114,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector, ...@@ -117,8 +114,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
* @connector vfunc for drivers that use the crtc helpers for output mode * @connector vfunc for drivers that use the crtc helpers for output mode
* filtering and detection. * filtering and detection.
* *
* RETURNS: * Returns:
* Number of modes found on @connector. * The number of modes found on @connector.
*/ */
int drm_helper_probe_single_connector_modes(struct drm_connector *connector, int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY) uint32_t maxX, uint32_t maxY)
...@@ -131,6 +128,8 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, ...@@ -131,6 +128,8 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
int mode_flags = 0; int mode_flags = 0;
bool verbose_prune = true; bool verbose_prune = true;
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
drm_get_connector_name(connector)); drm_get_connector_name(connector));
/* set all modes to the unverified state */ /* set all modes to the unverified state */
...@@ -176,8 +175,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, ...@@ -176,8 +175,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
drm_mode_connector_list_update(connector); drm_mode_connector_list_update(connector);
if (maxX && maxY) if (maxX && maxY)
drm_mode_validate_size(dev, &connector->modes, maxX, drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
maxY, 0);
if (connector->interlace_allowed) if (connector->interlace_allowed)
mode_flags |= DRM_MODE_FLAG_INTERLACE; mode_flags |= DRM_MODE_FLAG_INTERLACE;
...@@ -219,18 +217,19 @@ EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); ...@@ -219,18 +217,19 @@ EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
* drm_helper_encoder_in_use - check if a given encoder is in use * drm_helper_encoder_in_use - check if a given encoder is in use
* @encoder: encoder to check * @encoder: encoder to check
* *
* LOCKING: * Checks whether @encoder is with the current mode setting output configuration
* Caller must hold mode config lock. * in use by any connector. This doesn't mean that it is actually enabled since
* the DPMS state is tracked separately.
* *
* Walk @encoders's DRM device's mode_config and see if it's in use. * Returns:
* * True if @encoder is used, false otherwise.
* RETURNS:
* True if @encoder is part of the mode_config, false otherwise.
*/ */
bool drm_helper_encoder_in_use(struct drm_encoder *encoder) bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
{ {
struct drm_connector *connector; struct drm_connector *connector;
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
list_for_each_entry(connector, &dev->mode_config.connector_list, head) list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder) if (connector->encoder == encoder)
return true; return true;
...@@ -242,19 +241,19 @@ EXPORT_SYMBOL(drm_helper_encoder_in_use); ...@@ -242,19 +241,19 @@ EXPORT_SYMBOL(drm_helper_encoder_in_use);
* drm_helper_crtc_in_use - check if a given CRTC is in a mode_config * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
* @crtc: CRTC to check * @crtc: CRTC to check
* *
* LOCKING: * Checks whether @crtc is with the current mode setting output configuration
* Caller must hold mode config lock. * in use by any connector. This doesn't mean that it is actually enabled since
* the DPMS state is tracked separately.
* *
* Walk @crtc's DRM device's mode_config and see if it's in use. * Returns:
* * True if @crtc is used, false otherwise.
* RETURNS:
* True if @crtc is part of the mode_config, false otherwise.
*/ */
bool drm_helper_crtc_in_use(struct drm_crtc *crtc) bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
/* FIXME: Locking around list access? */
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
return true; return true;
...@@ -283,11 +282,11 @@ drm_encoder_disable(struct drm_encoder *encoder) ...@@ -283,11 +282,11 @@ drm_encoder_disable(struct drm_encoder *encoder)
* drm_helper_disable_unused_functions - disable unused objects * drm_helper_disable_unused_functions - disable unused objects
* @dev: DRM device * @dev: DRM device
* *
* LOCKING: * This function walks through the entire mode setting configuration of @dev. It
* Caller must hold mode config lock. * will remove any crtc links of unused encoders and encoder links of
* * disconnected connectors. Then it will disable all unused encoders and crtcs
* If an connector or CRTC isn't part of @dev's mode_config, it can be disabled * either by calling their disable callback if available or by calling their
* by calling its dpms function, which should power it off. * dpms callback with DRM_MODE_DPMS_OFF.
*/ */
void drm_helper_disable_unused_functions(struct drm_device *dev) void drm_helper_disable_unused_functions(struct drm_device *dev)
{ {
...@@ -295,6 +294,8 @@ void drm_helper_disable_unused_functions(struct drm_device *dev) ...@@ -295,6 +294,8 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
struct drm_connector *connector; struct drm_connector *connector;
struct drm_crtc *crtc; struct drm_crtc *crtc;
drm_warn_on_modeset_not_all_locked(dev);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder) if (!connector->encoder)
continue; continue;
...@@ -355,9 +356,6 @@ drm_crtc_prepare_encoders(struct drm_device *dev) ...@@ -355,9 +356,6 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
* @y: vertical offset into the surface * @y: vertical offset into the surface
* @old_fb: old framebuffer, for cleanup * @old_fb: old framebuffer, for cleanup
* *
* LOCKING:
* Caller must hold mode config lock.
*
* Try to set @mode on @crtc. Give @crtc and its associated connectors a chance * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance
* to fixup or reject the mode prior to trying to set it. This is an internal * to fixup or reject the mode prior to trying to set it. This is an internal
* helper that drivers could e.g. use to update properties that require the * helper that drivers could e.g. use to update properties that require the
...@@ -367,8 +365,8 @@ drm_crtc_prepare_encoders(struct drm_device *dev) ...@@ -367,8 +365,8 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
* drm_crtc_helper_set_config() helper function to drive the mode setting * drm_crtc_helper_set_config() helper function to drive the mode setting
* sequence. * sequence.
* *
* RETURNS: * Returns:
* True if the mode was set successfully, or false otherwise. * True if the mode was set successfully, false otherwise.
*/ */
bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *mode,
...@@ -384,6 +382,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, ...@@ -384,6 +382,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
struct drm_encoder *encoder; struct drm_encoder *encoder;
bool ret = true; bool ret = true;
drm_warn_on_modeset_not_all_locked(dev);
saved_enabled = crtc->enabled; saved_enabled = crtc->enabled;
crtc->enabled = drm_helper_crtc_in_use(crtc); crtc->enabled = drm_helper_crtc_in_use(crtc);
if (!crtc->enabled) if (!crtc->enabled)
...@@ -560,17 +560,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) ...@@ -560,17 +560,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
* drm_crtc_helper_set_config - set a new config from userspace * drm_crtc_helper_set_config - set a new config from userspace
* @set: mode set configuration * @set: mode set configuration
* *
* LOCKING:
* Caller must hold mode config lock.
*
* Setup a new configuration, provided by the upper layers (either an ioctl call * Setup a new configuration, provided by the upper layers (either an ioctl call
* from userspace or internally e.g. from the fbdev suppport code) in @set, and * from userspace or internally e.g. from the fbdev suppport code) in @set, and
* enable it. This is the main helper functions for drivers that implement * enable it. This is the main helper functions for drivers that implement
* kernel mode setting with the crtc helper functions and the assorted * kernel mode setting with the crtc helper functions and the assorted
* ->prepare(), ->modeset() and ->commit() helper callbacks. * ->prepare(), ->modeset() and ->commit() helper callbacks.
* *
* RETURNS: * Returns:
* Returns 0 on success, -ERRNO on failure. * Returns 0 on success, negative errno numbers on failure.
*/ */
int drm_crtc_helper_set_config(struct drm_mode_set *set) int drm_crtc_helper_set_config(struct drm_mode_set *set)
{ {
...@@ -612,6 +609,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) ...@@ -612,6 +609,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
dev = set->crtc->dev; dev = set->crtc->dev;
drm_warn_on_modeset_not_all_locked(dev);
/* /*
* Allocate space for the backup of all (non-pointer) encoder and * Allocate space for the backup of all (non-pointer) encoder and
* connector data. * connector data.
...@@ -924,8 +923,16 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode) ...@@ -924,8 +923,16 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
} }
EXPORT_SYMBOL(drm_helper_connector_dpms); EXPORT_SYMBOL(drm_helper_connector_dpms);
int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, /**
struct drm_mode_fb_cmd2 *mode_cmd) * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata
* @fb: drm_framebuffer object to fill out
* @mode_cmd: metadata from the userspace fb creation request
*
* This helper can be used in a drivers fb_create callback to pre-fill the fb's
* metadata fields.
*/
void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
struct drm_mode_fb_cmd2 *mode_cmd)
{ {
int i; int i;
...@@ -938,17 +945,36 @@ int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, ...@@ -938,17 +945,36 @@ int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
&fb->bits_per_pixel); &fb->bits_per_pixel);
fb->pixel_format = mode_cmd->pixel_format; fb->pixel_format = mode_cmd->pixel_format;
return 0;
} }
EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
int drm_helper_resume_force_mode(struct drm_device *dev) /**
* drm_helper_resume_force_mode - force-restore mode setting configuration
* @dev: drm_device which should be restored
*
* Drivers which use the mode setting helpers can use this function to
* force-restore the mode setting configuration e.g. on resume or when something
* else might have trampled over the hw state (like some overzealous old BIOSen
* tended to do).
*
* This helper doesn't provide a error return value since restoring the old
* config should never fail due to resource allocation issues since the driver
* has successfully set the restored configuration already. Hence this should
* boil down to the equivalent of a few dpms on calls, which also don't provide
* an error code.
*
* Drivers where simply restoring an old configuration again might fail (e.g.
* due to slight differences in allocating shared resources when the
* configuration is restored in a different order than when userspace set it up)
* need to use their own restore logic.
*/
void drm_helper_resume_force_mode(struct drm_device *dev)
{ {
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_crtc_helper_funcs *crtc_funcs; struct drm_crtc_helper_funcs *crtc_funcs;
int ret, encoder_dpms; int encoder_dpms;
bool ret;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
...@@ -958,6 +984,7 @@ int drm_helper_resume_force_mode(struct drm_device *dev) ...@@ -958,6 +984,7 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, ret = drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb); crtc->x, crtc->y, crtc->fb);
/* Restoring the old config should never fail! */
if (ret == false) if (ret == false)
DRM_ERROR("failed to set mode on crtc %p\n", crtc); DRM_ERROR("failed to set mode on crtc %p\n", crtc);
...@@ -980,12 +1007,28 @@ int drm_helper_resume_force_mode(struct drm_device *dev) ...@@ -980,12 +1007,28 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
drm_helper_choose_crtc_dpms(crtc)); drm_helper_choose_crtc_dpms(crtc));
} }
} }
/* disable the unused connectors while restoring the modesetting */ /* disable the unused connectors while restoring the modesetting */
drm_helper_disable_unused_functions(dev); drm_helper_disable_unused_functions(dev);
return 0;
} }
EXPORT_SYMBOL(drm_helper_resume_force_mode); EXPORT_SYMBOL(drm_helper_resume_force_mode);
/**
* drm_kms_helper_hotplug_event - fire off KMS hotplug events
* @dev: drm_device whose connector state changed
*
* This function fires off the uevent for userspace and also calls the
* output_poll_changed function, which is most commonly used to inform the fbdev
* emulation code and allow it to update the fbcon output configuration.
*
* Drivers should call this from their hotplug handling code when a change is
* detected. Note that this function does not do any output detection of its
* own, like drm_helper_hpd_irq_event() does - this is assumed to be done by the
* driver already.
*
* This function must be called from process context with no mode
* setting locks held.
*/
void drm_kms_helper_hotplug_event(struct drm_device *dev) void drm_kms_helper_hotplug_event(struct drm_device *dev)
{ {
/* send a uevent + call fbdev */ /* send a uevent + call fbdev */
...@@ -1054,6 +1097,16 @@ static void output_poll_execute(struct work_struct *work) ...@@ -1054,6 +1097,16 @@ static void output_poll_execute(struct work_struct *work)
schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
} }
/**
* drm_kms_helper_poll_disable - disable output polling
* @dev: drm_device
*
* This function disables the output polling work.
*
* Drivers can call this helper from their device suspend implementation. It is
* not an error to call this even when output polling isn't enabled or arlready
* disabled.
*/
void drm_kms_helper_poll_disable(struct drm_device *dev) void drm_kms_helper_poll_disable(struct drm_device *dev)
{ {
if (!dev->mode_config.poll_enabled) if (!dev->mode_config.poll_enabled)
...@@ -1062,6 +1115,16 @@ void drm_kms_helper_poll_disable(struct drm_device *dev) ...@@ -1062,6 +1115,16 @@ void drm_kms_helper_poll_disable(struct drm_device *dev)
} }
EXPORT_SYMBOL(drm_kms_helper_poll_disable); EXPORT_SYMBOL(drm_kms_helper_poll_disable);
/**
* drm_kms_helper_poll_enable - re-enable output polling.
* @dev: drm_device
*
* This function re-enables the output polling work.
*
* Drivers can call this helper from their device resume implementation. It is
* an error to call this when the output polling support has not yet been set
* up.
*/
void drm_kms_helper_poll_enable(struct drm_device *dev) void drm_kms_helper_poll_enable(struct drm_device *dev)
{ {
bool poll = false; bool poll = false;
...@@ -1081,6 +1144,25 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) ...@@ -1081,6 +1144,25 @@ void drm_kms_helper_poll_enable(struct drm_device *dev)
} }
EXPORT_SYMBOL(drm_kms_helper_poll_enable); EXPORT_SYMBOL(drm_kms_helper_poll_enable);
/**
* drm_kms_helper_poll_init - initialize and enable output polling
* @dev: drm_device
*
* This function intializes and then also enables output polling support for
* @dev. Drivers which do not have reliable hotplug support in hardware can use
* this helper infrastructure to regularly poll such connectors for changes in
* their connection state.
*
* Drivers can control which connectors are polled by setting the
* DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags. On
* connectors where probing live outputs can result in visual distortion drivers
* should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this.
* Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are
* completely ignored by the polling logic.
*
* Note that a connector can be both polled and probed from the hotplug handler,
* in case the hotplug interrupt is known to be unreliable.
*/
void drm_kms_helper_poll_init(struct drm_device *dev) void drm_kms_helper_poll_init(struct drm_device *dev)
{ {
INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute); INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);
...@@ -1090,12 +1172,39 @@ void drm_kms_helper_poll_init(struct drm_device *dev) ...@@ -1090,12 +1172,39 @@ void drm_kms_helper_poll_init(struct drm_device *dev)
} }
EXPORT_SYMBOL(drm_kms_helper_poll_init); EXPORT_SYMBOL(drm_kms_helper_poll_init);
/**
* drm_kms_helper_poll_fini - disable output polling and clean it up
* @dev: drm_device
*/
void drm_kms_helper_poll_fini(struct drm_device *dev) void drm_kms_helper_poll_fini(struct drm_device *dev)
{ {
drm_kms_helper_poll_disable(dev); drm_kms_helper_poll_disable(dev);
} }
EXPORT_SYMBOL(drm_kms_helper_poll_fini); EXPORT_SYMBOL(drm_kms_helper_poll_fini);
/**
* drm_helper_hpd_irq_event - hotplug processing
* @dev: drm_device
*
* Drivers can use this helper function to run a detect cycle on all connectors
* which have the DRM_CONNECTOR_POLL_HPD flag set in their &polled member. All
* other connectors are ignored, which is useful to avoid reprobing fixed
* panels.
*
* This helper function is useful for drivers which can't or don't track hotplug
* interrupts for each connector.
*
* Drivers which support hotplug interrupts for each connector individually and
* which have a more fine-grained detect logic should bypass this code and
* directly call drm_kms_helper_hotplug_event() in case the connector state
* changed.
*
* This function must be called from process context with no mode
* setting locks held.
*
* Note that a connector can be both polled and probed from the hotplug handler,
* in case the hotplug interrupt is known to be unreliable.
*/
bool drm_helper_hpd_irq_event(struct drm_device *dev) bool drm_helper_hpd_irq_event(struct drm_device *dev)
{ {
struct drm_connector *connector; struct drm_connector *connector;
......
/*
* Copyright © 2006 Keith Packard
* Copyright © 2007-2008 Dave Airlie
* Copyright © 2007-2008 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
* Copyright © 2014 Intel Corporation
* Daniel Vetter <daniel.vetter@ffwll.ch>
*
* 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
*/
/*
* This header file contains mode setting related functions and definitions
* which are only used within the drm module as internal implementation details
* and are not exported to drivers.
*/
int drm_mode_object_get(struct drm_device *dev,
struct drm_mode_object *obj, uint32_t obj_type);
void drm_mode_object_put(struct drm_device *dev,
struct drm_mode_object *object);
...@@ -346,3 +346,399 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw) ...@@ -346,3 +346,399 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw)
} }
} }
EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
/**
* DOC: dp helpers
*
* The DisplayPort AUX channel is an abstraction to allow generic, driver-
* independent access to AUX functionality. Drivers can take advantage of
* this by filling in the fields of the drm_dp_aux structure.
*
* Transactions are described using a hardware-independent drm_dp_aux_msg
* structure, which is passed into a driver's .transfer() implementation.
* Both native and I2C-over-AUX transactions are supported.
*/
static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
unsigned int offset, void *buffer, size_t size)
{
struct drm_dp_aux_msg msg;
unsigned int retry;
int err;
memset(&msg, 0, sizeof(msg));
msg.address = offset;
msg.request = request;
msg.buffer = buffer;
msg.size = size;
/*
* The specification doesn't give any recommendation on how often to
* retry native transactions, so retry 7 times like for I2C-over-AUX
* transactions.
*/
for (retry = 0; retry < 7; retry++) {
err = aux->transfer(aux, &msg);
if (err < 0) {
if (err == -EBUSY)
continue;
return err;
}
if (err < size)
return -EPROTO;
switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
case DP_AUX_NATIVE_REPLY_ACK:
return err;
case DP_AUX_NATIVE_REPLY_NACK:
return -EIO;
case DP_AUX_NATIVE_REPLY_DEFER:
usleep_range(400, 500);
break;
}
}
DRM_ERROR("too many retries, giving up\n");
return -EIO;
}
/**
* drm_dp_dpcd_read() - read a series of bytes from the DPCD
* @aux: DisplayPort AUX channel
* @offset: address of the (first) register to read
* @buffer: buffer to store the register values
* @size: number of bytes in @buffer
*
* Returns the number of bytes transferred on success, or a negative error
* code on failure. -EIO is returned if the request was NAKed by the sink or
* if the retry count was exceeded. If not all bytes were transferred, this
* function returns -EPROTO. Errors from the underlying AUX channel transfer
* function, with the exception of -EBUSY (which causes the transaction to
* be retried), are propagated to the caller.
*/
ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
void *buffer, size_t size)
{
return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer,
size);
}
EXPORT_SYMBOL(drm_dp_dpcd_read);
/**
* drm_dp_dpcd_write() - write a series of bytes to the DPCD
* @aux: DisplayPort AUX channel
* @offset: address of the (first) register to write
* @buffer: buffer containing the values to write
* @size: number of bytes in @buffer
*
* Returns the number of bytes transferred on success, or a negative error
* code on failure. -EIO is returned if the request was NAKed by the sink or
* if the retry count was exceeded. If not all bytes were transferred, this
* function returns -EPROTO. Errors from the underlying AUX channel transfer
* function, with the exception of -EBUSY (which causes the transaction to
* be retried), are propagated to the caller.
*/
ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
void *buffer, size_t size)
{
return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer,
size);
}
EXPORT_SYMBOL(drm_dp_dpcd_write);
/**
* drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207)
* @aux: DisplayPort AUX channel
* @status: buffer to store the link status in (must be at least 6 bytes)
*
* Returns the number of bytes transferred on success or a negative error
* code on failure.
*/
int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
u8 status[DP_LINK_STATUS_SIZE])
{
return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status,
DP_LINK_STATUS_SIZE);
}
EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
/**
* drm_dp_link_probe() - probe a DisplayPort link for capabilities
* @aux: DisplayPort AUX channel
* @link: pointer to structure in which to return link capabilities
*
* The structure filled in by this function can usually be passed directly
* into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
* configure the link based on the link's capabilities.
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
{
u8 values[3];
int err;
memset(link, 0, sizeof(*link));
err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
if (err < 0)
return err;
link->revision = values[0];
link->rate = drm_dp_bw_code_to_link_rate(values[1]);
link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
if (values[2] & DP_ENHANCED_FRAME_CAP)
link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
return 0;
}
EXPORT_SYMBOL(drm_dp_link_probe);
/**
* drm_dp_link_power_up() - power up a DisplayPort link
* @aux: DisplayPort AUX channel
* @link: pointer to a structure containing the link configuration
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
{
u8 value;
int err;
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
if (link->revision < 0x11)
return 0;
err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
if (err < 0)
return err;
value &= ~DP_SET_POWER_MASK;
value |= DP_SET_POWER_D0;
err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
if (err < 0)
return err;
/*
* According to the DP 1.1 specification, a "Sink Device must exit the
* power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
* Control Field" (register 0x600).
*/
usleep_range(1000, 2000);
return 0;
}
EXPORT_SYMBOL(drm_dp_link_power_up);
/**
* drm_dp_link_configure() - configure a DisplayPort link
* @aux: DisplayPort AUX channel
* @link: pointer to a structure containing the link configuration
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
{
u8 values[2];
int err;
values[0] = drm_dp_link_rate_to_bw_code(link->rate);
values[1] = link->num_lanes;
if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(drm_dp_link_configure);
/*
* I2C-over-AUX implementation
*/
static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_10BIT_ADDR;
}
/*
* Transfer a single I2C-over-AUX message and handle various error conditions,
* retrying the transaction as appropriate.
*/
static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
{
unsigned int retry;
int err;
/*
* DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
* is required to retry at least seven times upon receiving AUX_DEFER
* before giving up the AUX transaction.
*/
for (retry = 0; retry < 7; retry++) {
err = aux->transfer(aux, msg);
if (err < 0) {
if (err == -EBUSY)
continue;
DRM_DEBUG_KMS("transaction failed: %d\n", err);
return err;
}
if (err < msg->size)
return -EPROTO;
switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) {
case DP_AUX_NATIVE_REPLY_ACK:
/*
* For I2C-over-AUX transactions this isn't enough, we
* need to check for the I2C ACK reply.
*/
break;
case DP_AUX_NATIVE_REPLY_NACK:
DRM_DEBUG_KMS("native nack\n");
return -EREMOTEIO;
case DP_AUX_NATIVE_REPLY_DEFER:
DRM_DEBUG_KMS("native defer");
/*
* We could check for I2C bit rate capabilities and if
* available adjust this interval. We could also be
* more careful with DP-to-legacy adapters where a
* long legacy cable may force very low I2C bit rates.
*
* For now just defer for long enough to hopefully be
* safe for all use-cases.
*/
usleep_range(500, 600);
continue;
default:
DRM_ERROR("invalid native reply %#04x\n", msg->reply);
return -EREMOTEIO;
}
switch (msg->reply & DP_AUX_I2C_REPLY_MASK) {
case DP_AUX_I2C_REPLY_ACK:
/*
* Both native ACK and I2C ACK replies received. We
* can assume the transfer was successful.
*/
return 0;
case DP_AUX_I2C_REPLY_NACK:
DRM_DEBUG_KMS("I2C nack\n");
return -EREMOTEIO;
case DP_AUX_I2C_REPLY_DEFER:
DRM_DEBUG_KMS("I2C defer\n");
usleep_range(400, 500);
continue;
default:
DRM_ERROR("invalid I2C reply %#04x\n", msg->reply);
return -EREMOTEIO;
}
}
DRM_ERROR("too many retries, giving up\n");
return -EREMOTEIO;
}
static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
int num)
{
struct drm_dp_aux *aux = adapter->algo_data;
unsigned int i, j;
for (i = 0; i < num; i++) {
struct drm_dp_aux_msg msg;
int err;
/*
* Many hardware implementations support FIFOs larger than a
* single byte, but it has been empirically determined that
* transferring data in larger chunks can actually lead to
* decreased performance. Therefore each message is simply
* transferred byte-by-byte.
*/
for (j = 0; j < msgs[i].len; j++) {
memset(&msg, 0, sizeof(msg));
msg.address = msgs[i].addr;
msg.request = (msgs[i].flags & I2C_M_RD) ?
DP_AUX_I2C_READ :
DP_AUX_I2C_WRITE;
/*
* All messages except the last one are middle-of-
* transfer messages.
*/
if ((i < num - 1) || (j < msgs[i].len - 1))
msg.request |= DP_AUX_I2C_MOT;
msg.buffer = msgs[i].buf + j;
msg.size = 1;
err = drm_dp_i2c_do_msg(aux, &msg);
if (err < 0)
return err;
}
}
return num;
}
static const struct i2c_algorithm drm_dp_i2c_algo = {
.functionality = drm_dp_i2c_functionality,
.master_xfer = drm_dp_i2c_xfer,
};
/**
* drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
* @aux: DisplayPort AUX channel
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
{
aux->ddc.algo = &drm_dp_i2c_algo;
aux->ddc.algo_data = aux;
aux->ddc.retries = 3;
aux->ddc.class = I2C_CLASS_DDC;
aux->ddc.owner = THIS_MODULE;
aux->ddc.dev.parent = aux->dev;
aux->ddc.dev.of_node = aux->dev->of_node;
strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
sizeof(aux->ddc.name));
return i2c_add_adapter(&aux->ddc);
}
EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus);
/**
* drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
* @aux: DisplayPort AUX channel
*/
void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux)
{
i2c_del_adapter(&aux->ddc);
}
EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus);
...@@ -344,7 +344,7 @@ long drm_ioctl(struct file *filp, ...@@ -344,7 +344,7 @@ long drm_ioctl(struct file *filp,
DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n", DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
task_pid_nr(current), task_pid_nr(current),
(long)old_encode_dev(file_priv->minor->device), (long)old_encode_dev(file_priv->minor->kdev->devt),
file_priv->authenticated, ioctl->name); file_priv->authenticated, ioctl->name);
/* Do not trust userspace, use our own definition */ /* Do not trust userspace, use our own definition */
...@@ -402,7 +402,7 @@ long drm_ioctl(struct file *filp, ...@@ -402,7 +402,7 @@ long drm_ioctl(struct file *filp,
if (!ioctl) if (!ioctl)
DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
task_pid_nr(current), task_pid_nr(current),
(long)old_encode_dev(file_priv->minor->device), (long)old_encode_dev(file_priv->minor->kdev->devt),
file_priv->authenticated, cmd, nr); file_priv->authenticated, cmd, nr);
if (kdata != stack_kdata) if (kdata != stack_kdata)
......
...@@ -1098,10 +1098,14 @@ EXPORT_SYMBOL(drm_edid_is_valid); ...@@ -1098,10 +1098,14 @@ EXPORT_SYMBOL(drm_edid_is_valid);
/** /**
* Get EDID information via I2C. * Get EDID information via I2C.
* *
* \param adapter : i2c device adaptor * @adapter : i2c device adaptor
* \param buf : EDID data buffer to be filled * @buf: EDID data buffer to be filled
* \param len : EDID data buffer length * @block: 128 byte EDID block to start fetching from
* \return 0 on success or -1 on failure. * @len: EDID data buffer length to fetch
*
* Returns:
*
* 0 on success or -1 on failure.
* *
* Try to fetch EDID information by calling i2c driver function. * Try to fetch EDID information by calling i2c driver function.
*/ */
...@@ -1243,9 +1247,11 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ...@@ -1243,9 +1247,11 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
/** /**
* Probe DDC presence. * Probe DDC presence.
* @adapter: i2c adapter to probe
*
* Returns:
* *
* \param adapter : i2c device adaptor * 1 on success
* \return 1 on success
*/ */
bool bool
drm_probe_ddc(struct i2c_adapter *adapter) drm_probe_ddc(struct i2c_adapter *adapter)
...@@ -1586,8 +1592,10 @@ bad_std_timing(u8 a, u8 b) ...@@ -1586,8 +1592,10 @@ bad_std_timing(u8 a, u8 b)
/** /**
* drm_mode_std - convert standard mode info (width, height, refresh) into mode * drm_mode_std - convert standard mode info (width, height, refresh) into mode
* @connector: connector of for the EDID block
* @edid: EDID block to scan
* @t: standard timing params * @t: standard timing params
* @timing_level: standard timing level * @revision: standard timing level
* *
* Take the standard timing params (in this case width, aspect, and refresh) * Take the standard timing params (in this case width, aspect, and refresh)
* and convert them into a real mode using CVT/GTF/DMT. * and convert them into a real mode using CVT/GTF/DMT.
...@@ -2132,6 +2140,7 @@ do_established_modes(struct detailed_timing *timing, void *c) ...@@ -2132,6 +2140,7 @@ do_established_modes(struct detailed_timing *timing, void *c)
/** /**
* add_established_modes - get est. modes from EDID and add them * add_established_modes - get est. modes from EDID and add them
* @connector: connector of for the EDID block
* @edid: EDID block to scan * @edid: EDID block to scan
* *
* Each EDID block contains a bitmap of the supported "established modes" list * Each EDID block contains a bitmap of the supported "established modes" list
...@@ -2194,6 +2203,7 @@ do_standard_modes(struct detailed_timing *timing, void *c) ...@@ -2194,6 +2203,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
/** /**
* add_standard_modes - get std. modes from EDID and add them * add_standard_modes - get std. modes from EDID and add them
* @connector: connector of for the EDID block
* @edid: EDID block to scan * @edid: EDID block to scan
* *
* Standard modes can be calculated using the appropriate standard (DMT, * Standard modes can be calculated using the appropriate standard (DMT,
...@@ -2580,6 +2590,9 @@ drm_display_mode_from_vic_index(struct drm_connector *connector, ...@@ -2580,6 +2590,9 @@ drm_display_mode_from_vic_index(struct drm_connector *connector,
return NULL; return NULL;
newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]); newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
if (!newmode)
return NULL;
newmode->vrefresh = 0; newmode->vrefresh = 0;
return newmode; return newmode;
...@@ -3300,6 +3313,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor); ...@@ -3300,6 +3313,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
/** /**
* drm_detect_monitor_audio - check monitor audio capability * drm_detect_monitor_audio - check monitor audio capability
* @edid: EDID block to scan
* *
* Monitor should have CEA extension block. * Monitor should have CEA extension block.
* If monitor has 'basic audio', but no CEA audio blocks, it's 'basic * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
...@@ -3345,6 +3359,7 @@ EXPORT_SYMBOL(drm_detect_monitor_audio); ...@@ -3345,6 +3359,7 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);
/** /**
* drm_rgb_quant_range_selectable - is RGB quantization range selectable? * drm_rgb_quant_range_selectable - is RGB quantization range selectable?
* @edid: EDID block to scan
* *
* Check whether the monitor reports the RGB quantization range selection * Check whether the monitor reports the RGB quantization range selection
* as supported. The AVI infoframe can then be used to inform the monitor * as supported. The AVI infoframe can then be used to inform the monitor
...@@ -3564,8 +3579,8 @@ void drm_set_preferred_mode(struct drm_connector *connector, ...@@ -3564,8 +3579,8 @@ void drm_set_preferred_mode(struct drm_connector *connector,
struct drm_display_mode *mode; struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) { list_for_each_entry(mode, &connector->probed_modes, head) {
if (drm_mode_width(mode) == hpref && if (mode->hdisplay == hpref &&
drm_mode_height(mode) == vpref) mode->vdisplay == vpref)
mode->type |= DRM_MODE_TYPE_PREFERRED; mode->type |= DRM_MODE_TYPE_PREFERRED;
} }
} }
...@@ -3599,6 +3614,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, ...@@ -3599,6 +3614,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
return 0; return 0;
} }
......
...@@ -516,6 +516,9 @@ int drm_fb_helper_init(struct drm_device *dev, ...@@ -516,6 +516,9 @@ int drm_fb_helper_init(struct drm_device *dev,
struct drm_crtc *crtc; struct drm_crtc *crtc;
int i; int i;
if (!max_conn_count)
return -EINVAL;
fb_helper->dev = dev; fb_helper->dev = dev;
INIT_LIST_HEAD(&fb_helper->kernel_fb_list); INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
...@@ -809,8 +812,6 @@ int drm_fb_helper_set_par(struct fb_info *info) ...@@ -809,8 +812,6 @@ int drm_fb_helper_set_par(struct fb_info *info)
struct drm_fb_helper *fb_helper = info->par; struct drm_fb_helper *fb_helper = info->par;
struct drm_device *dev = fb_helper->dev; struct drm_device *dev = fb_helper->dev;
struct fb_var_screeninfo *var = &info->var; struct fb_var_screeninfo *var = &info->var;
int ret;
int i;
if (var->pixclock != 0) { if (var->pixclock != 0) {
DRM_ERROR("PIXEL CLOCK SET\n"); DRM_ERROR("PIXEL CLOCK SET\n");
...@@ -818,13 +819,7 @@ int drm_fb_helper_set_par(struct fb_info *info) ...@@ -818,13 +819,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
} }
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
for (i = 0; i < fb_helper->crtc_count; i++) { drm_fb_helper_restore_fbdev_mode(fb_helper);
ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set);
if (ret) {
drm_modeset_unlock_all(dev);
return ret;
}
}
drm_modeset_unlock_all(dev); drm_modeset_unlock_all(dev);
if (fb_helper->delayed_hotplug) { if (fb_helper->delayed_hotplug) {
...@@ -1141,8 +1136,8 @@ struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector * ...@@ -1141,8 +1136,8 @@ struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *
struct drm_display_mode *mode; struct drm_display_mode *mode;
list_for_each_entry(mode, &fb_connector->connector->modes, head) { list_for_each_entry(mode, &fb_connector->connector->modes, head) {
if (drm_mode_width(mode) > width || if (mode->hdisplay > width ||
drm_mode_height(mode) > height) mode->vdisplay > height)
continue; continue;
if (mode->type & DRM_MODE_TYPE_PREFERRED) if (mode->type & DRM_MODE_TYPE_PREFERRED)
return mode; return mode;
......
...@@ -39,12 +39,12 @@ ...@@ -39,12 +39,12 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
/* from BKL pushdown: note that nothing else serializes idr_find() */ /* from BKL pushdown */
DEFINE_MUTEX(drm_global_mutex); DEFINE_MUTEX(drm_global_mutex);
EXPORT_SYMBOL(drm_global_mutex); EXPORT_SYMBOL(drm_global_mutex);
static int drm_open_helper(struct inode *inode, struct file *filp, static int drm_open_helper(struct inode *inode, struct file *filp,
struct drm_device * dev); struct drm_minor *minor);
static int drm_setup(struct drm_device * dev) static int drm_setup(struct drm_device * dev)
{ {
...@@ -79,38 +79,23 @@ static int drm_setup(struct drm_device * dev) ...@@ -79,38 +79,23 @@ static int drm_setup(struct drm_device * dev)
*/ */
int drm_open(struct inode *inode, struct file *filp) int drm_open(struct inode *inode, struct file *filp)
{ {
struct drm_device *dev = NULL; struct drm_device *dev;
int minor_id = iminor(inode);
struct drm_minor *minor; struct drm_minor *minor;
int retcode = 0; int retcode;
int need_setup = 0; int need_setup = 0;
struct address_space *old_mapping;
struct address_space *old_imapping;
minor = idr_find(&drm_minors_idr, minor_id);
if (!minor)
return -ENODEV;
if (!(dev = minor->dev))
return -ENODEV;
if (drm_device_is_unplugged(dev)) minor = drm_minor_acquire(iminor(inode));
return -ENODEV; if (IS_ERR(minor))
return PTR_ERR(minor);
dev = minor->dev;
if (!dev->open_count++) if (!dev->open_count++)
need_setup = 1; need_setup = 1;
mutex_lock(&dev->struct_mutex);
old_imapping = inode->i_mapping;
old_mapping = dev->dev_mapping;
if (old_mapping == NULL)
dev->dev_mapping = &inode->i_data;
/* ihold ensures nobody can remove inode with our i_data */
ihold(container_of(dev->dev_mapping, struct inode, i_data));
inode->i_mapping = dev->dev_mapping;
filp->f_mapping = dev->dev_mapping;
mutex_unlock(&dev->struct_mutex);
retcode = drm_open_helper(inode, filp, dev); /* share address_space across all char-devs of a single device */
filp->f_mapping = dev->anon_inode->i_mapping;
retcode = drm_open_helper(inode, filp, minor);
if (retcode) if (retcode)
goto err_undo; goto err_undo;
if (need_setup) { if (need_setup) {
...@@ -121,13 +106,8 @@ int drm_open(struct inode *inode, struct file *filp) ...@@ -121,13 +106,8 @@ int drm_open(struct inode *inode, struct file *filp)
return 0; return 0;
err_undo: err_undo:
mutex_lock(&dev->struct_mutex);
filp->f_mapping = old_imapping;
inode->i_mapping = old_imapping;
iput(container_of(dev->dev_mapping, struct inode, i_data));
dev->dev_mapping = old_mapping;
mutex_unlock(&dev->struct_mutex);
dev->open_count--; dev->open_count--;
drm_minor_release(minor);
return retcode; return retcode;
} }
EXPORT_SYMBOL(drm_open); EXPORT_SYMBOL(drm_open);
...@@ -143,33 +123,30 @@ EXPORT_SYMBOL(drm_open); ...@@ -143,33 +123,30 @@ EXPORT_SYMBOL(drm_open);
*/ */
int drm_stub_open(struct inode *inode, struct file *filp) int drm_stub_open(struct inode *inode, struct file *filp)
{ {
struct drm_device *dev = NULL; struct drm_device *dev;
struct drm_minor *minor; struct drm_minor *minor;
int minor_id = iminor(inode);
int err = -ENODEV; int err = -ENODEV;
const struct file_operations *new_fops; const struct file_operations *new_fops;
DRM_DEBUG("\n"); DRM_DEBUG("\n");
mutex_lock(&drm_global_mutex); mutex_lock(&drm_global_mutex);
minor = idr_find(&drm_minors_idr, minor_id); minor = drm_minor_acquire(iminor(inode));
if (!minor) if (IS_ERR(minor))
goto out; goto out_unlock;
if (!(dev = minor->dev))
goto out;
if (drm_device_is_unplugged(dev))
goto out;
dev = minor->dev;
new_fops = fops_get(dev->driver->fops); new_fops = fops_get(dev->driver->fops);
if (!new_fops) if (!new_fops)
goto out; goto out_release;
replace_fops(filp, new_fops); replace_fops(filp, new_fops);
if (filp->f_op->open) if (filp->f_op->open)
err = filp->f_op->open(inode, filp); err = filp->f_op->open(inode, filp);
out:
out_release:
drm_minor_release(minor);
out_unlock:
mutex_unlock(&drm_global_mutex); mutex_unlock(&drm_global_mutex);
return err; return err;
} }
...@@ -196,16 +173,16 @@ static int drm_cpu_valid(void) ...@@ -196,16 +173,16 @@ static int drm_cpu_valid(void)
* *
* \param inode device inode. * \param inode device inode.
* \param filp file pointer. * \param filp file pointer.
* \param dev device. * \param minor acquired minor-object.
* \return zero on success or a negative number on failure. * \return zero on success or a negative number on failure.
* *
* Creates and initializes a drm_file structure for the file private data in \p * Creates and initializes a drm_file structure for the file private data in \p
* filp and add it into the double linked list in \p dev. * filp and add it into the double linked list in \p dev.
*/ */
static int drm_open_helper(struct inode *inode, struct file *filp, static int drm_open_helper(struct inode *inode, struct file *filp,
struct drm_device * dev) struct drm_minor *minor)
{ {
int minor_id = iminor(inode); struct drm_device *dev = minor->dev;
struct drm_file *priv; struct drm_file *priv;
int ret; int ret;
...@@ -216,7 +193,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, ...@@ -216,7 +193,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF)
return -EINVAL; return -EINVAL;
DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id); DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index);
priv = kzalloc(sizeof(*priv), GFP_KERNEL); priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -226,11 +203,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, ...@@ -226,11 +203,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->filp = filp; priv->filp = filp;
priv->uid = current_euid(); priv->uid = current_euid();
priv->pid = get_pid(task_pid(current)); priv->pid = get_pid(task_pid(current));
priv->minor = idr_find(&drm_minors_idr, minor_id); priv->minor = minor;
if (!priv->minor) {
ret = -ENODEV;
goto out_put_pid;
}
/* for compatibility root is always authenticated */ /* for compatibility root is always authenticated */
priv->always_authenticated = capable(CAP_SYS_ADMIN); priv->always_authenticated = capable(CAP_SYS_ADMIN);
...@@ -336,7 +309,6 @@ static int drm_open_helper(struct inode *inode, struct file *filp, ...@@ -336,7 +309,6 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
drm_prime_destroy_file_private(&priv->prime); drm_prime_destroy_file_private(&priv->prime);
if (dev->driver->driver_features & DRIVER_GEM) if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_release(dev, priv); drm_gem_release(dev, priv);
out_put_pid:
put_pid(priv->pid); put_pid(priv->pid);
kfree(priv); kfree(priv);
filp->private_data = NULL; filp->private_data = NULL;
...@@ -434,7 +406,6 @@ int drm_lastclose(struct drm_device * dev) ...@@ -434,7 +406,6 @@ int drm_lastclose(struct drm_device * dev)
drm_legacy_dma_takedown(dev); drm_legacy_dma_takedown(dev);
dev->dev_mapping = NULL;
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
drm_legacy_dev_reinit(dev); drm_legacy_dev_reinit(dev);
...@@ -458,7 +429,8 @@ int drm_lastclose(struct drm_device * dev) ...@@ -458,7 +429,8 @@ int drm_lastclose(struct drm_device * dev)
int drm_release(struct inode *inode, struct file *filp) int drm_release(struct inode *inode, struct file *filp)
{ {
struct drm_file *file_priv = filp->private_data; struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->minor->dev; struct drm_minor *minor = file_priv->minor;
struct drm_device *dev = minor->dev;
int retcode = 0; int retcode = 0;
mutex_lock(&drm_global_mutex); mutex_lock(&drm_global_mutex);
...@@ -474,7 +446,7 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -474,7 +446,7 @@ int drm_release(struct inode *inode, struct file *filp)
DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
task_pid_nr(current), task_pid_nr(current),
(long)old_encode_dev(file_priv->minor->device), (long)old_encode_dev(file_priv->minor->kdev->devt),
dev->open_count); dev->open_count);
/* Release any auth tokens that might point to this file_priv, /* Release any auth tokens that might point to this file_priv,
...@@ -549,9 +521,6 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -549,9 +521,6 @@ int drm_release(struct inode *inode, struct file *filp)
} }
} }
BUG_ON(dev->dev_mapping == NULL);
iput(container_of(dev->dev_mapping, struct inode, i_data));
/* drop the reference held my the file priv */ /* drop the reference held my the file priv */
if (file_priv->master) if (file_priv->master)
drm_master_put(&file_priv->master); drm_master_put(&file_priv->master);
...@@ -580,6 +549,8 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -580,6 +549,8 @@ int drm_release(struct inode *inode, struct file *filp)
} }
mutex_unlock(&drm_global_mutex); mutex_unlock(&drm_global_mutex);
drm_minor_release(minor);
return retcode; return retcode;
} }
EXPORT_SYMBOL(drm_release); EXPORT_SYMBOL(drm_release);
......
...@@ -85,9 +85,9 @@ ...@@ -85,9 +85,9 @@
#endif #endif
/** /**
* Initialize the GEM device fields * drm_gem_init - Initialize the GEM device fields
* @dev: drm_devic structure to initialize
*/ */
int int
drm_gem_init(struct drm_device *dev) drm_gem_init(struct drm_device *dev)
{ {
...@@ -120,6 +120,11 @@ drm_gem_destroy(struct drm_device *dev) ...@@ -120,6 +120,11 @@ drm_gem_destroy(struct drm_device *dev)
} }
/** /**
* drm_gem_object_init - initialize an allocated shmem-backed GEM object
* @dev: drm_device the object should be initialized for
* @obj: drm_gem_object to initialize
* @size: object size
*
* Initialize an already allocated GEM object of the specified size with * Initialize an already allocated GEM object of the specified size with
* shmfs backing store. * shmfs backing store.
*/ */
...@@ -141,6 +146,11 @@ int drm_gem_object_init(struct drm_device *dev, ...@@ -141,6 +146,11 @@ int drm_gem_object_init(struct drm_device *dev,
EXPORT_SYMBOL(drm_gem_object_init); EXPORT_SYMBOL(drm_gem_object_init);
/** /**
* drm_gem_object_init - initialize an allocated private GEM object
* @dev: drm_device the object should be initialized for
* @obj: drm_gem_object to initialize
* @size: object size
*
* Initialize an already allocated GEM object of the specified size with * Initialize an already allocated GEM object of the specified size with
* no GEM provided backing store. Instead the caller is responsible for * no GEM provided backing store. Instead the caller is responsible for
* backing the object and handling it. * backing the object and handling it.
...@@ -176,6 +186,9 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) ...@@ -176,6 +186,9 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
} }
/** /**
* drm_gem_object_free - release resources bound to userspace handles
* @obj: GEM object to clean up.
*
* Called after the last handle to the object has been closed * Called after the last handle to the object has been closed
* *
* Removes any name for the object. Note that this must be * Removes any name for the object. Note that this must be
...@@ -225,7 +238,12 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) ...@@ -225,7 +238,12 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
} }
/** /**
* Removes the mapping from handle to filp for this object. * drm_gem_handle_delete - deletes the given file-private handle
* @filp: drm file-private structure to use for the handle look up
* @handle: userspace handle to delete
*
* Removes the GEM handle from the @filp lookup table and if this is the last
* handle also cleans up linked resources like GEM names.
*/ */
int int
drm_gem_handle_delete(struct drm_file *filp, u32 handle) drm_gem_handle_delete(struct drm_file *filp, u32 handle)
...@@ -270,6 +288,9 @@ EXPORT_SYMBOL(drm_gem_handle_delete); ...@@ -270,6 +288,9 @@ EXPORT_SYMBOL(drm_gem_handle_delete);
/** /**
* drm_gem_dumb_destroy - dumb fb callback helper for gem based drivers * drm_gem_dumb_destroy - dumb fb callback helper for gem based drivers
* @file: drm file-private structure to remove the dumb handle from
* @dev: corresponding drm_device
* @handle: the dumb handle to remove
* *
* This implements the ->dumb_destroy kms driver callback for drivers which use * This implements the ->dumb_destroy kms driver callback for drivers which use
* gem to manage their backing storage. * gem to manage their backing storage.
...@@ -284,6 +305,9 @@ EXPORT_SYMBOL(drm_gem_dumb_destroy); ...@@ -284,6 +305,9 @@ EXPORT_SYMBOL(drm_gem_dumb_destroy);
/** /**
* drm_gem_handle_create_tail - internal functions to create a handle * drm_gem_handle_create_tail - internal functions to create a handle
* @file_priv: drm file-private structure to register the handle for
* @obj: object to register
* @handlep: pionter to return the created handle to the caller
* *
* This expects the dev->object_name_lock to be held already and will drop it * This expects the dev->object_name_lock to be held already and will drop it
* before returning. Used to avoid races in establishing new handles when * before returning. Used to avoid races in establishing new handles when
...@@ -336,6 +360,11 @@ drm_gem_handle_create_tail(struct drm_file *file_priv, ...@@ -336,6 +360,11 @@ drm_gem_handle_create_tail(struct drm_file *file_priv,
} }
/** /**
* gem_handle_create - create a gem handle for an object
* @file_priv: drm file-private structure to register the handle for
* @obj: object to register
* @handlep: pionter to return the created handle to the caller
*
* Create a handle for this object. This adds a handle reference * Create a handle for this object. This adds a handle reference
* to the object, which includes a regular reference count. Callers * to the object, which includes a regular reference count. Callers
* will likely want to dereference the object afterwards. * will likely want to dereference the object afterwards.
...@@ -536,6 +565,11 @@ drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, ...@@ -536,6 +565,11 @@ drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
EXPORT_SYMBOL(drm_gem_object_lookup); EXPORT_SYMBOL(drm_gem_object_lookup);
/** /**
* drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl
* @dev: drm_device
* @data: ioctl data
* @file_priv: drm file-private structure
*
* Releases the handle to an mm object. * Releases the handle to an mm object.
*/ */
int int
...@@ -554,6 +588,11 @@ drm_gem_close_ioctl(struct drm_device *dev, void *data, ...@@ -554,6 +588,11 @@ drm_gem_close_ioctl(struct drm_device *dev, void *data,
} }
/** /**
* drm_gem_flink_ioctl - implementation of the GEM_FLINK ioctl
* @dev: drm_device
* @data: ioctl data
* @file_priv: drm file-private structure
*
* Create a global name for an object, returning the name. * Create a global name for an object, returning the name.
* *
* Note that the name does not hold a reference; when the object * Note that the name does not hold a reference; when the object
...@@ -601,6 +640,11 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, ...@@ -601,6 +640,11 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
} }
/** /**
* drm_gem_open - implementation of the GEM_OPEN ioctl
* @dev: drm_device
* @data: ioctl data
* @file_priv: drm file-private structure
*
* Open an object using the global name, returning a handle and the size. * Open an object using the global name, returning a handle and the size.
* *
* This handle (of course) holds a reference to the object, so the object * This handle (of course) holds a reference to the object, so the object
...@@ -640,6 +684,10 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data, ...@@ -640,6 +684,10 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
} }
/** /**
* gem_gem_open - initalizes GEM file-private structures at devnode open time
* @dev: drm_device which is being opened by userspace
* @file_private: drm file-private structure to set up
*
* Called at device open time, sets up the structure for handling refcounting * Called at device open time, sets up the structure for handling refcounting
* of mm objects. * of mm objects.
*/ */
...@@ -650,7 +698,7 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_private) ...@@ -650,7 +698,7 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
spin_lock_init(&file_private->table_lock); spin_lock_init(&file_private->table_lock);
} }
/** /*
* Called at device close to release the file's * Called at device close to release the file's
* handle references on objects. * handle references on objects.
*/ */
...@@ -674,6 +722,10 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) ...@@ -674,6 +722,10 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
} }
/** /**
* drm_gem_release - release file-private GEM resources
* @dev: drm_device which is being closed by userspace
* @file_private: drm file-private structure to clean up
*
* Called at close time when the filp is going away. * Called at close time when the filp is going away.
* *
* Releases any remaining references on objects by this filp. * Releases any remaining references on objects by this filp.
...@@ -692,11 +744,16 @@ drm_gem_object_release(struct drm_gem_object *obj) ...@@ -692,11 +744,16 @@ drm_gem_object_release(struct drm_gem_object *obj)
WARN_ON(obj->dma_buf); WARN_ON(obj->dma_buf);
if (obj->filp) if (obj->filp)
fput(obj->filp); fput(obj->filp);
drm_gem_free_mmap_offset(obj);
} }
EXPORT_SYMBOL(drm_gem_object_release); EXPORT_SYMBOL(drm_gem_object_release);
/** /**
* drm_gem_object_free - free a GEM object
* @kref: kref of the object to free
*
* Called after the last reference to the object has been lost. * Called after the last reference to the object has been lost.
* Must be called holding struct_ mutex * Must be called holding struct_ mutex
* *
...@@ -782,7 +839,7 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, ...@@ -782,7 +839,7 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_ops = dev->driver->gem_vm_ops; vma->vm_ops = dev->driver->gem_vm_ops;
vma->vm_private_data = obj; vma->vm_private_data = obj;
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
/* Take a ref for this mapping of the object, so that the fault /* Take a ref for this mapping of the object, so that the fault
* handler can dereference the mmap offset's pointer to the object. * handler can dereference the mmap offset's pointer to the object.
...@@ -818,7 +875,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) ...@@ -818,7 +875,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_device *dev = priv->minor->dev; struct drm_device *dev = priv->minor->dev;
struct drm_gem_object *obj; struct drm_gem_object *obj;
struct drm_vma_offset_node *node; struct drm_vma_offset_node *node;
int ret = 0; int ret;
if (drm_device_is_unplugged(dev)) if (drm_device_is_unplugged(dev))
return -ENODEV; return -ENODEV;
......
...@@ -79,7 +79,6 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, ...@@ -79,7 +79,6 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
unsigned int size) unsigned int size)
{ {
struct drm_gem_cma_object *cma_obj; struct drm_gem_cma_object *cma_obj;
struct sg_table *sgt = NULL;
int ret; int ret;
size = round_up(size, PAGE_SIZE); size = round_up(size, PAGE_SIZE);
...@@ -97,23 +96,9 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, ...@@ -97,23 +96,9 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
goto error; goto error;
} }
sgt = kzalloc(sizeof(*cma_obj->sgt), GFP_KERNEL);
if (sgt == NULL) {
ret = -ENOMEM;
goto error;
}
ret = dma_get_sgtable(drm->dev, sgt, cma_obj->vaddr,
cma_obj->paddr, size);
if (ret < 0)
goto error;
cma_obj->sgt = sgt;
return cma_obj; return cma_obj;
error: error:
kfree(sgt);
drm_gem_cma_free_object(&cma_obj->base); drm_gem_cma_free_object(&cma_obj->base);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
...@@ -175,10 +160,6 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) ...@@ -175,10 +160,6 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
if (cma_obj->vaddr) { if (cma_obj->vaddr) {
dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size, dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size,
cma_obj->vaddr, cma_obj->paddr); cma_obj->vaddr, cma_obj->paddr);
if (cma_obj->sgt) {
sg_free_table(cma_obj->sgt);
kfree(cma_obj->sgt);
}
} else if (gem_obj->import_attach) { } else if (gem_obj->import_attach) {
drm_prime_gem_destroy(gem_obj, cma_obj->sgt); drm_prime_gem_destroy(gem_obj, cma_obj->sgt);
} }
......
...@@ -47,7 +47,44 @@ ...@@ -47,7 +47,44 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/export.h> #include <linux/export.h>
#define MM_UNUSED_TARGET 4 /**
* DOC: Overview
*
* drm_mm provides a simple range allocator. The drivers are free to use the
* resource allocator from the linux core if it suits them, the upside of drm_mm
* is that it's in the DRM core. Which means that it's easier to extend for
* some of the crazier special purpose needs of gpus.
*
* The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
* Drivers are free to embed either of them into their own suitable
* datastructures. drm_mm itself will not do any allocations of its own, so if
* drivers choose not to embed nodes they need to still allocate them
* themselves.
*
* The range allocator also supports reservation of preallocated blocks. This is
* useful for taking over initial mode setting configurations from the firmware,
* where an object needs to be created which exactly matches the firmware's
* scanout target. As long as the range is still free it can be inserted anytime
* after the allocator is initialized, which helps with avoiding looped
* depencies in the driver load sequence.
*
* drm_mm maintains a stack of most recently freed holes, which of all
* simplistic datastructures seems to be a fairly decent approach to clustering
* allocations and avoiding too much fragmentation. This means free space
* searches are O(num_holes). Given that all the fancy features drm_mm supports
* something better would be fairly complex and since gfx thrashing is a fairly
* steep cliff not a real concern. Removing a node again is O(1).
*
* drm_mm supports a few features: Alignment and range restrictions can be
* supplied. Further more every &drm_mm_node has a color value (which is just an
* opaqua unsigned long) which in conjunction with a driver callback can be used
* to implement sophisticated placement restrictions. The i915 DRM driver uses
* this to implement guard pages between incompatible caching domains in the
* graphics TT.
*
* Finally iteration helpers to walk all nodes and all holes are provided as are
* some basic allocator dumpers for debugging.
*/
static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
unsigned long size, unsigned long size,
...@@ -107,6 +144,20 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, ...@@ -107,6 +144,20 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
} }
} }
/**
* drm_mm_reserve_node - insert an pre-initialized node
* @mm: drm_mm allocator to insert @node into
* @node: drm_mm_node to insert
*
* This functions inserts an already set-up drm_mm_node into the allocator,
* meaning that start, size and color must be set by the caller. This is useful
* to initialize the allocator with preallocated objects which must be set-up
* before the range allocator can be set-up, e.g. when taking over a firmware
* framebuffer.
*
* Returns:
* 0 on success, -ENOSPC if there's no hole where @node is.
*/
int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
{ {
struct drm_mm_node *hole; struct drm_mm_node *hole;
...@@ -148,9 +199,18 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) ...@@ -148,9 +199,18 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
EXPORT_SYMBOL(drm_mm_reserve_node); EXPORT_SYMBOL(drm_mm_reserve_node);
/** /**
* Search for free space and insert a preallocated memory node. Returns * drm_mm_insert_node_generic - search for space and insert @node
* -ENOSPC if no suitable free area is available. The preallocated memory node * @mm: drm_mm to allocate from
* must be cleared. * @node: preallocate node to insert
* @size: size of the allocation
* @alignment: alignment of the allocation
* @color: opaque tag value to use for this node
* @flags: flags to fine-tune the allocation
*
* The preallocated node must be cleared to 0.
*
* Returns:
* 0 on success, -ENOSPC if there's no suitable hole.
*/ */
int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
unsigned long size, unsigned alignment, unsigned long size, unsigned alignment,
...@@ -222,9 +282,20 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, ...@@ -222,9 +282,20 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
} }
/** /**
* Search for free space and insert a preallocated memory node. Returns * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node
* -ENOSPC if no suitable free area is available. This is for range * @mm: drm_mm to allocate from
* restricted allocations. The preallocated memory node must be cleared. * @node: preallocate node to insert
* @size: size of the allocation
* @alignment: alignment of the allocation
* @color: opaque tag value to use for this node
* @start: start of the allowed range for this node
* @end: end of the allowed range for this node
* @flags: flags to fine-tune the allocation
*
* The preallocated node must be cleared to 0.
*
* Returns:
* 0 on success, -ENOSPC if there's no suitable hole.
*/ */
int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
unsigned long size, unsigned alignment, unsigned long color, unsigned long size, unsigned alignment, unsigned long color,
...@@ -247,7 +318,12 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n ...@@ -247,7 +318,12 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n
EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
/** /**
* Remove a memory node from the allocator. * drm_mm_remove_node - Remove a memory node from the allocator.
* @node: drm_mm_node to remove
*
* This just removes a node from its drm_mm allocator. The node does not need to
* be cleared again before it can be re-inserted into this or any other drm_mm
* allocator. It is a bug to call this function on a un-allocated node.
*/ */
void drm_mm_remove_node(struct drm_mm_node *node) void drm_mm_remove_node(struct drm_mm_node *node)
{ {
...@@ -384,7 +460,13 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ ...@@ -384,7 +460,13 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
} }
/** /**
* Moves an allocation. To be used with embedded struct drm_mm_node. * drm_mm_replace_node - move an allocation from @old to @new
* @old: drm_mm_node to remove from the allocator
* @new: drm_mm_node which should inherit @old's allocation
*
* This is useful for when drivers embed the drm_mm_node structure and hence
* can't move allocations by reassigning pointers. It's a combination of remove
* and insert with the guarantee that the allocation start will match.
*/ */
void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
{ {
...@@ -402,12 +484,46 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) ...@@ -402,12 +484,46 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
EXPORT_SYMBOL(drm_mm_replace_node); EXPORT_SYMBOL(drm_mm_replace_node);
/** /**
* Initializa lru scanning. * DOC: lru scan roaster
*
* Very often GPUs need to have continuous allocations for a given object. When
* evicting objects to make space for a new one it is therefore not most
* efficient when we simply start to select all objects from the tail of an LRU
* until there's a suitable hole: Especially for big objects or nodes that
* otherwise have special allocation constraints there's a good chance we evict
* lots of (smaller) objects unecessarily.
*
* The DRM range allocator supports this use-case through the scanning
* interfaces. First a scan operation needs to be initialized with
* drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds
* objects to the roaster (probably by walking an LRU list, but this can be
* freely implemented) until a suitable hole is found or there's no further
* evitable object.
*
* The the driver must walk through all objects again in exactly the reverse
* order to restore the allocator state. Note that while the allocator is used
* in the scan mode no other operation is allowed.
*
* Finally the driver evicts all objects selected in the scan. Adding and
* removing an object is O(1), and since freeing a node is also O(1) the overall
* complexity is O(scanned_objects). So like the free stack which needs to be
* walked before a scan operation even begins this is linear in the number of
* objects. It doesn't seem to hurt badly.
*/
/**
* drm_mm_init_scan - initialize lru scanning
* @mm: drm_mm to scan
* @size: size of the allocation
* @alignment: alignment of the allocation
* @color: opaque tag value to use for the allocation
* *
* This simply sets up the scanning routines with the parameters for the desired * This simply sets up the scanning routines with the parameters for the desired
* hole. * hole. Note that there's no need to specify allocation flags, since they only
* change the place a node is allocated from within a suitable hole.
* *
* Warning: As long as the scan list is non-empty, no other operations than * Warning:
* As long as the scan list is non-empty, no other operations than
* adding/removing nodes to/from the scan list are allowed. * adding/removing nodes to/from the scan list are allowed.
*/ */
void drm_mm_init_scan(struct drm_mm *mm, void drm_mm_init_scan(struct drm_mm *mm,
...@@ -427,12 +543,20 @@ void drm_mm_init_scan(struct drm_mm *mm, ...@@ -427,12 +543,20 @@ void drm_mm_init_scan(struct drm_mm *mm,
EXPORT_SYMBOL(drm_mm_init_scan); EXPORT_SYMBOL(drm_mm_init_scan);
/** /**
* Initializa lru scanning. * drm_mm_init_scan - initialize range-restricted lru scanning
* @mm: drm_mm to scan
* @size: size of the allocation
* @alignment: alignment of the allocation
* @color: opaque tag value to use for the allocation
* @start: start of the allowed range for the allocation
* @end: end of the allowed range for the allocation
* *
* This simply sets up the scanning routines with the parameters for the desired * This simply sets up the scanning routines with the parameters for the desired
* hole. This version is for range-restricted scans. * hole. Note that there's no need to specify allocation flags, since they only
* change the place a node is allocated from within a suitable hole.
* *
* Warning: As long as the scan list is non-empty, no other operations than * Warning:
* As long as the scan list is non-empty, no other operations than
* adding/removing nodes to/from the scan list are allowed. * adding/removing nodes to/from the scan list are allowed.
*/ */
void drm_mm_init_scan_with_range(struct drm_mm *mm, void drm_mm_init_scan_with_range(struct drm_mm *mm,
...@@ -456,12 +580,16 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm, ...@@ -456,12 +580,16 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm,
EXPORT_SYMBOL(drm_mm_init_scan_with_range); EXPORT_SYMBOL(drm_mm_init_scan_with_range);
/** /**
* drm_mm_scan_add_block - add a node to the scan list
* @node: drm_mm_node to add
*
* Add a node to the scan list that might be freed to make space for the desired * Add a node to the scan list that might be freed to make space for the desired
* hole. * hole.
* *
* Returns non-zero, if a hole has been found, zero otherwise. * Returns:
* True if a hole has been found, false otherwise.
*/ */
int drm_mm_scan_add_block(struct drm_mm_node *node) bool drm_mm_scan_add_block(struct drm_mm_node *node)
{ {
struct drm_mm *mm = node->mm; struct drm_mm *mm = node->mm;
struct drm_mm_node *prev_node; struct drm_mm_node *prev_node;
...@@ -501,15 +629,16 @@ int drm_mm_scan_add_block(struct drm_mm_node *node) ...@@ -501,15 +629,16 @@ int drm_mm_scan_add_block(struct drm_mm_node *node)
mm->scan_size, mm->scan_alignment)) { mm->scan_size, mm->scan_alignment)) {
mm->scan_hit_start = hole_start; mm->scan_hit_start = hole_start;
mm->scan_hit_end = hole_end; mm->scan_hit_end = hole_end;
return 1; return true;
} }
return 0; return false;
} }
EXPORT_SYMBOL(drm_mm_scan_add_block); EXPORT_SYMBOL(drm_mm_scan_add_block);
/** /**
* Remove a node from the scan list. * drm_mm_scan_remove_block - remove a node from the scan list
* @node: drm_mm_node to remove
* *
* Nodes _must_ be removed in the exact same order from the scan list as they * Nodes _must_ be removed in the exact same order from the scan list as they
* have been added, otherwise the internal state of the memory manager will be * have been added, otherwise the internal state of the memory manager will be
...@@ -519,10 +648,11 @@ EXPORT_SYMBOL(drm_mm_scan_add_block); ...@@ -519,10 +648,11 @@ EXPORT_SYMBOL(drm_mm_scan_add_block);
* immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then
* return the just freed block (because its at the top of the free_stack list). * return the just freed block (because its at the top of the free_stack list).
* *
* Returns one if this block should be evicted, zero otherwise. Will always * Returns:
* return zero when no hole has been found. * True if this block should be evicted, false otherwise. Will always
* return false when no hole has been found.
*/ */
int drm_mm_scan_remove_block(struct drm_mm_node *node) bool drm_mm_scan_remove_block(struct drm_mm_node *node)
{ {
struct drm_mm *mm = node->mm; struct drm_mm *mm = node->mm;
struct drm_mm_node *prev_node; struct drm_mm_node *prev_node;
...@@ -543,7 +673,15 @@ int drm_mm_scan_remove_block(struct drm_mm_node *node) ...@@ -543,7 +673,15 @@ int drm_mm_scan_remove_block(struct drm_mm_node *node)
} }
EXPORT_SYMBOL(drm_mm_scan_remove_block); EXPORT_SYMBOL(drm_mm_scan_remove_block);
int drm_mm_clean(struct drm_mm * mm) /**
* drm_mm_clean - checks whether an allocator is clean
* @mm: drm_mm allocator to check
*
* Returns:
* True if the allocator is completely free, false if there's still a node
* allocated in it.
*/
bool drm_mm_clean(struct drm_mm * mm)
{ {
struct list_head *head = &mm->head_node.node_list; struct list_head *head = &mm->head_node.node_list;
...@@ -551,6 +689,14 @@ int drm_mm_clean(struct drm_mm * mm) ...@@ -551,6 +689,14 @@ int drm_mm_clean(struct drm_mm * mm)
} }
EXPORT_SYMBOL(drm_mm_clean); EXPORT_SYMBOL(drm_mm_clean);
/**
* drm_mm_init - initialize a drm-mm allocator
* @mm: the drm_mm structure to initialize
* @start: start of the range managed by @mm
* @size: end of the range managed by @mm
*
* Note that @mm must be cleared to 0 before calling this function.
*/
void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
{ {
INIT_LIST_HEAD(&mm->hole_stack); INIT_LIST_HEAD(&mm->hole_stack);
...@@ -572,6 +718,13 @@ void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) ...@@ -572,6 +718,13 @@ void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
} }
EXPORT_SYMBOL(drm_mm_init); EXPORT_SYMBOL(drm_mm_init);
/**
* drm_mm_takedown - clean up a drm_mm allocator
* @mm: drm_mm allocator to clean up
*
* Note that it is a bug to call this function on an allocator which is not
* clean.
*/
void drm_mm_takedown(struct drm_mm * mm) void drm_mm_takedown(struct drm_mm * mm)
{ {
WARN(!list_empty(&mm->head_node.node_list), WARN(!list_empty(&mm->head_node.node_list),
...@@ -597,6 +750,11 @@ static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry, ...@@ -597,6 +750,11 @@ static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry,
return 0; return 0;
} }
/**
* drm_mm_debug_table - dump allocator state to dmesg
* @mm: drm_mm allocator to dump
* @prefix: prefix to use for dumping to dmesg
*/
void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
{ {
struct drm_mm_node *entry; struct drm_mm_node *entry;
...@@ -635,6 +793,11 @@ static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *en ...@@ -635,6 +793,11 @@ static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *en
return 0; return 0;
} }
/**
* drm_mm_dump_table - dump allocator state to a seq_file
* @m: seq_file to dump to
* @mm: drm_mm allocator to dump
*/
int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
{ {
struct drm_mm_node *entry; struct drm_mm_node *entry;
......
...@@ -37,15 +37,14 @@ ...@@ -37,15 +37,14 @@
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <video/of_videomode.h> #include <video/of_videomode.h>
#include <video/videomode.h> #include <video/videomode.h>
#include <drm/drm_modes.h>
#include "drm_crtc_internal.h"
/** /**
* drm_mode_debug_printmodeline - debug print a mode * drm_mode_debug_printmodeline - print a mode to dmesg
* @dev: DRM device
* @mode: mode to print * @mode: mode to print
* *
* LOCKING:
* None.
*
* Describe @mode using DRM_DEBUG. * Describe @mode using DRM_DEBUG.
*/ */
void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) void drm_mode_debug_printmodeline(const struct drm_display_mode *mode)
...@@ -61,18 +60,77 @@ void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) ...@@ -61,18 +60,77 @@ void drm_mode_debug_printmodeline(const struct drm_display_mode *mode)
EXPORT_SYMBOL(drm_mode_debug_printmodeline); EXPORT_SYMBOL(drm_mode_debug_printmodeline);
/** /**
* drm_cvt_mode -create a modeline based on CVT algorithm * drm_mode_create - create a new display mode
* @dev: DRM device * @dev: DRM device
* @hdisplay: hdisplay size
* @vdisplay: vdisplay size
* @vrefresh : vrefresh rate
* @reduced : Whether the GTF calculation is simplified
* @interlaced:Whether the interlace is supported
* *
* LOCKING: * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it
* none. * and return it.
* *
* return the modeline based on CVT algorithm * Returns:
* Pointer to new mode on success, NULL on error.
*/
struct drm_display_mode *drm_mode_create(struct drm_device *dev)
{
struct drm_display_mode *nmode;
nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
if (!nmode)
return NULL;
if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
kfree(nmode);
return NULL;
}
return nmode;
}
EXPORT_SYMBOL(drm_mode_create);
/**
* drm_mode_destroy - remove a mode
* @dev: DRM device
* @mode: mode to remove
*
* Release @mode's unique ID, then free it @mode structure itself using kfree.
*/
void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
{
if (!mode)
return;
drm_mode_object_put(dev, &mode->base);
kfree(mode);
}
EXPORT_SYMBOL(drm_mode_destroy);
/**
* drm_mode_probed_add - add a mode to a connector's probed_mode list
* @connector: connector the new mode
* @mode: mode data
*
* Add @mode to @connector's probed_mode list for later use. This list should
* then in a second step get filtered and all the modes actually supported by
* the hardware moved to the @connector's modes list.
*/
void drm_mode_probed_add(struct drm_connector *connector,
struct drm_display_mode *mode)
{
WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
list_add_tail(&mode->head, &connector->probed_modes);
}
EXPORT_SYMBOL(drm_mode_probed_add);
/**
* drm_cvt_mode -create a modeline based on the CVT algorithm
* @dev: drm device
* @hdisplay: hdisplay size
* @vdisplay: vdisplay size
* @vrefresh: vrefresh rate
* @reduced: whether to use reduced blanking
* @interlaced: whether to compute an interlaced mode
* @margins: whether to add margins (borders)
* *
* This function is called to generate the modeline based on CVT algorithm * This function is called to generate the modeline based on CVT algorithm
* according to the hdisplay, vdisplay, vrefresh. * according to the hdisplay, vdisplay, vrefresh.
...@@ -82,12 +140,17 @@ EXPORT_SYMBOL(drm_mode_debug_printmodeline); ...@@ -82,12 +140,17 @@ EXPORT_SYMBOL(drm_mode_debug_printmodeline);
* *
* And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c. * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c.
* What I have done is to translate it by using integer calculation. * What I have done is to translate it by using integer calculation.
*
* Returns:
* The modeline based on the CVT algorithm stored in a drm_display_mode object.
* The display mode object is allocated with drm_mode_create(). Returns NULL
* when no mode could be allocated.
*/ */
#define HV_FACTOR 1000
struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
int vdisplay, int vrefresh, int vdisplay, int vrefresh,
bool reduced, bool interlaced, bool margins) bool reduced, bool interlaced, bool margins)
{ {
#define HV_FACTOR 1000
/* 1) top/bottom margin size (% of height) - default: 1.8, */ /* 1) top/bottom margin size (% of height) - default: 1.8, */
#define CVT_MARGIN_PERCENTAGE 18 #define CVT_MARGIN_PERCENTAGE 18
/* 2) character cell horizontal granularity (pixels) - default 8 */ /* 2) character cell horizontal granularity (pixels) - default 8 */
...@@ -281,23 +344,25 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, ...@@ -281,23 +344,25 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
EXPORT_SYMBOL(drm_cvt_mode); EXPORT_SYMBOL(drm_cvt_mode);
/** /**
* drm_gtf_mode_complex - create the modeline based on full GTF algorithm * drm_gtf_mode_complex - create the modeline based on the full GTF algorithm
* * @dev: drm device
* @dev :drm device * @hdisplay: hdisplay size
* @hdisplay :hdisplay size * @vdisplay: vdisplay size
* @vdisplay :vdisplay size * @vrefresh: vrefresh rate.
* @vrefresh :vrefresh rate. * @interlaced: whether to compute an interlaced mode
* @interlaced :whether the interlace is supported * @margins: desired margin (borders) size
* @margins :desired margin size * @GTF_M: extended GTF formula parameters
* @GTF_[MCKJ] :extended GTF formula parameters * @GTF_2C: extended GTF formula parameters
* * @GTF_K: extended GTF formula parameters
* LOCKING. * @GTF_2J: extended GTF formula parameters
* none.
*
* return the modeline based on full GTF algorithm.
* *
* GTF feature blocks specify C and J in multiples of 0.5, so we pass them * GTF feature blocks specify C and J in multiples of 0.5, so we pass them
* in here multiplied by two. For a C of 40, pass in 80. * in here multiplied by two. For a C of 40, pass in 80.
*
* Returns:
* The modeline based on the full GTF algorithm stored in a drm_display_mode object.
* The display mode object is allocated with drm_mode_create(). Returns NULL
* when no mode could be allocated.
*/ */
struct drm_display_mode * struct drm_display_mode *
drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay,
...@@ -467,17 +532,13 @@ drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, ...@@ -467,17 +532,13 @@ drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay,
EXPORT_SYMBOL(drm_gtf_mode_complex); EXPORT_SYMBOL(drm_gtf_mode_complex);
/** /**
* drm_gtf_mode - create the modeline based on GTF algorithm * drm_gtf_mode - create the modeline based on the GTF algorithm
* * @dev: drm device
* @dev :drm device * @hdisplay: hdisplay size
* @hdisplay :hdisplay size * @vdisplay: vdisplay size
* @vdisplay :vdisplay size * @vrefresh: vrefresh rate.
* @vrefresh :vrefresh rate. * @interlaced: whether to compute an interlaced mode
* @interlaced :whether the interlace is supported * @margins: desired margin (borders) size
* @margins :whether the margin is supported
*
* LOCKING.
* none.
* *
* return the modeline based on GTF algorithm * return the modeline based on GTF algorithm
* *
...@@ -496,19 +557,32 @@ EXPORT_SYMBOL(drm_gtf_mode_complex); ...@@ -496,19 +557,32 @@ EXPORT_SYMBOL(drm_gtf_mode_complex);
* C = 40 * C = 40
* K = 128 * K = 128
* J = 20 * J = 20
*
* Returns:
* The modeline based on the GTF algorithm stored in a drm_display_mode object.
* The display mode object is allocated with drm_mode_create(). Returns NULL
* when no mode could be allocated.
*/ */
struct drm_display_mode * struct drm_display_mode *
drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh,
bool lace, int margins) bool interlaced, int margins)
{ {
return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, lace, return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh,
margins, 600, 40 * 2, 128, 20 * 2); interlaced, margins,
600, 40 * 2, 128, 20 * 2);
} }
EXPORT_SYMBOL(drm_gtf_mode); EXPORT_SYMBOL(drm_gtf_mode);
#ifdef CONFIG_VIDEOMODE_HELPERS #ifdef CONFIG_VIDEOMODE_HELPERS
int drm_display_mode_from_videomode(const struct videomode *vm, /**
struct drm_display_mode *dmode) * drm_display_mode_from_videomode - fill in @dmode using @vm,
* @vm: videomode structure to use as source
* @dmode: drm_display_mode structure to use as destination
*
* Fills out @dmode using the display mode specified in @vm.
*/
void drm_display_mode_from_videomode(const struct videomode *vm,
struct drm_display_mode *dmode)
{ {
dmode->hdisplay = vm->hactive; dmode->hdisplay = vm->hactive;
dmode->hsync_start = dmode->hdisplay + vm->hfront_porch; dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
...@@ -538,8 +612,6 @@ int drm_display_mode_from_videomode(const struct videomode *vm, ...@@ -538,8 +612,6 @@ int drm_display_mode_from_videomode(const struct videomode *vm,
if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
dmode->flags |= DRM_MODE_FLAG_DBLCLK; dmode->flags |= DRM_MODE_FLAG_DBLCLK;
drm_mode_set_name(dmode); drm_mode_set_name(dmode);
return 0;
} }
EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
...@@ -553,6 +625,9 @@ EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); ...@@ -553,6 +625,9 @@ EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
* This function is expensive and should only be used, if only one mode is to be * This function is expensive and should only be used, if only one mode is to be
* read from DT. To get multiple modes start with of_get_display_timings and * read from DT. To get multiple modes start with of_get_display_timings and
* work with that instead. * work with that instead.
*
* Returns:
* 0 on success, a negative errno code when no of videomode node was found.
*/ */
int of_get_drm_display_mode(struct device_node *np, int of_get_drm_display_mode(struct device_node *np,
struct drm_display_mode *dmode, int index) struct drm_display_mode *dmode, int index)
...@@ -580,10 +655,8 @@ EXPORT_SYMBOL_GPL(of_get_drm_display_mode); ...@@ -580,10 +655,8 @@ EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
* drm_mode_set_name - set the name on a mode * drm_mode_set_name - set the name on a mode
* @mode: name will be set in this mode * @mode: name will be set in this mode
* *
* LOCKING: * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay>
* None. * with an optional 'i' suffix for interlaced modes.
*
* Set the name of @mode to a standard format.
*/ */
void drm_mode_set_name(struct drm_display_mode *mode) void drm_mode_set_name(struct drm_display_mode *mode)
{ {
...@@ -595,54 +668,12 @@ void drm_mode_set_name(struct drm_display_mode *mode) ...@@ -595,54 +668,12 @@ void drm_mode_set_name(struct drm_display_mode *mode)
} }
EXPORT_SYMBOL(drm_mode_set_name); EXPORT_SYMBOL(drm_mode_set_name);
/**
* drm_mode_width - get the width of a mode
* @mode: mode
*
* LOCKING:
* None.
*
* Return @mode's width (hdisplay) value.
*
* FIXME: is this needed?
*
* RETURNS:
* @mode->hdisplay
*/
int drm_mode_width(const struct drm_display_mode *mode)
{
return mode->hdisplay;
}
EXPORT_SYMBOL(drm_mode_width);
/**
* drm_mode_height - get the height of a mode
* @mode: mode
*
* LOCKING:
* None.
*
* Return @mode's height (vdisplay) value.
*
* FIXME: is this needed?
*
* RETURNS:
* @mode->vdisplay
*/
int drm_mode_height(const struct drm_display_mode *mode)
{
return mode->vdisplay;
}
EXPORT_SYMBOL(drm_mode_height);
/** drm_mode_hsync - get the hsync of a mode /** drm_mode_hsync - get the hsync of a mode
* @mode: mode * @mode: mode
* *
* LOCKING: * Returns:
* None. * @modes's hsync rate in kHz, rounded to the nearest integer. Calculates the
* * value first if it is not yet set.
* Return @modes's hsync rate in kHz, rounded to the nearest int.
*/ */
int drm_mode_hsync(const struct drm_display_mode *mode) int drm_mode_hsync(const struct drm_display_mode *mode)
{ {
...@@ -666,17 +697,9 @@ EXPORT_SYMBOL(drm_mode_hsync); ...@@ -666,17 +697,9 @@ EXPORT_SYMBOL(drm_mode_hsync);
* drm_mode_vrefresh - get the vrefresh of a mode * drm_mode_vrefresh - get the vrefresh of a mode
* @mode: mode * @mode: mode
* *
* LOCKING: * Returns:
* None. * @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the
* * value first if it is not yet set.
* Return @mode's vrefresh rate in Hz or calculate it if necessary.
*
* FIXME: why is this needed? shouldn't vrefresh be set already?
*
* RETURNS:
* Vertical refresh rate. It will be the result of actual value plus 0.5.
* If it is 70.288, it will return 70Hz.
* If it is 59.6, it will return 60Hz.
*/ */
int drm_mode_vrefresh(const struct drm_display_mode *mode) int drm_mode_vrefresh(const struct drm_display_mode *mode)
{ {
...@@ -705,14 +728,11 @@ int drm_mode_vrefresh(const struct drm_display_mode *mode) ...@@ -705,14 +728,11 @@ int drm_mode_vrefresh(const struct drm_display_mode *mode)
EXPORT_SYMBOL(drm_mode_vrefresh); EXPORT_SYMBOL(drm_mode_vrefresh);
/** /**
* drm_mode_set_crtcinfo - set CRTC modesetting parameters * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters
* @p: mode * @p: mode
* @adjust_flags: a combination of adjustment flags * @adjust_flags: a combination of adjustment flags
* *
* LOCKING: * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary.
* None.
*
* Setup the CRTC modesetting parameters for @p, adjusting if necessary.
* *
* - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
* interlaced modes. * interlaced modes.
...@@ -780,15 +800,11 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) ...@@ -780,15 +800,11 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
} }
EXPORT_SYMBOL(drm_mode_set_crtcinfo); EXPORT_SYMBOL(drm_mode_set_crtcinfo);
/** /**
* drm_mode_copy - copy the mode * drm_mode_copy - copy the mode
* @dst: mode to overwrite * @dst: mode to overwrite
* @src: mode to copy * @src: mode to copy
* *
* LOCKING:
* None.
*
* Copy an existing mode into another mode, preserving the object id and * Copy an existing mode into another mode, preserving the object id and
* list head of the destination mode. * list head of the destination mode.
*/ */
...@@ -805,13 +821,14 @@ EXPORT_SYMBOL(drm_mode_copy); ...@@ -805,13 +821,14 @@ EXPORT_SYMBOL(drm_mode_copy);
/** /**
* drm_mode_duplicate - allocate and duplicate an existing mode * drm_mode_duplicate - allocate and duplicate an existing mode
* @m: mode to duplicate * @dev: drm_device to allocate the duplicated mode for
* * @mode: mode to duplicate
* LOCKING:
* None.
* *
* Just allocate a new mode, copy the existing mode into it, and return * Just allocate a new mode, copy the existing mode into it, and return
* a pointer to it. Used to create new instances of established modes. * a pointer to it. Used to create new instances of established modes.
*
* Returns:
* Pointer to duplicated mode on success, NULL on error.
*/ */
struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
...@@ -833,12 +850,9 @@ EXPORT_SYMBOL(drm_mode_duplicate); ...@@ -833,12 +850,9 @@ EXPORT_SYMBOL(drm_mode_duplicate);
* @mode1: first mode * @mode1: first mode
* @mode2: second mode * @mode2: second mode
* *
* LOCKING:
* None.
*
* Check to see if @mode1 and @mode2 are equivalent. * Check to see if @mode1 and @mode2 are equivalent.
* *
* RETURNS: * Returns:
* True if the modes are equal, false otherwise. * True if the modes are equal, false otherwise.
*/ */
bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2)
...@@ -864,13 +878,10 @@ EXPORT_SYMBOL(drm_mode_equal); ...@@ -864,13 +878,10 @@ EXPORT_SYMBOL(drm_mode_equal);
* @mode1: first mode * @mode1: first mode
* @mode2: second mode * @mode2: second mode
* *
* LOCKING:
* None.
*
* Check to see if @mode1 and @mode2 are equivalent, but * Check to see if @mode1 and @mode2 are equivalent, but
* don't check the pixel clocks nor the stereo layout. * don't check the pixel clocks nor the stereo layout.
* *
* RETURNS: * Returns:
* True if the modes are equal, false otherwise. * True if the modes are equal, false otherwise.
*/ */
bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1,
...@@ -900,25 +911,19 @@ EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); ...@@ -900,25 +911,19 @@ EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo);
* @mode_list: list of modes to check * @mode_list: list of modes to check
* @maxX: maximum width * @maxX: maximum width
* @maxY: maximum height * @maxY: maximum height
* @maxPitch: max pitch
* *
* LOCKING: * This function is a helper which can be used to validate modes against size
* Caller must hold a lock protecting @mode_list. * limitations of the DRM device/connector. If a mode is too big its status
* * memeber is updated with the appropriate validation failure code. The list
* The DRM device (@dev) has size and pitch limits. Here we validate the * itself is not changed.
* modes we probed for @dev against those limits and set their status as
* necessary.
*/ */
void drm_mode_validate_size(struct drm_device *dev, void drm_mode_validate_size(struct drm_device *dev,
struct list_head *mode_list, struct list_head *mode_list,
int maxX, int maxY, int maxPitch) int maxX, int maxY)
{ {
struct drm_display_mode *mode; struct drm_display_mode *mode;
list_for_each_entry(mode, mode_list, head) { list_for_each_entry(mode, mode_list, head) {
if (maxPitch > 0 && mode->hdisplay > maxPitch)
mode->status = MODE_BAD_WIDTH;
if (maxX > 0 && mode->hdisplay > maxX) if (maxX > 0 && mode->hdisplay > maxX)
mode->status = MODE_VIRTUAL_X; mode->status = MODE_VIRTUAL_X;
...@@ -934,12 +939,10 @@ EXPORT_SYMBOL(drm_mode_validate_size); ...@@ -934,12 +939,10 @@ EXPORT_SYMBOL(drm_mode_validate_size);
* @mode_list: list of modes to check * @mode_list: list of modes to check
* @verbose: be verbose about it * @verbose: be verbose about it
* *
* LOCKING: * This helper function can be used to prune a display mode list after
* Caller must hold a lock protecting @mode_list. * validation has been completed. All modes who's status is not MODE_OK will be
* * removed from the list, and if @verbose the status code and mode name is also
* Once mode list generation is complete, a caller can use this routine to * printed to dmesg.
* remove invalid modes from a mode list. If any of the modes have a
* status other than %MODE_OK, they are removed from @mode_list and freed.
*/ */
void drm_mode_prune_invalid(struct drm_device *dev, void drm_mode_prune_invalid(struct drm_device *dev,
struct list_head *mode_list, bool verbose) struct list_head *mode_list, bool verbose)
...@@ -966,13 +969,10 @@ EXPORT_SYMBOL(drm_mode_prune_invalid); ...@@ -966,13 +969,10 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
* @lh_a: list_head for first mode * @lh_a: list_head for first mode
* @lh_b: list_head for second mode * @lh_b: list_head for second mode
* *
* LOCKING:
* None.
*
* Compare two modes, given by @lh_a and @lh_b, returning a value indicating * Compare two modes, given by @lh_a and @lh_b, returning a value indicating
* which is better. * which is better.
* *
* RETURNS: * Returns:
* Negative if @lh_a is better than @lh_b, zero if they're equivalent, or * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
* positive if @lh_b is better than @lh_a. * positive if @lh_b is better than @lh_a.
*/ */
...@@ -1000,12 +1000,9 @@ static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head ...@@ -1000,12 +1000,9 @@ static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head
/** /**
* drm_mode_sort - sort mode list * drm_mode_sort - sort mode list
* @mode_list: list to sort * @mode_list: list of drm_display_mode structures to sort
* *
* LOCKING: * Sort @mode_list by favorability, moving good modes to the head of the list.
* Caller must hold a lock protecting @mode_list.
*
* Sort @mode_list by favorability, putting good modes first.
*/ */
void drm_mode_sort(struct list_head *mode_list) void drm_mode_sort(struct list_head *mode_list)
{ {
...@@ -1017,13 +1014,12 @@ EXPORT_SYMBOL(drm_mode_sort); ...@@ -1017,13 +1014,12 @@ EXPORT_SYMBOL(drm_mode_sort);
* drm_mode_connector_list_update - update the mode list for the connector * drm_mode_connector_list_update - update the mode list for the connector
* @connector: the connector to update * @connector: the connector to update
* *
* LOCKING:
* Caller must hold a lock protecting @mode_list.
*
* This moves the modes from the @connector probed_modes list * This moves the modes from the @connector probed_modes list
* to the actual mode list. It compares the probed mode against the current * to the actual mode list. It compares the probed mode against the current
* list and only adds different modes. All modes unverified after this point * list and only adds different/new modes.
* will be removed by the prune invalid modes. *
* This is just a helper functions doesn't validate any modes itself and also
* doesn't prune any invalid modes. Callers need to do that themselves.
*/ */
void drm_mode_connector_list_update(struct drm_connector *connector) void drm_mode_connector_list_update(struct drm_connector *connector)
{ {
...@@ -1031,6 +1027,8 @@ void drm_mode_connector_list_update(struct drm_connector *connector) ...@@ -1031,6 +1027,8 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
struct drm_display_mode *pmode, *pt; struct drm_display_mode *pmode, *pt;
int found_it; int found_it;
WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
list_for_each_entry_safe(pmode, pt, &connector->probed_modes, list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
head) { head) {
found_it = 0; found_it = 0;
...@@ -1056,17 +1054,25 @@ void drm_mode_connector_list_update(struct drm_connector *connector) ...@@ -1056,17 +1054,25 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
EXPORT_SYMBOL(drm_mode_connector_list_update); EXPORT_SYMBOL(drm_mode_connector_list_update);
/** /**
* drm_mode_parse_command_line_for_connector - parse command line for connector * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
* @mode_option - per connector mode option * @mode_option: optional per connector mode option
* @connector - connector to parse line for * @connector: connector to parse modeline for
* @mode: preallocated drm_cmdline_mode structure to fill out
*
* This parses @mode_option command line modeline for modes and options to
* configure the connector. If @mode_option is NULL the default command line
* modeline in fb_mode_option will be parsed instead.
* *
* This parses the connector specific then generic command lines for * This uses the same parameters as the fb modedb.c, except for an extra
* modes and options to configure the connector. * force-enable, force-enable-digital and force-disable bit at the end:
* *
* This uses the same parameters as the fb modedb.c, except for extra
* <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
* *
* enable/enable Digital/disable bit at the end * The intermediate drm_cmdline_mode structure is required to store additional
* options from the command line modline like the force-enabel/disable flag.
*
* Returns:
* True if a valid modeline has been parsed, false otherwise.
*/ */
bool drm_mode_parse_command_line_for_connector(const char *mode_option, bool drm_mode_parse_command_line_for_connector(const char *mode_option,
struct drm_connector *connector, struct drm_connector *connector,
...@@ -1219,6 +1225,14 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, ...@@ -1219,6 +1225,14 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
} }
EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);
/**
* drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode
* @dev: DRM device to create the new mode for
* @cmd: input command line modeline
*
* Returns:
* Pointer to converted mode on success, NULL on error.
*/
struct drm_display_mode * struct drm_display_mode *
drm_mode_create_from_cmdline_mode(struct drm_device *dev, drm_mode_create_from_cmdline_mode(struct drm_device *dev,
struct drm_cmdline_mode *cmd) struct drm_cmdline_mode *cmd)
......
...@@ -351,7 +351,7 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, ...@@ -351,7 +351,7 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
drm_pci_agp_destroy(dev); drm_pci_agp_destroy(dev);
pci_disable_device(pdev); pci_disable_device(pdev);
err_free: err_free:
drm_dev_free(dev); drm_dev_unref(dev);
return ret; return ret;
} }
EXPORT_SYMBOL(drm_get_pci_dev); EXPORT_SYMBOL(drm_get_pci_dev);
...@@ -468,8 +468,8 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) ...@@ -468,8 +468,8 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
} else { } else {
list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list, list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list,
legacy_dev_list) { legacy_dev_list) {
drm_put_dev(dev);
list_del(&dev->legacy_dev_list); list_del(&dev->legacy_dev_list);
drm_put_dev(dev);
} }
} }
DRM_INFO("Module unloaded\n"); DRM_INFO("Module unloaded\n");
......
...@@ -64,7 +64,7 @@ static int drm_get_platform_dev(struct platform_device *platdev, ...@@ -64,7 +64,7 @@ static int drm_get_platform_dev(struct platform_device *platdev,
return 0; return 0;
err_free: err_free:
drm_dev_free(dev); drm_dev_unref(dev);
return ret; return ret;
} }
......
此差异已折叠。
此差异已折叠。
...@@ -30,7 +30,7 @@ int drm_get_usb_dev(struct usb_interface *interface, ...@@ -30,7 +30,7 @@ int drm_get_usb_dev(struct usb_interface *interface,
return 0; return 0;
err_free: err_free:
drm_dev_free(dev); drm_dev_unref(dev);
return ret; return ret;
} }
......
此差异已折叠。
...@@ -1526,7 +1526,8 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) ...@@ -1526,7 +1526,8 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
if (!obj->fault_mappable) if (!obj->fault_mappable)
return; return;
drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->dev_mapping); drm_vma_node_unmap(&obj->base.vma_node,
obj->base.dev->anon_inode->i_mapping);
obj->fault_mappable = false; obj->fault_mappable = false;
} }
......
此差异已折叠。
...@@ -489,8 +489,7 @@ struct intel_dp { ...@@ -489,8 +489,7 @@ struct intel_dp {
uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE]; uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
struct i2c_adapter adapter; struct drm_dp_aux aux;
struct i2c_algo_dp_aux_data algo;
uint8_t train_set[4]; uint8_t train_set[4];
int panel_power_up_delay; int panel_power_up_delay;
int panel_power_down_delay; int panel_power_down_delay;
......
...@@ -259,7 +259,9 @@ int mgag200_mm_init(struct mga_device *mdev) ...@@ -259,7 +259,9 @@ int mgag200_mm_init(struct mga_device *mdev)
ret = ttm_bo_device_init(&mdev->ttm.bdev, ret = ttm_bo_device_init(&mdev->ttm.bdev,
mdev->ttm.bo_global_ref.ref.object, mdev->ttm.bo_global_ref.ref.object,
&mgag200_bo_driver, DRM_FILE_PAGE_OFFSET, &mgag200_bo_driver,
dev->anon_inode->i_mapping,
DRM_FILE_PAGE_OFFSET,
true); true);
if (ret) { if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret); DRM_ERROR("Error initialising bo driver; %d\n", ret);
...@@ -324,7 +326,6 @@ int mgag200_bo_create(struct drm_device *dev, int size, int align, ...@@ -324,7 +326,6 @@ int mgag200_bo_create(struct drm_device *dev, int size, int align,
} }
mgabo->bo.bdev = &mdev->ttm.bdev; mgabo->bo.bdev = &mdev->ttm.bdev;
mgabo->bo.bdev->dev_mapping = dev->dev_mapping;
mgag200_ttm_placement(mgabo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); mgag200_ttm_placement(mgabo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
......
...@@ -228,8 +228,6 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, ...@@ -228,8 +228,6 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
struct nouveau_bo *nvbo = NULL; struct nouveau_bo *nvbo = NULL;
int ret = 0; int ret = 0;
drm->ttm.bdev.dev_mapping = drm->dev->dev_mapping;
if (!pfb->memtype_valid(pfb, req->info.tile_flags)) { if (!pfb->memtype_valid(pfb, req->info.tile_flags)) {
NV_ERROR(cli, "bad page flags: 0x%08x\n", req->info.tile_flags); NV_ERROR(cli, "bad page flags: 0x%08x\n", req->info.tile_flags);
return -EINVAL; return -EINVAL;
......
...@@ -376,7 +376,9 @@ nouveau_ttm_init(struct nouveau_drm *drm) ...@@ -376,7 +376,9 @@ nouveau_ttm_init(struct nouveau_drm *drm)
ret = ttm_bo_device_init(&drm->ttm.bdev, ret = ttm_bo_device_init(&drm->ttm.bdev,
drm->ttm.bo_global_ref.ref.object, drm->ttm.bo_global_ref.ref.object,
&nouveau_bo_driver, DRM_FILE_PAGE_OFFSET, &nouveau_bo_driver,
dev->anon_inode->i_mapping,
DRM_FILE_PAGE_OFFSET,
bits <= 32 ? true : false); bits <= 32 ? true : false);
if (ret) { if (ret) {
NV_ERROR(drm, "error initialising bo driver, %d\n", ret); NV_ERROR(drm, "error initialising bo driver, %d\n", ret);
......
...@@ -153,24 +153,24 @@ static struct { ...@@ -153,24 +153,24 @@ static struct {
static void evict_entry(struct drm_gem_object *obj, static void evict_entry(struct drm_gem_object *obj,
enum tiler_fmt fmt, struct usergart_entry *entry) enum tiler_fmt fmt, struct usergart_entry *entry)
{ {
if (obj->dev->dev_mapping) { struct omap_gem_object *omap_obj = to_omap_bo(obj);
struct omap_gem_object *omap_obj = to_omap_bo(obj); int n = usergart[fmt].height;
int n = usergart[fmt].height; size_t size = PAGE_SIZE * n;
size_t size = PAGE_SIZE * n; loff_t off = mmap_offset(obj) +
loff_t off = mmap_offset(obj) + (entry->obj_pgoff << PAGE_SHIFT);
(entry->obj_pgoff << PAGE_SHIFT); const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
if (m > 1) { if (m > 1) {
int i; int i;
/* if stride > than PAGE_SIZE then sparse mapping: */ /* if stride > than PAGE_SIZE then sparse mapping: */
for (i = n; i > 0; i--) { for (i = n; i > 0; i--) {
unmap_mapping_range(obj->dev->dev_mapping, unmap_mapping_range(obj->dev->anon_inode->i_mapping,
off, PAGE_SIZE, 1); off, PAGE_SIZE, 1);
off += PAGE_SIZE * m; off += PAGE_SIZE * m;
}
} else {
unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
} }
} else {
unmap_mapping_range(obj->dev->anon_inode->i_mapping,
off, size, 1);
} }
entry->obj = NULL; entry->obj = NULL;
......
...@@ -82,8 +82,6 @@ int qxl_bo_create(struct qxl_device *qdev, ...@@ -82,8 +82,6 @@ int qxl_bo_create(struct qxl_device *qdev,
enum ttm_bo_type type; enum ttm_bo_type type;
int r; int r;
if (unlikely(qdev->mman.bdev.dev_mapping == NULL))
qdev->mman.bdev.dev_mapping = qdev->ddev->dev_mapping;
if (kernel) if (kernel)
type = ttm_bo_type_kernel; type = ttm_bo_type_kernel;
else else
......
...@@ -493,7 +493,9 @@ int qxl_ttm_init(struct qxl_device *qdev) ...@@ -493,7 +493,9 @@ int qxl_ttm_init(struct qxl_device *qdev)
/* No others user of address space so set it to 0 */ /* No others user of address space so set it to 0 */
r = ttm_bo_device_init(&qdev->mman.bdev, r = ttm_bo_device_init(&qdev->mman.bdev,
qdev->mman.bo_global_ref.ref.object, qdev->mman.bo_global_ref.ref.object,
&qxl_bo_driver, DRM_FILE_PAGE_OFFSET, 0); &qxl_bo_driver,
qdev->ddev->anon_inode->i_mapping,
DRM_FILE_PAGE_OFFSET, 0);
if (r) { if (r) {
DRM_ERROR("failed initializing buffer object driver(%d).\n", r); DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
return r; return r;
...@@ -518,8 +520,6 @@ int qxl_ttm_init(struct qxl_device *qdev) ...@@ -518,8 +520,6 @@ int qxl_ttm_init(struct qxl_device *qdev)
((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024)); ((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024));
DRM_INFO("qxl: %uM of Surface memory size\n", DRM_INFO("qxl: %uM of Surface memory size\n",
(unsigned)qdev->surfaceram_size / (1024 * 1024)); (unsigned)qdev->surfaceram_size / (1024 * 1024));
if (unlikely(qdev->mman.bdev.dev_mapping == NULL))
qdev->mman.bdev.dev_mapping = qdev->ddev->dev_mapping;
r = qxl_ttm_debugfs_init(qdev); r = qxl_ttm_debugfs_init(qdev);
if (r) { if (r) {
DRM_ERROR("Failed to init debugfs\n"); DRM_ERROR("Failed to init debugfs\n");
......
...@@ -80,7 +80,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ ...@@ -80,7 +80,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \ rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \ trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
ci_dpm.o dce6_afmt.o ci_dpm.o dce6_afmt.o radeon_vm.o
# add async DMA block # add async DMA block
radeon-y += \ radeon-y += \
...@@ -99,6 +99,12 @@ radeon-y += \ ...@@ -99,6 +99,12 @@ radeon-y += \
uvd_v3_1.o \ uvd_v3_1.o \
uvd_v4_2.o uvd_v4_2.o
# add VCE block
radeon-y += \
radeon_vce.o \
vce_v1_0.o \
vce_v2_0.o \
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
radeon-$(CONFIG_ACPI) += radeon_acpi.o radeon-$(CONFIG_ACPI) += radeon_acpi.o
......
...@@ -2601,6 +2601,10 @@ int btc_dpm_init(struct radeon_device *rdev) ...@@ -2601,6 +2601,10 @@ int btc_dpm_init(struct radeon_device *rdev)
pi->min_vddc_in_table = 0; pi->min_vddc_in_table = 0;
pi->max_vddc_in_table = 0; pi->max_vddc_in_table = 0;
ret = r600_get_platform_caps(rdev);
if (ret)
return ret;
ret = rv7xx_parse_power_table(rdev); ret = rv7xx_parse_power_table(rdev);
if (ret) if (ret)
return ret; return ret;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -2036,6 +2036,10 @@ int cypress_dpm_init(struct radeon_device *rdev) ...@@ -2036,6 +2036,10 @@ int cypress_dpm_init(struct radeon_device *rdev)
pi->min_vddc_in_table = 0; pi->min_vddc_in_table = 0;
pi->max_vddc_in_table = 0; pi->max_vddc_in_table = 0;
ret = r600_get_platform_caps(rdev);
if (ret)
return ret;
ret = rv7xx_parse_power_table(rdev); ret = rv7xx_parse_power_table(rdev);
if (ret) if (ret)
return ret; return ret;
......
...@@ -2990,8 +2990,6 @@ static int evergreen_cp_resume(struct radeon_device *rdev) ...@@ -2990,8 +2990,6 @@ static int evergreen_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB_BASE, ring->gpu_addr >> 8); WREG32(CP_RB_BASE, ring->gpu_addr >> 8);
WREG32(CP_DEBUG, (1 << 27) | (1 << 28)); WREG32(CP_DEBUG, (1 << 27) | (1 << 28));
ring->rptr = RREG32(CP_RB_RPTR);
evergreen_cp_start(rdev); evergreen_cp_start(rdev);
ring->ready = true; ring->ready = true;
r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring); r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring);
...@@ -3952,11 +3950,9 @@ bool evergreen_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *rin ...@@ -3952,11 +3950,9 @@ bool evergreen_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *rin
if (!(reset_mask & (RADEON_RESET_GFX | if (!(reset_mask & (RADEON_RESET_GFX |
RADEON_RESET_COMPUTE | RADEON_RESET_COMPUTE |
RADEON_RESET_CP))) { RADEON_RESET_CP))) {
radeon_ring_lockup_update(ring); radeon_ring_lockup_update(rdev, ring);
return false; return false;
} }
/* force CP activities */
radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring); return radeon_ring_test_lockup(rdev, ring);
} }
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册